Merge branch 'develop'
Some checks failed
Deploy Canina / deploy (push) Successful in 2m10s
E2E Playwright Tests / Run Full E2E & Security Suites (push) Failing after 0s

This commit is contained in:
parsa aghaei 2026-09-06 09:30:15 +03:30
commit a8a08488d9
18 changed files with 157515 additions and 7538 deletions

View File

@ -7,7 +7,7 @@ import { productService } from "../lib/services/productService";
import { isSuitableForDog, isSuitableForCat, isSuitableForBoth, isSpeciesCompatible, matchesMedicalSymptom } from "../lib/petCompatibility";
import Link from "next/link";
import { motion, AnimatePresence } from "motion/react";
import { ProductCardSkeleton } from "./Skeleton";
import { ProductCardSkeleton, ProductListSkeleton } from "./Skeleton";
import { toast } from "sonner";
import {
Search,
@ -16,7 +16,6 @@ import {
Cat,
ShieldCheck,
ChevronLeft,
ChevronRight,
Activity,
Sparkles,
Stethoscope,
@ -24,7 +23,11 @@ import {
Heart,
AlertCircle,
Plus,
Loader2
Loader2,
LayoutGrid,
List,
SlidersHorizontal,
ChevronDown
} from "lucide-react";
import { useCartStore } from "../lib/store/cartStore";
import { useSettingsStore } from "../lib/store/settingsStore";
@ -250,6 +253,176 @@ const ArchiveProductCard: React.FC<{ product: Product }> = ({ product }) => {
);
};
const ArchiveProductListItem: React.FC<{ product: Product }> = ({ product }) => {
const { addItem } = useCartStore();
const cartItem = useCartStore((s) => s.items.find(i => i.product.id === product.id));
const [isAdding, setIsAdding] = useState(false);
const activePet = usePetStore(state => state.pets.find(p => p.id === state.activePetId) || null);
const productUrl = `/shop/${product.slug || product.id}`;
const handleAddToCart = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setIsAdding(true);
addItem(product, 1);
toast.success(`${product.name} به سبد خرید اضافه شد`);
setTimeout(() => setIsAdding(false), 400);
};
const forDog = isSuitableForDog(product.suitableFor);
const forCat = isSuitableForCat(product.suitableFor);
const compatibility = useMemo(() => {
if (!activePet) return null;
const sameSpecies = isSpeciesCompatible(product.suitableFor, activePet.type);
const matchedSymptom = matchesMedicalSymptom(activePet.medicalConditions, product.symptoms, product.benefits);
if (!sameSpecies) return { type: 'alert', text: `مخصوص ${product.suitableFor}` };
if (matchedSymptom) return { type: 'success', text: `توصیه شده برای ${matchedSymptom} ${activePet.name}` };
if (sameSpecies) return { type: 'neutral', text: `مناسب برای ${activePet.name}` };
return null;
}, [product, activePet]);
const isOutOfStock = (product.packageSize !== undefined && product.packageSize !== null ? Number(product.packageSize) : 100) <= 0;
const hidePrices = useSettingsStore.getState().getText("catalog_hide_prices", "false").includes("true");
const disableCart = useSettingsStore.getState().getText("catalog_disable_cart", "false").includes("true");
return (
<motion.div
layout
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="w-full"
>
<Link
data-testid="archive-product-list-item"
href={productUrl}
className="group bg-white rounded-2xl border border-medical-gray-200 overflow-hidden hover:border-canina-blue/30 transition-all flex items-center p-2.5 gap-3 relative shadow-xs"
>
{/* Product Thumbnail with species badge */}
<div className="w-24 h-24 sm:w-28 sm:h-28 bg-medical-gray-50 rounded-xl flex-shrink-0 flex items-center justify-center p-1 relative overflow-hidden border border-medical-gray-100">
<SafeImage
src={product.image}
alt={product.name}
sizes="120px"
className="w-full h-full group-hover:scale-105 transition-transform duration-500"
imgClassName="w-full h-full object-contain"
/>
{product.specialBadge && (
<div className="absolute top-1 right-1 z-10 bg-canina-blue text-white text-[9px] font-black uppercase px-1.5 py-0.5 rounded-md shadow-xs">
{product.specialBadge}
</div>
)}
<div className="absolute bottom-1 left-1 flex gap-1">
{forDog && (
<div className="w-5 h-5 bg-white/90 backdrop-blur-xs rounded-md flex items-center justify-center text-medical-gray-500 border border-medical-gray-100 shadow-2xs" title="مناسب برای سگ">
<Dog className="w-3 h-3" />
</div>
)}
{forCat && (
<div className="w-5 h-5 bg-white/90 backdrop-blur-xs rounded-md flex items-center justify-center text-medical-gray-500 border border-medical-gray-100 shadow-2xs" title="مناسب برای گربه">
<Cat className="w-3 h-3" />
</div>
)}
</div>
</div>
{/* Product Info */}
<div className="flex-1 flex flex-col justify-between self-stretch min-w-0 py-0.5">
<div>
<div className="flex items-center gap-1.5 mb-1">
<span className="text-[10px] font-bold text-canina-blue truncate">{product.category}</span>
{compatibility && (
<span className={`text-[9px] font-black px-1.5 py-0.2 rounded-md ${compatibility.type === 'alert' ? 'bg-amber-100 text-amber-700' :
compatibility.type === 'success' ? 'bg-green-100 text-green-700' : 'bg-medical-gray-100 text-medical-gray-600'
}`}>
{compatibility.text}
</span>
)}
</div>
<h3 dir="rtl" className="text-xs sm:text-sm font-black text-medical-gray-900 group-hover:text-canina-blue transition-colors leading-snug text-right font-vazir line-clamp-2">
{product.nameFa || product.name}
</h3>
{product.nameEn && (
<span dir="ltr" className="text-[10px] font-semibold text-slate-400 font-sans text-left block truncate mt-0.5">
{product.nameEn}
</span>
)}
{product.symptoms && product.symptoms.length > 0 && (
<div className="flex flex-wrap gap-1 mt-1.5">
{product.symptoms.slice(0, 2).map((s, i) => (
<span key={i} className="text-[9px] font-bold bg-medical-gray-100 text-medical-gray-500 px-1.5 py-0.5 rounded">
{s}
</span>
))}
</div>
)}
</div>
{/* Price & Add to Cart */}
<div className="mt-2 flex items-center justify-between pt-2 border-t border-medical-gray-100/70 gap-2">
{!hidePrices ? (
<span className="font-black text-medical-gray-900 font-vazir text-xs sm:text-sm">{product.price}</span>
) : (
<span className="text-[10px] font-bold text-amber-600 bg-amber-50 px-1.5 py-0.5 rounded">استعلام قیمت</span>
)}
{isOutOfStock ? (
<span className="px-2 py-1 rounded-lg bg-rose-50 text-rose-600 border border-rose-100 text-[10px] font-bold font-vazir">
ناموجود
</span>
) : !disableCart && (
cartItem && cartItem.quantity > 0 ? (
<div
onClick={(e) => { e.preventDefault(); e.stopPropagation(); }}
className="flex items-center gap-1 bg-canina-blue/10 border border-canina-blue/20 rounded-lg p-0.5"
>
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
useCartStore.getState().updateQuantity(product.id, cartItem.quantity + 1);
}}
className="w-6 h-6 rounded-md bg-canina-blue text-white flex items-center justify-center font-bold text-xs"
>
+
</button>
<span className="w-4 text-center font-black text-canina-blue text-xs font-vazir">
{toPersian(cartItem.quantity)}
</span>
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
useCartStore.getState().updateQuantity(product.id, cartItem.quantity - 1);
}}
className="w-6 h-6 rounded-md bg-white text-medical-gray-700 flex items-center justify-center font-bold text-xs shadow-2xs"
>
-
</button>
</div>
) : (
<button
onClick={handleAddToCart}
disabled={isAdding}
className="px-2.5 py-1.5 rounded-lg bg-canina-blue text-white text-[11px] font-black flex items-center gap-1 hover:bg-canina-dark transition-all disabled:opacity-50 shadow-xs shadow-canina-blue/20"
>
{isAdding ? <Loader2 className="w-3 h-3 animate-spin" /> : <Plus className="w-3 h-3" />}
<span>خرید</span>
</button>
)
)}
</div>
</div>
</Link>
</motion.div>
);
};
import { useRouter } from 'next/navigation';
export default function ArchivePage({
@ -270,10 +443,12 @@ export default function ArchivePage({
const [activeSymptoms, setActiveSymptoms] = useState<string[]>(initialSymptoms ? initialSymptoms.split(',') : []);
const [searchQuery, setSearchQuery] = useState(initialSearch);
const [isUpdating, setIsUpdating] = useState(false);
const [isMobileFilterOpen, setIsMobileFilterOpen] = useState(false);
const [filteredProducts, setFilteredProducts] = useState<Product[]>([]);
const [sortBy, setSortBy] = useState<string>("createdAt");
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc");
const [viewMode, setViewMode] = useState<"list" | "grid">("list");
const [isMobileSolutionsOpen, setIsMobileSolutionsOpen] = useState(false);
const [isMobileSymptomsOpen, setIsMobileSymptomsOpen] = useState(false);
const [symptomSearch, setSymptomSearch] = useState("");
const [categories, setCategories] = useState<{ id: string; label: string; icon: React.ReactNode }[]>([]);
@ -408,8 +583,8 @@ export default function ArchivePage({
<div className="flex items-center justify-between mb-8 pb-4 border-b border-medical-gray-200/60 font-vazir gap-2" dir="rtl">
{/* Breadcrumb - right side in RTL */}
<div className="flex items-center gap-1.5 text-xs font-bold text-medical-gray-400 overflow-hidden min-w-0">
<span className="hidden sm:inline cursor-pointer hover:text-canina-blue transition-colors shrink-0" onClick={() => router.push('/')}>خانه</span>
<ChevronLeft className="hidden sm:block w-3 h-3 text-medical-gray-300 shrink-0" />
<span className="cursor-pointer hover:text-canina-blue transition-colors shrink-0" onClick={() => router.push('/')}>خانه</span>
<ChevronLeft className="w-3 h-3 text-medical-gray-300 shrink-0" />
<span className="cursor-pointer hover:text-canina-blue transition-colors shrink-0" onClick={() => { setSelectedCategory("all"); }}>فروشگاه محصولات</span>
{selectedCategory !== "all" && (
<>
@ -420,15 +595,6 @@ export default function ArchivePage({
</div>
<div className="flex items-center gap-2 shrink-0">
{/* Mobile Filter Button */}
<button
onClick={() => setIsMobileFilterOpen(!isMobileFilterOpen)}
className="lg:hidden flex-shrink-0 flex items-center gap-1.5 bg-canina-blue text-white px-3 py-1.5 rounded-lg text-xs font-bold shadow-xs cursor-pointer"
>
<Filter className="w-3.5 h-3.5" />
<span>فیلترها {activeSymptoms.length > 0 || selectedPet !== "all" || selectedCategory !== "all" ? "•" : ""}</span>
</button>
{/* Back Button - leftmost in RTL */}
<button
onClick={() => router.push('/')}
@ -445,28 +611,14 @@ export default function ArchivePage({
<div className="flex flex-col lg:flex-row gap-8">
{/* Mobile Overlay Backdrop */}
{isMobileFilterOpen && (
<div
className="lg:hidden fixed inset-0 bg-black/50 z-40 backdrop-blur-xs transition-opacity"
onClick={() => setIsMobileFilterOpen(false)}
/>
)}
{/* Sidebar Filters - Collapsible on Mobile */}
<aside className={`lg:w-72 flex-shrink-0 space-y-6 ${isMobileFilterOpen ? 'fixed inset-y-0 right-0 z-50 w-4/5 max-w-xs bg-white p-6 overflow-y-auto shadow-2xl space-y-6' : 'hidden lg:block'}`}>
{/* Sidebar Filters - Desktop only */}
<aside className="lg:w-72 flex-shrink-0 space-y-6 hidden lg:block">
<div className="bg-white rounded-[2rem] lg:p-6 border-0 lg:border lg:border-medical-gray-200 shadow-none lg:shadow-sm">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-2">
<Filter className="w-5 h-5 text-canina-blue" />
<h3 className="font-black text-medical-gray-900">{getText('shop_filter_title', 'فیلترهای تخصصی')}</h3>
</div>
<button
onClick={() => setIsMobileFilterOpen(false)}
className="lg:hidden text-xs text-medical-gray-400 font-bold p-1 hover:text-red-500"
>
بستن
</button>
</div>
<div className="space-y-6">
@ -498,7 +650,7 @@ export default function ArchivePage({
{categories.map(cat => (
<button
key={cat.id}
onClick={() => { setSelectedCategory(cat.id); setIsMobileFilterOpen(false); }}
onClick={() => setSelectedCategory(cat.id)}
className={`flex items-center gap-3 px-4 py-3 rounded-2xl text-xs font-bold transition-all border ${selectedCategory === cat.id ? 'bg-canina-blue border-canina-blue text-white' : 'bg-white border-medical-gray-100 text-medical-gray-600 hover:border-canina-blue/30'}`}
>
{cat.icon}
@ -568,8 +720,9 @@ export default function ArchivePage({
{/* Main Content */}
<main className="flex-1 relative">
<div className="flex flex-col sm:flex-row items-center justify-between mb-6 gap-4">
{getText('shop_catalog_title', 'کاتولوگ دارویی Canina') ? (
{/* Desktop Top Title & Search / Sort Row */}
<div className="hidden lg:flex flex-col sm:flex-row items-center justify-between mb-6 gap-4">
{getText('shop_catalog_title', '').trim() ? (
<h2
className="text-3xl font-black text-medical-gray-900"
dangerouslySetInnerHTML={{
@ -578,7 +731,7 @@ export default function ArchivePage({
/>
) : null}
<div className="flex flex-wrap sm:flex-nowrap items-center gap-3 w-full sm:w-auto">
<div className="flex items-center gap-3 w-auto">
{/* Sort Dropdown */}
<div className="flex items-center gap-2 bg-white border border-medical-gray-200 rounded-2xl px-3 py-2 text-xs font-bold shadow-xs">
<span className="text-medical-gray-400 whitespace-nowrap">مرتبسازی:</span>
@ -599,7 +752,7 @@ export default function ArchivePage({
</select>
</div>
<div className="relative w-full sm:w-64">
<div className="relative w-64">
<Search className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-medical-gray-400" />
<input
data-testid="shop-search-input"
@ -613,9 +766,213 @@ export default function ArchivePage({
</div>
</div>
{/* Mobile Title (Only if set in admin) */}
{getText('shop_catalog_title', '').trim() ? (
<div className="lg:hidden mb-4">
<h2
className="text-2xl font-black text-medical-gray-900"
dangerouslySetInnerHTML={{
__html: getText('shop_catalog_title', 'کاتولوگ دارویی <span class="text-canina-blue">Canina</span>')
}}
/>
</div>
) : null}
{/* Mobile Controls: Horizontal Sliding Filters & View Toggle Row */}
<div className="lg:hidden flex flex-col gap-3 mb-5">
{/* Search Bar on Mobile */}
<div className="relative w-full">
<Search className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-medical-gray-400" />
<input
data-testid="shop-search-input-mobile"
type="text"
placeholder="جستجوی محصول..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full bg-white border border-medical-gray-200 rounded-2xl py-2.5 pr-11 pl-4 text-xs font-bold focus:outline-none focus:ring-2 focus:ring-canina-blue/20 shadow-2xs"
/>
</div>
{/* Horizontal Scrollable Row for Sort, Pet Type, Solutions & Symptoms Filter */}
<div className="flex items-center gap-2 overflow-x-auto pb-1.5 pt-0.5 scrollbar-none no-scrollbar -mx-4 px-4 select-none touch-pan-x">
{/* 1. Compact Sort Button */}
<div className="relative flex-shrink-0">
<div className="flex items-center bg-white border border-medical-gray-200 rounded-xl px-2.5 py-1.5 shadow-2xs text-xs font-bold text-medical-gray-700">
<SlidersHorizontal className="w-3.5 h-3.5 text-canina-blue ml-1.5 shrink-0" />
<select
value={`${sortBy}:${sortOrder}`}
onChange={(e) => {
const [b, o] = e.target.value.split(':');
setSortBy(b);
setSortOrder(o as 'asc' | 'desc');
}}
className="bg-transparent text-[11px] font-black text-medical-gray-800 outline-none cursor-pointer pr-1"
>
<option value="createdAt:desc">جدیدترین</option>
<option value="createdAt:asc">قدیمیترین</option>
<option value="priceValue:asc">ارزانترین</option>
<option value="priceValue:desc">گرانترین</option>
<option value="nameFa:asc">الف - ی</option>
</select>
</div>
</div>
{/* 2. Three Segmented Pet Type Buttons (Dog right, Both middle, Cat left) */}
<div className="flex-shrink-0 inline-flex items-center bg-white border border-medical-gray-200 rounded-xl p-0.5 shadow-2xs">
{/* Right: Dog */}
<button
type="button"
onClick={() => setSelectedPet("سگ")}
className={`flex items-center gap-1 px-2.5 py-1 rounded-lg text-[11px] font-black transition-all ${selectedPet === "سگ" ? "bg-canina-blue text-white shadow-xs" : "text-medical-gray-500 hover:text-medical-gray-800"}`}
>
<Dog className="w-3.5 h-3.5" />
<span>سگ</span>
</button>
{/* Middle: Both */}
<button
type="button"
onClick={() => setSelectedPet("all")}
className={`flex items-center gap-1 px-2.5 py-1 rounded-lg text-[11px] font-black transition-all ${selectedPet === "all" ? "bg-canina-blue text-white shadow-xs" : "text-medical-gray-500 hover:text-medical-gray-800"}`}
>
<Activity className="w-3.5 h-3.5" />
<span>هردو</span>
</button>
{/* Left: Cat */}
<button
type="button"
onClick={() => setSelectedPet("گربه")}
className={`flex items-center gap-1 px-2.5 py-1 rounded-lg text-[11px] font-black transition-all ${selectedPet === "گربه" ? "bg-canina-blue text-white shadow-xs" : "text-medical-gray-500 hover:text-medical-gray-800"}`}
>
<Cat className="w-3.5 h-3.5" />
<span>گربه</span>
</button>
</div>
{/* 3. Solutions Dropdown Button */}
<div className="relative flex-shrink-0">
<button
type="button"
onClick={() => {
setIsMobileSolutionsOpen(!isMobileSolutionsOpen);
setIsMobileSymptomsOpen(false);
}}
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-xl border text-[11px] font-black shadow-2xs transition-all whitespace-nowrap ${selectedCategory !== "all" ? "bg-canina-blue text-white border-canina-blue" : "bg-white border-medical-gray-200 text-medical-gray-700"}`}
>
<span>{selectedCategory !== "all" ? (CATEGORY_LABELS[selectedCategory] || "راهکار درمانی") : "راهکار درمانی"}</span>
<ChevronDown className={`w-3 h-3 transition-transform ${isMobileSolutionsOpen ? "rotate-180" : ""}`} />
</button>
{isMobileSolutionsOpen && (
<>
<div className="fixed inset-0 z-40" onClick={() => setIsMobileSolutionsOpen(false)} />
<div className="absolute right-0 top-full mt-2 w-64 max-h-72 overflow-y-auto bg-white rounded-2xl p-2 border border-medical-gray-200 shadow-xl z-50 space-y-1 font-vazir">
{categories.map(cat => (
<button
key={cat.id}
type="button"
onClick={() => {
setSelectedCategory(cat.id);
setIsMobileSolutionsOpen(false);
}}
className={`w-full flex items-center gap-2.5 px-3 py-2 rounded-xl text-xs font-bold text-right transition-colors ${selectedCategory === cat.id ? "bg-canina-blue text-white" : "hover:bg-medical-gray-50 text-medical-gray-700"}`}
>
<span className="shrink-0">{cat.icon}</span>
<span className="truncate">{cat.label}</span>
</button>
))}
</div>
</>
)}
</div>
{/* 4. Symptoms Dropdown Button with search inside */}
<div className="relative flex-shrink-0">
<button
type="button"
onClick={() => {
setIsMobileSymptomsOpen(!isMobileSymptomsOpen);
setIsMobileSolutionsOpen(false);
}}
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-xl border text-[11px] font-black shadow-2xs transition-all whitespace-nowrap ${activeSymptoms.length > 0 ? "bg-canina-blue text-white border-canina-blue" : "bg-white border-medical-gray-200 text-medical-gray-700"}`}
>
<span>علائم پت {activeSymptoms.length > 0 ? `(${toPersian(activeSymptoms.length)})` : ""}</span>
<ChevronDown className={`w-3 h-3 transition-transform ${isMobileSymptomsOpen ? "rotate-180" : ""}`} />
</button>
{isMobileSymptomsOpen && (
<>
<div className="fixed inset-0 z-40" onClick={() => setIsMobileSymptomsOpen(false)} />
<div className="absolute right-0 top-full mt-2 w-72 max-h-80 overflow-y-auto bg-white rounded-2xl p-3 border border-medical-gray-200 shadow-xl z-50 font-vazir">
<div className="flex items-center justify-between mb-2 pb-1 border-b border-medical-gray-100">
<span className="text-[10px] font-black text-medical-gray-400">جستجو بر اساس علائم</span>
{activeSymptoms.length > 0 && (
<button
type="button"
onClick={() => setActiveSymptoms([])}
className="text-[10px] font-bold text-red-500 hover:underline"
>
پاک کردن همه
</button>
)}
</div>
<input
type="text"
placeholder="جستجوی علائم..."
value={symptomSearch}
onChange={(e) => setSymptomSearch(e.target.value)}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-1.5 px-2.5 text-xs outline-none focus:border-canina-blue mb-2.5 font-vazir"
/>
<div className="flex flex-wrap gap-1.5 max-h-48 overflow-y-auto scrollbar-thin pr-0.5">
{symptoms.length === 0 ? (
<span className="text-[10px] text-medical-gray-400">در حال بارگذاری...</span>
) : symptoms
.filter(s => s.toLowerCase().includes(symptomSearch.toLowerCase()))
.map(s => (
<button
key={s}
type="button"
onClick={() => toggleSymptom(s)}
className={`px-2.5 py-1 rounded-lg text-[10px] font-bold transition-all border whitespace-nowrap ${activeSymptoms.includes(s)
? "bg-canina-blue border-canina-blue text-white shadow-2xs"
: "bg-medical-gray-50 border-medical-gray-200 text-medical-gray-600 hover:border-canina-blue/40 hover:text-canina-blue"
}`}
>
{s}
</button>
))}
</div>
</div>
</>
)}
</div>
{/* 5. View Mode Switch: 2 Buttons (List / Card) */}
<div className="flex-shrink-0 inline-flex items-center bg-white border border-medical-gray-200 rounded-xl p-0.5 shadow-2xs mr-auto">
<button
type="button"
onClick={() => setViewMode("list")}
className={`p-1.5 rounded-lg transition-all ${viewMode === "list" ? "bg-canina-blue text-white shadow-xs" : "text-medical-gray-400 hover:text-medical-gray-700"}`}
title="نمایش لیستی"
>
<List className="w-3.5 h-3.5" />
</button>
<button
type="button"
onClick={() => setViewMode("grid")}
className={`p-1.5 rounded-lg transition-all ${viewMode === "grid" ? "bg-canina-blue text-white shadow-xs" : "text-medical-gray-400 hover:text-medical-gray-700"}`}
title="نمایش کارتی"
>
<LayoutGrid className="w-3.5 h-3.5" />
</button>
</div>
</div>
</div>
{/* Active Filters Bar with X Dismiss Cross Buttons */}
{(selectedCategory !== "all" || selectedPet !== "all" || activeSymptoms.length > 0 || searchQuery) && (
<div className="flex flex-wrap items-center gap-2 mb-6 bg-white p-4 rounded-2xl border border-medical-gray-200 shadow-sm font-vazir" dir="rtl">
<div className="flex flex-wrap items-center gap-2 mb-6 bg-white p-3 sm:p-4 rounded-2xl border border-medical-gray-200 shadow-sm font-vazir" dir="rtl">
<span className="text-xs font-black text-medical-gray-400 ml-2">فیلترهای فعال:</span>
{selectedCategory !== "all" && (
<span className="inline-flex items-center gap-1.5 px-3 py-1 bg-canina-blue/10 text-canina-blue rounded-xl text-xs font-black border border-canina-blue/20">
@ -652,17 +1009,47 @@ export default function ArchivePage({
<AnimatePresence mode="wait">
{isUpdating ? (
<div key="skeleton" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
{Array.from({ length: 8 }).map((_, i) => (
<ProductCardSkeleton key={i} />
))}
</div>
viewMode === "list" ? (
<div key="skeleton-list" className="flex flex-col gap-3 lg:hidden">
{Array.from({ length: 8 }).map((_, i) => (
<ProductListSkeleton key={i} />
))}
</div>
) : (
<div key="skeleton-grid" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
{Array.from({ length: 8 }).map((_, i) => (
<ProductCardSkeleton key={i} />
))}
</div>
)
) : filteredProducts.length > 0 ? (
<>
<div key="grid" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
{/* Mobile List View vs Card View (Desktop is always responsive multi-column grid) */}
<div
key={viewMode}
className={
viewMode === "list"
? "flex flex-col gap-3 lg:grid lg:grid-cols-2 xl:grid-cols-4 lg:gap-6"
: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6"
}
>
<AnimatePresence mode="popLayout">
{filteredProducts.map((p: Product) => (
<ArchiveProductCard key={p.id} product={p} />
<React.Fragment key={p.id}>
{/* In list mode on mobile, render ArchiveProductListItem; on desktop (lg+), render standard card */}
{viewMode === "list" ? (
<>
<div className="block lg:hidden w-full">
<ArchiveProductListItem product={p} />
</div>
<div className="hidden lg:block h-full">
<ArchiveProductCard product={p} />
</div>
</>
) : (
<ArchiveProductCard product={p} />
)}
</React.Fragment>
))}
</AnimatePresence>
</div>

View File

@ -47,8 +47,8 @@ export default function BrandLogo({
<Image
src={logoUrl}
alt={`${textEn} ${textFa}`}
width={180}
height={48}
width={140}
height={50}
priority
unoptimized={isBlobOrData}
className="h-10 sm:h-12 w-auto max-w-[160px] sm:max-w-[200px] object-contain shrink-0"

View File

@ -209,7 +209,7 @@ export default function Header({
{/* Sliding Announcement Ticker Banner */}
<TickerBanner />
<header className="bg-white border-b border-medical-gray-200">
<header className="bg-white border-b border-medical-gray-200 overflow-hidden">
{/* TOP ROW: Logo + Search + Primary Actions */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-3 flex items-center justify-between gap-4">

View File

@ -138,19 +138,7 @@ export default function Hero({ banners = [], onShopNavigate }: { banners?: Banne
}}
/>
<div className="whitespace-nowrap flex flex-nowrap justify-center lg:justify-start gap-2 sm:gap-4 sm:gap-6 min-h-[58px]">
<button
onClick={() => {
const element = document.getElementById('canino-advisor');
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
}}
className="bg-canina-blue text-white px-3 sm:px-10 py-2 sm:py-5 rounded-full font-bold text-lg hover:bg-canina-blue/90 hover:shadow-2xl focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all flex items-center gap-2 group font-vazir min-h-[58px] shadow-lg shadow-canina-blue/20 cursor-pointer"
>
<span suppressHydrationWarning>{mounted && isInitialized ? getText('hero_btn_advisor', "دستیار سلامت پت") : "دستیار سلامت پت"}</span>
<ChevronLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
</button>
<div className="whitespace-nowrap flex flex-nowrap justify-center lg:justify-start gap-2 sm:gap-6">
<button
onClick={() => {
if (onShopNavigate) {
@ -159,9 +147,23 @@ export default function Hero({ banners = [], onShopNavigate }: { banners?: Banne
router.push('/shop');
}
}}
className="bg-white border-2 border-medical-gray-900 text-medical-gray-900 px-3 sm:px-10 py-2 sm:py-5 rounded-full font-bold text-lg hover:bg-medical-gray-50 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all font-vazir min-h-[58px] shadow-sm cursor-pointer"
className="bg-canina-blue text-white px-3 sm:px-10 py-2 sm:py-5 rounded-full font-bold text-lg hover:bg-canina-blue/90 hover:shadow-2xl focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all gap-2 group font-vazir shadow-lg shadow-canina-blue/20 cursor-pointer"
>
<span suppressHydrationWarning>{mounted && isInitialized ? getText('hero_btn_products', "مشاهده محصولات") : "مشاهده محصولات"}</span>
</button>
<button
onClick={() => {
const element = document.getElementById('canino-advisor');
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
}}
className="flex items-center bg-white border-2 border-medical-gray-900 text-medical-gray-900 px-3 sm:px-10 py-2 sm:py-5 rounded-full font-bold text-lg hover:bg-medical-gray-50 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all font-vazir shadow-sm cursor-pointer"
>
<span suppressHydrationWarning>{mounted && isInitialized ? getText('hero_btn_advisor', "دستیار سلامت پت") : "دستیار سلامت پت"}</span>
<ChevronLeft className="w-5 h-5 group-hover:-translate-x-1 transition-transform" />
</button>
</div>
</div>
@ -270,11 +272,10 @@ export default function Hero({ banners = [], onShopNavigate }: { banners?: Banne
aria-label={`رفتن به اسلاید ${idx + 1}`}
>
<span
className={`block rounded-full transition-opacity duration-300 ${
currentSlide === idx
className={`block rounded-full transition-opacity duration-300 ${currentSlide === idx
? "w-4 h-2 bg-canina-gold opacity-100"
: "w-2 h-2 bg-white opacity-60 hover:opacity-100"
}`}
}`}
/>
</button>
))}

View File

@ -40,6 +40,25 @@ export const ProductCardSkeleton = () => {
);
};
export const ProductListSkeleton = () => {
return (
<div className="bg-white rounded-2xl p-3 border border-medical-gray-100 shadow-xs flex items-center gap-3">
<Skeleton className="w-24 h-24 rounded-xl flex-shrink-0" />
<div className="flex-1 flex flex-col justify-between self-stretch py-0.5 min-w-0">
<div className="space-y-1.5">
<Skeleton className="w-16 h-3 rounded-full" />
<Skeleton className="w-4/5 h-4 rounded-md" />
<Skeleton className="w-1/2 h-3 rounded-md" />
</div>
<div className="flex items-center justify-between pt-2 border-t border-medical-gray-50">
<Skeleton className="w-20 h-4 rounded-md" />
<Skeleton className="w-16 h-7 rounded-lg" />
</div>
</div>
</div>
);
};
export const PetProfileSkeleton = () => {
return (
<div className="bg-white rounded-[3rem] p-10 shadow-2xl border border-medical-gray-50">

View File

@ -3,13 +3,13 @@
"1": "app.module.ts",
"2": "WikiController",
"3": "productService.ts",
"4": "ClientLayout.tsx",
"4": "useSettingsStore",
"5": "CmsController",
"6": "tickets.controller.ts",
"7": "SmsSettingsPage.tsx",
"8": "SmsService",
"9": "devDependencies",
"10": "CreateReviewDto",
"10": "reviews.controller.ts",
"11": "UsersService",
"12": "index.ts",
"13": "app-audit-verification.e2e-spec.js",
@ -20,7 +20,7 @@
"18": "JwtAuthGuard",
"19": "admin.controller.ts",
"20": "CreateVideoDto",
"21": "OrderService",
"21": "HomeClient.tsx",
"22": "ProductDto",
"23": "MenuService",
"24": "BE-001",
@ -32,32 +32,32 @@
"30": "DEVOPS-001",
"31": "DOC-001",
"32": "adminRoutes.tsx",
"33": "WholesaleService",
"34": "B2BService",
"35": "ContactService",
"33": "WholesaleApplyDto",
"34": "B2BController",
"35": "ContactController",
"36": "FaqService",
"37": "راهنمای تست سیستم (Software Testing)",
"38": "Button.tsx",
"38": "Button",
"39": "CategoriesController",
"40": "MediaController",
"41": "What You Must Do When Invoked",
"42": "SslController",
"43": "BannersService",
"44": "TestimonialsService",
"43": "BannersController",
"44": "TestimonialsController",
"45": "What You Must Do When Invoked",
"46": "20260526145407_init/migration.sql",
"47": "IngredientsService",
"47": "IngredientsController",
"48": "app.e2e-spec.js",
"49": "devDependencies",
"50": "devDependencies",
"51": "BlogsController",
"52": "prescriptions.controller.ts",
"53": "SmartAdvisorService",
"54": "MenuManager.tsx",
"54": "Button.tsx",
"55": "UITexts.tsx",
"56": "PrescriptionsManager.tsx",
"56": "Orders.tsx",
"57": "Role & Core Objective",
"58": "RevalidationService",
"58": "RedisService",
"59": "compilerOptions",
"60": "CreateUserDto",
"61": "ProductPage.tsx",
@ -65,9 +65,9 @@
"63": "dependencies",
"64": "compilerOptions",
"65": "admin.service.ts",
"66": "AdminQueryDto",
"67": "PetsController",
"68": "useSettingsStore",
"66": "AdminController",
"67": "ConfirmModal.tsx",
"68": "lib/services/api.ts",
"69": "Required Review Group Closures",
"70": "compilerOptions",
"71": "getPageMetadata",
@ -75,7 +75,7 @@
"73": "Operational Rules & Boundaries",
"74": "WikiController",
"75": "PetsController",
"76": "ProductsService",
"76": "RevalidationService",
"77": "seo.module.ts",
"78": "rss.xml/route.ts",
"79": "🏢 AI Software Agency — Master Orchestration Protocol v3",
@ -84,7 +84,7 @@
"82": "scripts",
"83": "dependencies",
"84": "Role & Core Objective",
"85": "auth.controller.ts",
"85": "AuthController",
"86": "zibal.service.ts",
"87": "dependencies",
"88": "CreateEBankCheckoutDto",
@ -92,9 +92,9 @@
"90": "PetProfile.tsx",
"91": "Reconciled Audit Roles & Assignments",
"92": "OrdersService",
"93": "Orders.tsx",
"93": "auth.service.ts",
"94": "Phase 3.1 — Human Review Preparation and Master Backlog Critique",
"95": "getSeoConfig",
"95": "wiki/[slug]/page.tsx",
"96": "compilerOptions",
"97": "PaymentService",
"98": "scripts",
@ -112,7 +112,7 @@
"110": "Operational Rules & Boundaries",
"111": "Operational Rules & Boundaries",
"112": "Operational Rules & Boundaries",
"113": "AdminTransactionFilterDto",
"113": "sms.service.ts",
"114": "AppService",
"115": "Spinner.tsx",
"116": "Vazirmatn Changelog",
@ -122,13 +122,13 @@
"120": "compilerOptions",
"121": "backend/README.md",
"122": "AdminService",
"123": "Body",
"123": "ApiOperation",
"124": "Repository Map",
"125": "validate_integrity.js",
"126": "admin-panel/package.json",
"127": "Sahel-Font",
"128": "WholesaleApplyDto",
"129": "MetricsController",
"128": "HomeController",
"129": "auth.module.ts",
"130": "Sahel-Font",
"131": "Role & Core Objective",
"132": "orchestrate.py",
@ -144,14 +144,14 @@
"142": "start-dev.js",
"143": "generate-openapi.js",
"144": "SafeImage.tsx",
"145": "wiki/[slug]/page.tsx",
"145": "auth.controller.ts",
"146": "System Discovery",
"147": "catalog/page.tsx",
"148": "RouteErrorBoundary",
"149": "search/page.tsx",
"147": "torob.controller.ts",
"148": "Reports.tsx",
"149": "RegisterDto",
"150": "Product Requirement Document (PRD)",
"151": "@eslint/js",
"152": "auth.service.ts",
"152": "AuthService",
"153": "exclude",
"154": "Baseline Command Plan & Reconciled Command History",
"155": "seo-backfill.ts",
@ -176,15 +176,15 @@
"174": "rebuild_honest_ledger.js",
"175": "validate_evidence_grade.js",
"176": "uploads/[...path]/route.ts",
"177": "bcrypt",
"177": "WikiService",
"178": "نقشه جامع پوشش تست‌های سرتاسری (E2E Test Coverage Map) — پروژه Canina",
"179": "class-transformer",
"179": "track/page.tsx",
"180": "API Contract Specification",
"181": "⚙️ Backend Technical Review (05_dev_backend)",
"182": "🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)",
"183": "🚀 SEO & Content Strategy Review (12_seo_content)",
"184": "helmet",
"185": "Reviews.tsx",
"184": "trust-seals/page.tsx",
"185": "@types/node",
"186": "seed-ui-texts.ts",
"187": "seed-wiki.ts",
"188": "update-blog.dto.ts",
@ -196,14 +196,14 @@
"194": "Raw Finding Verification & Disposition Report",
"195": "React + TypeScript + Vite",
"196": "Select.tsx",
"197": "toPersian",
"198": "js-yaml",
"197": "userStore.ts",
"198": "typescript",
"199": "prisma",
"200": "application/README.md",
"201": "deploy.sh",
"202": "🔒 Security & Performance Review (09_devops_security)",
"203": "👁️ UX & Persona Interface Review (08_visual_qa)",
"204": "@nestjs/core",
"204": "eslint-config-next",
"205": "prisma/scientificTerms.ts",
"206": "seed-blogs.ts",
"207": "seed-custom.ts",
@ -219,20 +219,15 @@
"217": "sync_honest_manifest.js",
"218": "sync_manifest.js",
"219": "FormField.tsx",
"220": "@nestjs/jwt",
"221": "Textarea.tsx",
"222": "admin-panel/tsconfig.json",
"223": "tailwindcss",
"224": "@nestjs/throttler",
"225": "next.config.ts",
"226": "Shabnam Font README",
"227": "AGENTS.md",
"228": "rules/graphify.md",
"229": ".agents/workflows/graphify.md",
"230": "instructions.md",
"231": "passport",
"232": "reflect-metadata",
"233": "swagger-ui-express",
"234": "source-map-support",
"235": "ts-loader",
"236": "ts-node",
@ -264,7 +259,6 @@
"262": "User Logout API",
"263": "generate-openapi.d.ts",
"264": "@types/compression",
"265": "@eslint/eslintrc",
"266": "@types/express",
"267": "@types/jest",
"268": "@types/multer",
@ -294,34 +288,24 @@
"292": "Production Docker Compose",
"293": "Staging Docker Compose",
"294": "ZibalService",
"295": "eslint-plugin-prettier",
"296": "globals",
"297": "ZibalEBankService",
"298": ".initiateOrderPayment",
"299": "@tailwindcss/postcss",
"300": "typescript",
"301": "@nestjs/cli",
"302": "typescript-eslint",
"303": "@nestjs/schematics",
"304": "@nestjs/testing",
"305": "eslint-config-prettier",
"306": "prettier",
"307": "ts-jest",
"308": "jest",
"309": "axios",
"310": "tailwindcss",
"311": "@types/js-yaml",
"312": "@types/passport-jwt",
"313": "20260526160916_add_ui_texts_and_scientific_terms/migration.sql",
"314": "@types/supertest",
"315": "typescript",
"316": "typescript-eslint",
"317": "revalidate/route.ts",
"318": "MaskableField.tsx",
"319": "@tailwindcss/postcss",
"321": "orders/page.tsx",
"322": "pets/page.tsx",
"325": "@types/react-dom",
"327": "eslint-plugin-react-refresh",
"330": "tailwindcss"
"327": "eslint-plugin-react-refresh"
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,327 @@
{
"0": "Roles",
"1": "app.module.ts",
"2": "WikiController",
"3": "productService.ts",
"4": "ClientLayout.tsx",
"5": "CmsController",
"6": "tickets.controller.ts",
"7": "SmsSettingsPage.tsx",
"8": "SmsService",
"9": "devDependencies",
"10": "CreateReviewDto",
"11": "UsersService",
"12": "index.ts",
"13": "app-audit-verification.e2e-spec.js",
"14": "UserDashboard.tsx",
"15": "src/services/api.ts",
"16": "DoctorQueryDto",
"17": "schema.ts",
"18": "JwtAuthGuard",
"19": "admin.controller.ts",
"20": "CreateVideoDto",
"21": "OrderService",
"22": "ProductDto",
"23": "MenuService",
"24": "BE-001",
"25": "FE-001",
"26": "ADM-001",
"27": "DB-001",
"28": "TS-001",
"29": "TEST-001",
"30": "DEVOPS-001",
"31": "DOC-001",
"32": "adminRoutes.tsx",
"33": "WholesaleService",
"34": "B2BService",
"35": "ContactService",
"36": "FaqService",
"37": "راهنمای تست سیستم (Software Testing)",
"38": "Button.tsx",
"39": "CategoriesController",
"40": "MediaController",
"41": "What You Must Do When Invoked",
"42": "SslController",
"43": "BannersService",
"44": "TestimonialsService",
"45": "What You Must Do When Invoked",
"46": "20260526145407_init/migration.sql",
"47": "IngredientsService",
"48": "app.e2e-spec.js",
"49": "devDependencies",
"50": "devDependencies",
"51": "BlogsController",
"52": "prescriptions.controller.ts",
"53": "SmartAdvisorService",
"54": "MenuManager.tsx",
"55": "UITexts.tsx",
"56": "PrescriptionsManager.tsx",
"57": "Role & Core Objective",
"58": "RevalidationService",
"59": "compilerOptions",
"60": "CreateUserDto",
"61": "ProductPage.tsx",
"62": "admin.module.ts",
"63": "dependencies",
"64": "compilerOptions",
"65": "admin.service.ts",
"66": "AdminQueryDto",
"67": "PetsController",
"68": "useSettingsStore",
"69": "Required Review Group Closures",
"70": "compilerOptions",
"71": "getPageMetadata",
"72": "Operational Rules & Boundaries",
"73": "Operational Rules & Boundaries",
"74": "WikiController",
"75": "PetsController",
"76": "ProductsService",
"77": "seo.module.ts",
"78": "rss.xml/route.ts",
"79": "🏢 AI Software Agency — Master Orchestration Protocol v3",
"80": "Operational Rules & Boundaries",
"81": "Operational Rules & Boundaries",
"82": "scripts",
"83": "dependencies",
"84": "Role & Core Objective",
"85": "auth.controller.ts",
"86": "zibal.service.ts",
"87": "dependencies",
"88": "CreateEBankCheckoutDto",
"89": "seed-products.ts",
"90": "PetProfile.tsx",
"91": "Reconciled Audit Roles & Assignments",
"92": "OrdersService",
"93": "Orders.tsx",
"94": "Phase 3.1 — Human Review Preparation and Master Backlog Critique",
"95": "getSeoConfig",
"96": "compilerOptions",
"97": "PaymentService",
"98": "scripts",
"99": "BlogsController",
"100": "Deep Audit Summary Report",
"101": "Operational Rules & Boundaries",
"102": "jest",
"103": "Comprehensive Change Log",
"104": "Products.tsx",
"105": "Operational Rules & Boundaries",
"106": "InitiatePaymentDto",
"107": "PaginationDto",
"108": "PrismaService",
"109": "1. Summary of Integrity Repairs Performed",
"110": "Operational Rules & Boundaries",
"111": "Operational Rules & Boundaries",
"112": "Operational Rules & Boundaries",
"113": "AdminTransactionFilterDto",
"114": "AppService",
"115": "Spinner.tsx",
"116": "Vazirmatn Changelog",
"117": "Vazirmatn Font فونت وزیرمتن",
"118": "Operational Rules & Boundaries",
"119": "compilerOptions",
"120": "compilerOptions",
"121": "backend/README.md",
"122": "AdminService",
"123": "Body",
"124": "Repository Map",
"125": "validate_integrity.js",
"126": "admin-panel/package.json",
"127": "Sahel-Font",
"128": "WholesaleApplyDto",
"129": "MetricsController",
"130": "Sahel-Font",
"131": "Role & Core Objective",
"132": "orchestrate.py",
"133": "backend/package.json",
"134": "blog/page.tsx",
"135": "graphify reference: extra exports and benchmark",
"136": "Phase 2 Final Quality Gate Summary Report",
"137": "Task Modifications Log",
"138": "Install",
"139": "layout.tsx",
"140": "ErrorBoundary",
"141": "application/package.json",
"142": "start-dev.js",
"143": "generate-openapi.js",
"144": "SafeImage.tsx",
"145": "wiki/[slug]/page.tsx",
"146": "System Discovery",
"147": "catalog/page.tsx",
"148": "RouteErrorBoundary",
"149": "search/page.tsx",
"150": "Product Requirement Document (PRD)",
"151": "@eslint/js",
"152": "auth.service.ts",
"153": "exclude",
"154": "Baseline Command Plan & Reconciled Command History",
"155": "seo-backfill.ts",
"156": "manual-test-scenarios.md",
"157": "ErrorPages.tsx",
"158": "media/[...path]/route.ts",
"159": "with-vpn.sh",
"160": "Architecture Specification",
"161": "Project Health Audit Report",
"162": "nest-cli.json",
"163": "graphify reference: query, path, explain",
"164": "Open Questions",
"165": "Final Phase 2 Audit Closure Report",
"166": "open-browsers.js",
"167": "📝 Active Agent Working Scratchpad",
"168": "🔍 Code Health Audit Review (01_auditor)",
"169": "paginated-response.schema.ts",
"170": "Vazirmatn Font README",
"171": "Omitted File Inspection Report",
"172": "Phase 3.2 / 3.3 — Implementation Readiness & Architectural Finalization Report",
"173": "Phase 3 Audit Traceability Matrix",
"174": "rebuild_honest_ledger.js",
"175": "validate_evidence_grade.js",
"176": "uploads/[...path]/route.ts",
"177": "bcrypt",
"178": "نقشه جامع پوشش تست‌های سرتاسری (E2E Test Coverage Map) — پروژه Canina",
"179": "class-transformer",
"180": "API Contract Specification",
"181": "⚙️ Backend Technical Review (05_dev_backend)",
"182": "🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)",
"183": "🚀 SEO & Content Strategy Review (12_seo_content)",
"184": "helmet",
"185": "Reviews.tsx",
"186": "seed-ui-texts.ts",
"187": "seed-wiki.ts",
"188": "update-blog.dto.ts",
"189": "update-home.dto.ts",
"190": "update-wiki.dto.ts",
"191": "graphify reference: add a URL and watch a folder",
"192": "graphify reference: commit hook and native CLAUDE.md integration",
"193": "graphify reference: incremental update and cluster-only",
"194": "Raw Finding Verification & Disposition Report",
"195": "React + TypeScript + Vite",
"196": "Select.tsx",
"197": "toPersian",
"198": "js-yaml",
"199": "prisma",
"200": "application/README.md",
"201": "deploy.sh",
"202": "🔒 Security & Performance Review (09_devops_security)",
"203": "👁️ UX & Persona Interface Review (08_visual_qa)",
"204": "@nestjs/core",
"205": "prisma/scientificTerms.ts",
"206": "seed-blogs.ts",
"207": "seed-custom.ts",
"208": "graphify reference: GitHub clone and cross-repo merge",
"209": "graphify reference: transcribe video and audio",
"210": "Compiler Diagnostic Dispositions",
"211": "Master Task Backlog (Phase 3.3)",
"212": "build_manifest.js",
"213": "generate_classification.js",
"214": "generate_evidence.js",
"215": "generate_ledger.js",
"216": "generate_manifest.js",
"217": "sync_honest_manifest.js",
"218": "sync_manifest.js",
"219": "FormField.tsx",
"220": "@nestjs/jwt",
"221": "Textarea.tsx",
"222": "admin-panel/tsconfig.json",
"223": "tailwindcss",
"224": "@nestjs/throttler",
"225": "next.config.ts",
"226": "Shabnam Font README",
"227": "AGENTS.md",
"228": "rules/graphify.md",
"229": ".agents/workflows/graphify.md",
"230": "instructions.md",
"231": "passport",
"232": "reflect-metadata",
"233": "swagger-ui-express",
"234": "source-map-support",
"235": "ts-loader",
"236": "ts-node",
"237": "tsconfig-paths",
"238": "@vitejs/plugin-react",
"239": "@types/bcrypt",
"240": "supertest",
"241": "blog.entity.ts",
"242": "home.entity.ts",
"243": "wiki.entity.ts",
"244": "User Profile Photo",
"245": "CLAUDE.md",
"246": ".claude/CLAUDE.md",
"247": "extraction-spec.md",
"248": "Products Table",
"249": "Users Table",
"250": "Architectural Audit Findings",
"251": "Cross Boundary Dependencies & Backend Architecture Specification",
"252": "Next.js Agent Rules & Brand Guidelines",
"253": "robots.ts",
"254": "application/eslint.config.mjs",
"255": "postcss.config.mjs",
"256": "vitest.setup.ts",
"257": "backup_db.sh",
"258": "start.sh",
"259": "reviews/README.md",
"260": "backend/eslint.config.mjs",
"261": "User Login API",
"262": "User Logout API",
"263": "generate-openapi.d.ts",
"264": "@types/compression",
"265": "@eslint/eslintrc",
"266": "@types/express",
"267": "@types/jest",
"268": "@types/multer",
"269": "eslint-plugin-react-hooks",
"270": "app-audit-verification.e2e-spec.d.ts",
"271": "app.e2e-spec.d.ts",
"272": "Canina Pharma GmbH",
"273": "Pets Table",
"274": "Canina Iran Project Introduction",
"275": "Developer Standards and Architecture",
"276": "Frontend & Admin Architecture Route Map Specification",
"277": "Project Backlog and Tasks",
"278": "eslint.config.js",
"279": "postcss.config.js",
"280": "admin-panel/public/fonts/sahel-font-v3.4.0/CHANGELOG.md",
"281": "shabnam-font-v5.0.1/CHANGELOG.md",
"282": "tailwind.config.js",
"283": "vite.config.ts",
"284": "application/CLAUDE.md",
"285": "application/public/fonts/sahel-font-v3.4.0/CHANGELOG.md",
"286": "Sahel Font Sample",
"287": "Shabnam Font Changelog",
"288": "Vazirmatn Changelog",
"289": "vitest.config.ts",
"290": "Sahel Font Variable Sample",
"291": "Shabnam Font Sample",
"292": "Production Docker Compose",
"293": "Staging Docker Compose",
"294": "ZibalService",
"295": "eslint-plugin-prettier",
"296": "globals",
"297": "ZibalEBankService",
"298": ".initiateOrderPayment",
"299": "@tailwindcss/postcss",
"300": "typescript",
"301": "@nestjs/cli",
"302": "typescript-eslint",
"303": "@nestjs/schematics",
"304": "@nestjs/testing",
"305": "eslint-config-prettier",
"306": "prettier",
"307": "ts-jest",
"308": "jest",
"309": "axios",
"310": "tailwindcss",
"311": "@types/js-yaml",
"312": "@types/passport-jwt",
"313": "20260526160916_add_ui_texts_and_scientific_terms/migration.sql",
"314": "@types/supertest",
"315": "typescript",
"316": "typescript-eslint",
"317": "revalidate/route.ts",
"318": "MaskableField.tsx",
"319": "@tailwindcss/postcss",
"321": "orders/page.tsx",
"322": "pets/page.tsx",
"325": "@types/react-dom",
"327": "eslint-plugin-react-refresh",
"330": "tailwindcss"
}

View File

@ -0,0 +1 @@
{"output_tokens": 7105}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,16 @@
# Graph Report - canina (2026-09-05)
# Graph Report - canina (2026-09-06)
## Corpus Check
- 603 files · ~1,116,468 words
- 603 files · ~1,117,926 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 4251 nodes · 7789 edges · 325 communities (209 shown, 116 thin omitted)
- 4253 nodes · 7800 edges · 309 communities (212 shown, 97 thin omitted)
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 298 edges (avg confidence: 0.79)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `c57f775c`
- Built from commit: `026c5b90`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
@ -19,13 +19,13 @@
- app.module.ts
- WikiController
- productService.ts
- ClientLayout.tsx
- useSettingsStore
- CmsController
- tickets.controller.ts
- SmsSettingsPage.tsx
- SmsService
- devDependencies
- CreateReviewDto
- reviews.controller.ts
- UsersService
- index.ts
- app-audit-verification.e2e-spec.js
@ -36,7 +36,7 @@
- JwtAuthGuard
- admin.controller.ts
- CreateVideoDto
- OrderService
- HomeClient.tsx
- ProductDto
- MenuService
- BE-001
@ -48,32 +48,32 @@
- DEVOPS-001
- DOC-001
- adminRoutes.tsx
- WholesaleService
- B2BService
- ContactService
- WholesaleApplyDto
- B2BController
- ContactController
- FaqService
- راهنمای تست سیستم (Software Testing)
- Button.tsx
- Button
- CategoriesController
- MediaController
- What You Must Do When Invoked
- SslController
- BannersService
- TestimonialsService
- BannersController
- TestimonialsController
- What You Must Do When Invoked
- 20260526145407_init/migration.sql
- IngredientsService
- IngredientsController
- app.e2e-spec.js
- devDependencies
- devDependencies
- BlogsController
- prescriptions.controller.ts
- SmartAdvisorService
- MenuManager.tsx
- Button.tsx
- UITexts.tsx
- PrescriptionsManager.tsx
- Orders.tsx
- Role & Core Objective
- RevalidationService
- RedisService
- compilerOptions
- CreateUserDto
- ProductPage.tsx
@ -81,9 +81,9 @@
- dependencies
- compilerOptions
- admin.service.ts
- AdminQueryDto
- PetsController
- useSettingsStore
- AdminController
- ConfirmModal.tsx
- lib/services/api.ts
- Required Review Group Closures
- compilerOptions
- getPageMetadata
@ -91,7 +91,7 @@
- Operational Rules & Boundaries
- WikiController
- PetsController
- ProductsService
- RevalidationService
- seo.module.ts
- rss.xml/route.ts
- 🏢 AI Software Agency — Master Orchestration Protocol v3
@ -100,7 +100,7 @@
- scripts
- dependencies
- Role & Core Objective
- auth.controller.ts
- AuthController
- zibal.service.ts
- dependencies
- CreateEBankCheckoutDto
@ -108,9 +108,9 @@
- PetProfile.tsx
- Reconciled Audit Roles & Assignments
- OrdersService
- Orders.tsx
- auth.service.ts
- Phase 3.1 — Human Review Preparation and Master Backlog Critique
- getSeoConfig
- wiki/[slug]/page.tsx
- compilerOptions
- PaymentService
- scripts
@ -128,7 +128,7 @@
- Operational Rules & Boundaries
- Operational Rules & Boundaries
- Operational Rules & Boundaries
- AdminTransactionFilterDto
- sms.service.ts
- AppService
- Spinner.tsx
- Vazirmatn Changelog
@ -138,13 +138,13 @@
- compilerOptions
- backend/README.md
- AdminService
- Body
- ApiOperation
- Repository Map
- validate_integrity.js
- admin-panel/package.json
- Sahel-Font
- WholesaleApplyDto
- MetricsController
- HomeController
- auth.module.ts
- Sahel-Font
- Role & Core Objective
- orchestrate.py
@ -160,14 +160,14 @@
- start-dev.js
- generate-openapi.js
- SafeImage.tsx
- wiki/[slug]/page.tsx
- auth.controller.ts
- System Discovery
- catalog/page.tsx
- RouteErrorBoundary
- search/page.tsx
- torob.controller.ts
- Reports.tsx
- RegisterDto
- Product Requirement Document (PRD)
- @eslint/js
- auth.service.ts
- AuthService
- exclude
- Baseline Command Plan & Reconciled Command History
- seo-backfill.ts
@ -191,15 +191,15 @@
- rebuild_honest_ledger.js
- validate_evidence_grade.js
- uploads/[...path]/route.ts
- bcrypt
- WikiService
- نقشه جامع پوشش تست‌های سرتاسری (E2E Test Coverage Map) — پروژه Canina
- class-transformer
- track/page.tsx
- API Contract Specification
- ⚙️ Backend Technical Review (05_dev_backend)
- 🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)
- 🚀 SEO & Content Strategy Review (12_seo_content)
- helmet
- Reviews.tsx
- trust-seals/page.tsx
- @types/node
- seed-ui-texts.ts
- seed-wiki.ts
- update-blog.dto.ts
@ -211,14 +211,14 @@
- Raw Finding Verification & Disposition Report
- React + TypeScript + Vite
- Select.tsx
- toPersian
- js-yaml
- userStore.ts
- typescript
- prisma
- application/README.md
- deploy.sh
- 🔒 Security & Performance Review (09_devops_security)
- 👁️ UX & Persona Interface Review (08_visual_qa)
- @nestjs/core
- eslint-config-next
- prisma/scientificTerms.ts
- seed-blogs.ts
- seed-custom.ts
@ -234,20 +234,15 @@
- sync_honest_manifest.js
- sync_manifest.js
- FormField.tsx
- @nestjs/jwt
- Textarea.tsx
- admin-panel/tsconfig.json
- tailwindcss
- @nestjs/throttler
- next.config.ts
- Shabnam Font README
- AGENTS.md
- rules/graphify.md
- .agents/workflows/graphify.md
- instructions.md
- passport
- reflect-metadata
- swagger-ui-express
- source-map-support
- ts-loader
- ts-node
@ -275,7 +270,6 @@
- User Login API
- User Logout API
- @types/compression
- @eslint/eslintrc
- @types/express
- @types/jest
- @types/multer
@ -294,32 +288,22 @@
- Production Docker Compose
- Staging Docker Compose
- ZibalService
- eslint-plugin-prettier
- globals
- ZibalEBankService
- .initiateOrderPayment
- @tailwindcss/postcss
- typescript
- @nestjs/cli
- typescript-eslint
- @nestjs/schematics
- @nestjs/testing
- eslint-config-prettier
- prettier
- ts-jest
- jest
- @types/js-yaml
- @types/passport-jwt
- 20260526160916_add_ui_texts_and_scientific_terms/migration.sql
- @types/supertest
- typescript
- typescript-eslint
- revalidate/route.ts
- MaskableField.tsx
- @tailwindcss/postcss
- @types/react-dom
- eslint-plugin-react-refresh
- tailwindcss
## God Nodes (most connected - your core abstractions)
1. `Roles()` - 108 edges
@ -338,22 +322,22 @@
docs/02-user-guide.md → backend/uploads/1781288429353-508765350.jpg
- `AuthController` --references--> `ApiResponse` [EXTRACTED]
backend/src/auth/auth.controller.ts → frontend/admin-panel/src/types/admin.ts
- `BlogsController` --references--> `ApiResponse` [EXTRACTED]
backend/src/blogs/blogs.controller.ts → frontend/admin-panel/src/types/admin.ts
- `HomeController` --references--> `ApiResponse` [EXTRACTED]
backend/src/home/home.controller.ts → frontend/admin-panel/src/types/admin.ts
- `OrdersController` --references--> `ApiResponse` [EXTRACTED]
backend/src/orders/orders.controller.ts → frontend/admin-panel/src/types/admin.ts
- `PetsController` --references--> `ApiResponse` [EXTRACTED]
backend/src/pets/pets.controller.ts → frontend/admin-panel/src/types/admin.ts
- `ProductsController` --references--> `ApiResponse` [EXTRACTED]
backend/src/products/products.controller.ts → frontend/admin-panel/src/types/admin.ts
## Import Cycles
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/api.ts`
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/services/authService.ts -> frontend/application/lib/services/api.ts`
- 4-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/orderService.ts -> frontend/application/lib/services/api.ts`
## Communities (325 total, 116 thin omitted)
## Communities (309 total, 97 thin omitted)
### Community 0 - "Roles"
Cohesion: 0.23
Cohesion: 0.24
Nodes (13): Roles(), PaymentController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get (+5 more)
### Community 1 - "app.module.ts"
@ -366,11 +350,11 @@ Nodes (13): ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controlle
### Community 3 - "productService.ts"
Cohesion: 0.06
Nodes (35): DEFAULT_CATEGORIES, dynamic, revalidate, dynamic, revalidate, BlogCategory, BlogPostItem, CatalogPageSpread() (+27 more)
Nodes (35): B2BPortal, dynamic, GET(), revalidate, DEFAULT_CATEGORIES, dynamic, revalidate, dynamic (+27 more)
### Community 4 - "ClientLayout.tsx"
### Community 4 - "useSettingsStore"
Cohesion: 0.08
Nodes (23): ClientLayout(), LoginModal, BannerPlacement(), BannerPlacementProps, BrandLogo(), BrandLogoProps, Footer(), MaintenancePage() (+15 more)
Nodes (30): ClientLayout(), metadata, B2BLandingClient(), BrandLogo(), BrandLogoProps, Footer(), Header(), MENU_ICONS (+22 more)
### Community 5 - "CmsController"
Cohesion: 0.09
@ -386,35 +370,35 @@ Nodes (10): PatternItem, SmsConfigState, SmsEventDefinition, SmsEventVariable, S
### Community 8 - "SmsService"
Cohesion: 0.05
Nodes (28): SmsEventDefinition, SmsLogQuery, SmsService, Injectable, SmsLogQueryDto, ApiPropertyOptional, IsIn, IsNumber (+20 more)
Nodes (27): SmsEventDefinition, SmsService, Injectable, SmsLogQueryDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional (+19 more)
### Community 9 - "devDependencies"
Cohesion: 0.22
Nodes (9): devDependencies, eslint, @types/bcryptjs, @types/node, typescript, eslint, @types/node, typescript (+1 more)
Cohesion: 0.08
Nodes (25): devDependencies, eslint, @eslint/eslintrc, eslint-plugin-prettier, globals, @nestjs/cli, @nestjs/testing, prettier (+17 more)
### Community 10 - "CreateReviewDto"
### Community 10 - "reviews.controller.ts"
Cohesion: 0.07
Nodes (30): CreateReviewDto, ApiProperty, IsInt, IsNotEmpty, IsOptional, IsString, Min, ApiProperty (+22 more)
Nodes (32): CreateReviewDto, ApiProperty, IsInt, IsNotEmpty, IsOptional, IsString, Min, ApiProperty (+24 more)
### Community 11 - "UsersService"
Cohesion: 0.05
Nodes (42): DEFAULT_JWT_REFRESH_SECRET, DEFAULT_JWT_SECRET, getJwtSecret(), AuthModule, Module, JwtPayload, JwtStrategy, Injectable (+34 more)
Cohesion: 0.07
Nodes (32): AddressDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, ApiPropertyOptional (+24 more)
### Community 12 - "index.ts"
Cohesion: 0.06
Nodes (24): backend, frontend, playwright.config.ts, @playwright/test, tests/**/*.ts, authFile, test, compilerOptions (+16 more)
### Community 13 - "app-audit-verification.e2e-spec.js"
Cohesion: 0.07
Nodes (26): EnvironmentVariables, IsNotEmpty, IsOptional, IsString, validateEnv(), CustomHttpExceptionFilter, Catch, PrismaExceptionFilter (+18 more)
Cohesion: 0.06
Nodes (28): AppModule, Module, EnvironmentVariables, IsNotEmpty, IsOptional, IsString, validateEnv(), CustomHttpExceptionFilter (+20 more)
### Community 14 - "UserDashboard.tsx"
Cohesion: 0.11
Nodes (22): AddressFormData, AddressModal(), AddressModalProps, normalizeDigits(), validateAddressForm(), CheckoutPage(), DeleteConfirmModal(), DeleteConfirmModalProps (+14 more)
Cohesion: 0.14
Nodes (23): AddressFormData, AddressModal(), AddressModalProps, normalizeDigits(), validateAddressForm(), BackButton(), BackButtonProps, CheckoutPage() (+15 more)
### Community 15 - "src/services/api.ts"
Cohesion: 0.07
Nodes (34): Layout(), ProtectedRoute(), MenuGroup, Sidebar(), SidebarProps, SubMenuItem, LiveMonitoringSummary, SEARCHABLE_PAGES (+26 more)
Cohesion: 0.06
Nodes (38): Layout(), ProtectedRoute(), MenuGroup, Sidebar(), SidebarProps, SubMenuItem, LiveMonitoringSummary, SEARCHABLE_PAGES (+30 more)
### Community 16 - "DoctorQueryDto"
Cohesion: 0.09
@ -425,19 +409,23 @@ Cohesion: 0.14
Nodes (18): B2BPage(), generateMetadata(), ContactPage(), generateMetadata(), generateMetadata(), getInitialVideos(), Videos(), ContactFormClient() (+10 more)
### Community 18 - "JwtAuthGuard"
Cohesion: 0.21
Nodes (6): JwtAuthGuard, Injectable, ROLES_KEY, RequestWithUser, RolesGuard, Injectable
Cohesion: 0.15
Nodes (9): JwtAuthGuard, Injectable, B2BService, B2BWholesaleOrderItem, Injectable, ROLES_KEY, RequestWithUser, RolesGuard (+1 more)
### Community 19 - "admin.controller.ts"
Cohesion: 0.29
Cohesion: 0.35
Nodes (12): ApplyGlobalMarginsDto, BulkPriceAdjustmentDto, ApiProperty, ApiPropertyOptional, IsArray, IsBoolean, IsIn, IsNumber (+4 more)
### Community 20 - "CreateVideoDto"
Cohesion: 0.09
Nodes (24): CreateVideoDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, UpdateVideoDto (+16 more)
### Community 21 - "HomeClient.tsx"
Cohesion: 0.10
Nodes (22): HomeClient(), HomeClientProps, getHomeData(), Home(), BannerPlacement(), BannerPlacementProps, BlogPreviewSection(), FAQSection() (+14 more)
### Community 22 - "ProductDto"
Cohesion: 0.20
Cohesion: 0.16
Nodes (7): ProductDto, ApiPropertyOptional, IsArray, IsBoolean, IsNumber, IsOptional, IsString
### Community 23 - "MenuService"
@ -477,32 +465,32 @@ Cohesion: 0.06
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Category, Completion Statement, Confidence, Dependencies (+23 more)
### Community 32 - "adminRoutes.tsx"
Cohesion: 0.06
Nodes (22): App(), CategoryDist, DashboardData, SslStatus, WholesaleRequest, AdminRouteConfig, BannersManager, Categories (+14 more)
Cohesion: 0.07
Nodes (16): App(), Props, RouteErrorBoundary, State, HeroBanner, VetTestimonial, SslStatus, AdminRouteConfig (+8 more)
### Community 33 - "WholesaleService"
### Community 33 - "WholesaleApplyDto"
Cohesion: 0.10
Nodes (20): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto, ApiBearerAuth, ApiOperation (+12 more)
### Community 34 - "B2BController"
Cohesion: 0.14
Nodes (14): ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param, Post (+6 more)
Nodes (13): B2BController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+5 more)
### Community 34 - "B2BService"
Cohesion: 0.12
Nodes (16): B2BController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+8 more)
### Community 35 - "ContactService"
### Community 35 - "ContactController"
Cohesion: 0.13
Nodes (12): ContactController, Body, Controller, Get, Param, Post, Put, Query (+4 more)
Nodes (10): ContactController, Body, Controller, Get, Param, Post, Put, Query (+2 more)
### Community 36 - "FaqService"
Cohesion: 0.12
Nodes (16): FaqController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+8 more)
Cohesion: 0.14
Nodes (14): FaqController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
### Community 37 - "راهنمای تست سیستم (Software Testing)"
Cohesion: 0.07
Nodes (25): اجرای بک‌اند, اجرای فرانت‌اند, اجرای پروژه در سرور با PM2, برای راه‌اندازی بک‌اند:, برای راه‌اندازی فرانت‌اند Next.js:, بیلد کردن پروژه (Building), ذخیره پروسه‌ها تا پس از ری‌استارت سرور قطع نشوند:, راه‌اندازی و انتشار سیستم (Setup & Deployment) (+17 more)
### Community 38 - "Button.tsx"
Cohesion: 0.06
Nodes (32): TransactionReceiptData, TransactionReceiptModal(), TransactionReceiptModalProps, Button(), ButtonProps, ButtonSize, ButtonVariant, ThSort() (+24 more)
### Community 38 - "Button"
Cohesion: 0.16
Nodes (13): TransactionReceiptData, TransactionReceiptModal(), TransactionReceiptModalProps, Button(), ThSort(), ThSortProps, GatewayHealth, Stats (+5 more)
### Community 39 - "CategoriesController"
Cohesion: 0.10
@ -510,7 +498,7 @@ Nodes (16): CategoriesController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags
### Community 40 - "MediaController"
Cohesion: 0.11
Nodes (16): MediaController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+8 more)
Nodes (14): MediaController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
### Community 41 - "What You Must Do When Invoked"
Cohesion: 0.07
@ -520,13 +508,13 @@ Nodes (26): For /graphify add and --watch, For /graphify query, For the commit h
Cohesion: 0.13
Nodes (14): SslController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Post (+6 more)
### Community 43 - "BannersService"
### Community 43 - "BannersController"
Cohesion: 0.13
Nodes (15): BannersController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+7 more)
Nodes (13): BannersController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+5 more)
### Community 44 - "TestimonialsService"
### Community 44 - "TestimonialsController"
Cohesion: 0.13
Nodes (14): TestimonialsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
Nodes (12): TestimonialsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+4 more)
### Community 45 - "What You Must Do When Invoked"
Cohesion: 0.07
@ -536,9 +524,9 @@ Nodes (26): For /graphify add and --watch, For /graphify query, For the commit h
Cohesion: 0.27
Nodes (14): "coupons", "health_logs", "order_items", "orders", "pet_medical_conditions", "pets", "product_ingredients", "product_symptoms" (+6 more)
### Community 47 - "IngredientsService"
### Community 47 - "IngredientsController"
Cohesion: 0.13
Nodes (14): IngredientsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
Nodes (12): IngredientsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+4 more)
### Community 48 - "app.e2e-spec.js"
Cohesion: 0.50
@ -550,7 +538,7 @@ Nodes (19): autoprefixer, devDependencies, autoprefixer, eslint, @eslint/js, glo
### Community 50 - "devDependencies"
Cohesion: 0.12
Nodes (17): eslint-config-next, devDependencies, eslint, eslint-config-next, jsdom, @testing-library/jest-dom, @testing-library/react, @types/node (+9 more)
Nodes (17): devDependencies, eslint, jsdom, tailwindcss, @testing-library/jest-dom, @testing-library/react, @types/node, @types/react (+9 more)
### Community 51 - "BlogsController"
Cohesion: 0.07
@ -564,25 +552,25 @@ Nodes (17): PrescriptionsController, ApiBearerAuth, ApiOperation, ApiTags, Body,
Cohesion: 0.13
Nodes (14): SmartAdvisorController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
### Community 54 - "MenuManager.tsx"
Cohesion: 0.33
Nodes (4): MENU_TABS, MenuItem, MenuType, MenuManager
### Community 54 - "Button.tsx"
Cohesion: 0.07
Nodes (24): ButtonProps, ButtonSize, ButtonVariant, maxWidthClasses, Modal(), ModalProps, ContactInfoItem, ContactSubmission (+16 more)
### Community 55 - "UITexts.tsx"
Cohesion: 0.10
Nodes (17): ICON_REGISTRY, IconPickerModal(), IconPickerModalProps, ActiveStates, PRESET_BG_COLORS, PRESET_COLORS, RichTextEditor(), RichTextEditorProps (+9 more)
Cohesion: 0.08
Nodes (23): ICON_REGISTRY, IconPickerModal(), IconPickerModalProps, ActiveStates, PRESET_BG_COLORS, PRESET_COLORS, RichTextEditor(), RichTextEditorProps (+15 more)
### Community 56 - "PrescriptionsManager.tsx"
Cohesion: 0.12
Nodes (14): Badge(), BadgeProps, BadgeVariant, variantStyles, Skeleton(), MonitoringStats, ProductItem, UserRecord (+6 more)
### Community 56 - "Orders.tsx"
Cohesion: 0.11
Nodes (15): Badge(), BadgeProps, BadgeVariant, variantStyles, Skeleton(), MonitoringStats, getPaymentMethodLabel(), Order (+7 more)
### Community 57 - "Role & Core Objective"
Cohesion: 0.09
Nodes (22): CREATE MODE — Normal Operation, Expected JSON Output Schema, File Scope Boundary, Forbidden Actions, MODE 1: REVIEW (called during Review Phase), MODE 2: CONTENT CREATION (standalone task from backlog), Operating Modes, Operational Rules & Boundaries (+14 more)
### Community 58 - "RevalidationService"
Cohesion: 0.23
Nodes (3): Optional, RevalidationService, Injectable
### Community 58 - "RedisService"
Cohesion: 0.14
Nodes (6): Optional, RedisModule, Global, Module, RedisService, Injectable
### Community 59 - "compilerOptions"
Cohesion: 0.06
@ -594,35 +582,35 @@ Nodes (10): CreateUserDto, ApiProperty, ApiPropertyOptional, IsEmail, IsNotEmpty
### Community 61 - "ProductPage.tsx"
Cohesion: 0.11
Nodes (24): ArchiveProductCard(), CATEGORY_MAP, ICON_MAP, ProductCard(), ProductImageZoomModalProps, CalculatorState, GalleryMediaItem, ICON_MAP (+16 more)
Nodes (38): VerifyContent(), ArchivePage(), ArchiveProductCard(), ArchiveProductListItem(), CATEGORY_MAP, ICON_MAP, B2BPortal(), CartDrawer() (+30 more)
### Community 62 - "admin.module.ts"
Cohesion: 0.07
Nodes (20): AdminModule, Module, ReportsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Controller (+12 more)
Cohesion: 0.05
Nodes (30): AdminModule, Module, MediaService, Injectable, PetsController, ApiBearerAuth, ApiOperation, ApiQuery (+22 more)
### Community 63 - "dependencies"
Cohesion: 0.09
Nodes (23): dependencies, bcryptjs, class-validator, compression, ioredis, @nestjs/common, @nestjs/passport, @nestjs/platform-express (+15 more)
Cohesion: 0.05
Nodes (43): dependencies, bcrypt, bcryptjs, class-transformer, class-validator, compression, helmet, ioredis (+35 more)
### Community 64 - "compilerOptions"
Cohesion: 0.10
Nodes (20): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, lib, module, moduleDetection, moduleResolution, noEmit (+12 more)
### Community 65 - "admin.service.ts"
Cohesion: 0.39
Nodes (7): CouponTargetInput, PaginationQuery, applyRounding(), calculateSellingPrice(), DEFAULT_PRICING_SETTINGS, PricingSettings, RoundingMode
Cohesion: 0.33
Nodes (8): CouponTargetInput, PaginationQuery, applyRounding(), calculateSellingPrice(), DEFAULT_PRICING_SETTINGS, PricingSettings, RoundingMode, CANONICAL_ALIASES
### Community 66 - "AdminQueryDto"
Cohesion: 0.18
Nodes (7): ApiQuery, Query, AdminProductQueryDto, AdminQueryDto, ApiPropertyOptional, IsOptional, IsString
### Community 67 - "PetsController"
### Community 66 - "AdminController"
Cohesion: 0.10
Nodes (14): PetsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Controller, Delete, Get (+6 more)
Nodes (13): AdminController, ApiBearerAuth, ApiQuery, ApiTags, Controller, Get, Query, UseGuards (+5 more)
### Community 68 - "useSettingsStore"
Cohesion: 0.07
Nodes (35): HomeClient(), HomeClientProps, B2BLandingClient(), BlogPostClientProps, BlogPost, BlogPreviewSection(), ContactInfoItem, EnamadBadge() (+27 more)
### Community 67 - "ConfirmModal.tsx"
Cohesion: 0.10
Nodes (16): ConfirmModal(), ConfirmModalProps, Pagination(), PaginationProps, Category, Pet, DoctorOption, Video (+8 more)
### Community 68 - "lib/services/api.ts"
Cohesion: 0.09
Nodes (16): BlogPost, ContactInfoItem, FAQItem, Testimonial, api, ApiErrorPayload, BASE_DOMAIN, baseURL (+8 more)
### Community 69 - "Required Review Group Closures"
Cohesion: 0.10
@ -634,7 +622,7 @@ Nodes (23): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, jsx
### Community 71 - "getPageMetadata"
Cohesion: 0.08
Nodes (18): generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), getHomeData(), Home(), generateMetadata() (+10 more)
Nodes (16): generateMetadata(), CatalogClient(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata() (+8 more)
### Community 72 - "Operational Rules & Boundaries"
Cohesion: 0.11
@ -652,9 +640,9 @@ Nodes (14): ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, De
Cohesion: 0.05
Nodes (47): CreateHealthLogDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, CreatePetDto, ApiProperty (+39 more)
### Community 76 - "ProductsService"
Cohesion: 0.06
Nodes (30): RevalidationModule, Global, Module, GetProductsDto, ApiPropertyOptional, IsEnum, IsOptional, IsString (+22 more)
### Community 76 - "RevalidationService"
Cohesion: 0.07
Nodes (24): RevalidationModule, Global, Module, RevalidationService, Injectable, GetProductsDto, ApiPropertyOptional, IsEnum (+16 more)
### Community 77 - "seo.module.ts"
Cohesion: 0.16
@ -688,9 +676,9 @@ Nodes (21): dependencies, axios, lucide-react, react, react-dom, react-hot-toast
Cohesion: 0.12
Nodes (16): 1. Active Secret Scanning (All Modified Files), 2. Docker Container Verification (if Dockerfile exists), 3. CI/CD Basic Check (if `.github/workflows/` exists), 4. Failure Routing Protocol, 5. Forbidden Actions, ENFORCE MODE — Normal Operation, Expected JSON Output Schema, Operational Rules & Boundaries (+8 more)
### Community 85 - "auth.controller.ts"
Cohesion: 0.06
Nodes (43): AuthController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+35 more)
### Community 85 - "AuthController"
Cohesion: 0.25
Nodes (13): AuthController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+5 more)
### Community 86 - "zibal.service.ts"
Cohesion: 0.16
@ -709,8 +697,8 @@ Cohesion: 0.17
Nodes (12): prisma, main(), prisma, determineSuitableFor(), findOrCreateCategory(), getProductImageUrl(), main(), parseSize() (+4 more)
### Community 90 - "PetProfile.tsx"
Cohesion: 0.10
Nodes (30): B2BPortal, VerifyContent(), metadata, B2BPortal(), Header(), MENU_ICONS, OrderSuccess(), OrderTracking() (+22 more)
Cohesion: 0.12
Nodes (11): OrderDetailsModal(), OrderRowSkeleton(), PetProfileSkeleton(), ProductListSkeleton(), Skeleton(), SkeletonProps, HealthLog, PetConsumption (+3 more)
### Community 91 - "Reconciled Audit Roles & Assignments"
Cohesion: 0.12
@ -720,29 +708,33 @@ Nodes (15): 10. Documentation Engineer, 1. Lead Architect / Orchestrator, 2. Rea
Cohesion: 0.07
Nodes (29): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+21 more)
### Community 93 - "Orders.tsx"
Cohesion: 0.22
Nodes (8): getPaymentMethodLabel(), Order, ORDER_STATUS_MAP, OrderItem, Orders(), PaymentTx, toPersianDigits(), Orders
### Community 93 - "auth.service.ts"
Cohesion: 0.13
Nodes (14): AdminLoginInput, LoginInput, RegisterInput, SendOtpDto, ApiProperty, IsNotEmpty, IsString, Matches (+6 more)
### Community 94 - "Phase 3.1 — Human Review Preparation and Master Backlog Critique"
Cohesion: 0.13
Nodes (14): 10. Dependency & Wave Adjustments, 11. Acceptance Criteria Refinements, 12. Business and Product Decision Classification, 13. Final Recommended Backlog Summary, 1. Executive Assessment, 2. Findings That Require No Change, 3. Findings Requiring Task Refinements, 4. Tasks Requiring Splitting (+6 more)
### Community 95 - "getSeoConfig"
Cohesion: 0.24
Nodes (12): BlogPostPage(), generateMetadata(), getBlog(), revalidate, safeIso(), safeLocalDate(), generateMetadata(), revalidate (+4 more)
### Community 95 - "wiki/[slug]/page.tsx"
Cohesion: 0.18
Nodes (17): BlogPostPage(), generateMetadata(), getBlog(), revalidate, safeIso(), safeLocalDate(), generateMetadata(), revalidate (+9 more)
### Community 96 - "compilerOptions"
Cohesion: 0.06
Nodes (32): compilerOptions, allowJs, esModuleInterop, incremental, isolatedModules, jsx, lib, module (+24 more)
### Community 97 - "PaymentService"
Cohesion: 0.10
Nodes (9): AdminTransactionFilterDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type, PaymentService (+1 more)
### Community 98 - "scripts"
Cohesion: 0.13
Nodes (15): scripts, build, build:nest, docs:generate, lint, seo:backfill, start, start:debug (+7 more)
### Community 99 - "BlogsController"
Cohesion: 0.08
Nodes (23): BlogsController, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Body, Controller, Get (+15 more)
Cohesion: 0.18
Nodes (12): BlogsController, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Body, Controller, Get (+4 more)
### Community 100 - "Deep Audit Summary Report"
Cohesion: 0.14
@ -777,8 +769,8 @@ Cohesion: 0.06
Nodes (25): BlogsModule, Module, BlogFilterDto, BlogsService, Injectable, PaginationDto, SortOrder, ApiPropertyOptional (+17 more)
### Community 108 - "PrismaService"
Cohesion: 0.07
Nodes (25): CategoryQuery, DEFAULT_SMS_RULES, SMS_EVENT_DEFINITIONS, SmsEventVariable, SmsRule, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions (+17 more)
Cohesion: 0.05
Nodes (26): ApiExcludeController, CategoryQuery, WikiQuery, BannersService, Injectable, MetricsController, Controller, Get (+18 more)
### Community 109 - "1. Summary of Integrity Repairs Performed"
Cohesion: 0.17
@ -796,17 +788,17 @@ Nodes (10): 1. Mark Active Task as Complete, 2. Documentation Updates, 3. Dynami
Cohesion: 0.18
Nodes (10): 1. Pre-Deployment Checklist, 2. Build Verification, 3. Deployment Strategy (Based on Target), 4. Post-Deployment Health Check, 5. Forbidden Actions, Expected JSON Output Schema, Operational Rules & Boundaries, Required Output Artifacts (What files to write/update) (+2 more)
### Community 113 - "AdminTransactionFilterDto"
Cohesion: 0.22
Nodes (7): AdminTransactionFilterDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type
### Community 113 - "sms.service.ts"
Cohesion: 0.12
Nodes (13): DEFAULT_SMS_RULES, SMS_EVENT_DEFINITIONS, SmsEventVariable, SmsRule, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions, SmsConfig (+5 more)
### Community 114 - "AppService"
Cohesion: 0.29
Nodes (5): AppController, Controller, Get, AppService, Injectable
### Community 115 - "Spinner.tsx"
Cohesion: 0.08
Nodes (34): ConfirmModal(), ConfirmModalProps, ImagePreviewModal(), ImagePreviewModalProps, getFileType(), Media, MediaSelector(), MediaSelectorProps (+26 more)
Cohesion: 0.10
Nodes (22): ImagePreviewModal(), ImagePreviewModalProps, getFileType(), Media, MediaSelector(), MediaSelectorProps, Spinner(), Doctor (+14 more)
### Community 116 - "Vazirmatn Changelog"
Cohesion: 0.18
@ -833,12 +825,12 @@ Cohesion: 0.20
Nodes (9): Compile and run the project, Deployment, Description, License, Project setup, Resources, Run tests, Stay in touch (+1 more)
### Community 122 - "AdminService"
Cohesion: 0.09
Nodes (12): AdminController, ApiBearerAuth, ApiOperation, ApiTags, Controller, Delete, Get, Param (+4 more)
Cohesion: 0.12
Nodes (5): Delete, Param, Put, AdminService, Injectable
### Community 123 - "Body"
Cohesion: 0.20
Nodes (3): Body, Post, CouponInput
### Community 123 - "ApiOperation"
Cohesion: 0.15
Nodes (4): ApiOperation, Body, Post, CouponInput
### Community 124 - "Repository Map"
Cohesion: 0.20
@ -856,13 +848,13 @@ Nodes (9): name, private, scripts, build, dev, lint, preview, type (+1 more)
Cohesion: 0.20
Nodes (9): Arch Linux, Contributors, Install, Known problems for variable version, License, Sahel-Font, To Do (variable), طریقه استفاده از نسخه متغیر variable (+1 more)
### Community 128 - "WholesaleApplyDto"
Cohesion: 0.29
Nodes (6): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto
### Community 128 - "HomeController"
Cohesion: 0.16
Nodes (10): HomeController, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, HomeModule, Module (+2 more)
### Community 129 - "MetricsController"
Cohesion: 0.29
Nodes (5): ApiExcludeController, MetricsController, Controller, Get, Res
### Community 129 - "auth.module.ts"
Cohesion: 0.18
Nodes (10): DEFAULT_JWT_REFRESH_SECRET, DEFAULT_JWT_SECRET, getJwtSecret(), AuthModule, Module, JwtPayload, JwtStrategy, Injectable (+2 more)
### Community 130 - "Sahel-Font"
Cohesion: 0.20
@ -921,28 +913,36 @@ Cohesion: 0.25
Nodes (6): app_module_1, core_1, fs, path, swagger_1, yaml
### Community 144 - "SafeImage.tsx"
Cohesion: 0.08
Nodes (30): buildTorobProductSpec(), buildTorobProductTitle(), dynamic, GET(), revalidate, dynamic, GET(), revalidate (+22 more)
Cohesion: 0.09
Nodes (25): buildTorobProductSpec(), buildTorobProductTitle(), dynamic, GET(), revalidate, BlogPostClientProps, OrderDetailsModalProps, PLAYBACK_RATES (+17 more)
### Community 145 - "wiki/[slug]/page.tsx"
Cohesion: 0.60
Nodes (5): generateMetadata(), getRelatedProducts(), getWikiTerm(), WikiTermPage(), generateMedicalWebPageSchema()
### Community 145 - "auth.controller.ts"
Cohesion: 0.16
Nodes (11): AdminLoginDto, ApiProperty, IsEmail, IsNotEmpty, IsString, MinLength, LoginDto, ApiProperty (+3 more)
### Community 146 - "System Discovery"
Cohesion: 0.25
Nodes (7): Active Core Applications, Current Architecture, Evidence-Based Status Matrix, Excluded / Non-Auditable Artifacts, Major Business Domains Discovered, System Discovery, Technical Architecture Summary
### Community 148 - "RouteErrorBoundary"
### Community 147 - "torob.controller.ts"
Cohesion: 0.22
Nodes (3): Props, RouteErrorBoundary, State
Nodes (8): buildTorobProductSpec(), buildTorobProductTitle(), TorobController, ApiOperation, ApiTags, Controller, Get, Query
### Community 148 - "Reports.tsx"
Cohesion: 0.20
Nodes (7): BestSellerItem, CategoryDistItem, COLORS, CustomTooltipProps, ReportData, TopCouponItem, Reports
### Community 149 - "RegisterDto"
Cohesion: 0.22
Nodes (8): RegisterDto, ApiProperty, ApiPropertyOptional, IsEmail, IsNotEmpty, IsOptional, IsString, MinLength
### Community 150 - "Product Requirement Document (PRD)"
Cohesion: 0.29
Nodes (6): 1. Executive Vision, 2. Target Audience, 3. Functional Requirements, 4. Non-Functional Requirements (Performance, Security), 5. Epic / Feature Breakdown, Product Requirement Document (PRD)
### Community 152 - "auth.service.ts"
Cohesion: 0.08
Nodes (10): AppModule, Module, AdminLoginInput, AuthService, LoginInput, RegisterInput, Injectable, normalizeMobile() (+2 more)
### Community 152 - "AuthService"
Cohesion: 0.15
Nodes (3): AuthService, Injectable, normalizeMobile()
### Community 153 - "exclude"
Cohesion: 0.22
@ -1052,10 +1052,6 @@ Nodes (3): Architectural Overview, Critical Review Findings & Required Enhanceme
Cohesion: 0.50
Nodes (3): Overview & Content Foundation, SEO & Content Requirements, 🚀 SEO & Content Strategy Review (12_seo_content)
### Community 185 - "Reviews.tsx"
Cohesion: 0.40
Nodes (5): BlogCommentItem, ProductReview, Reviews(), toPersianDigits(), Reviews
### Community 191 - "graphify reference: add a URL and watch a folder"
Cohesion: 0.50
Nodes (3): For /graphify add, For --watch, graphify reference: add a URL and watch a folder
@ -1080,9 +1076,9 @@ Nodes (3): Expanding the ESLint configuration, React Compiler, React + TypeScrip
Cohesion: 0.50
Nodes (3): Select, SelectOption, SelectProps
### Community 197 - "toPersian"
Cohesion: 0.09
Nodes (23): AuthModal, CartDrawer, AuthModal(), AuthModalProps, extractOtpFromText(), otpCooldownExpiries, IMPORTANT: only depend on isOpen here — NOT subView. If we depend on subView,, CartDrawer() (+15 more)
### Community 197 - "userStore.ts"
Cohesion: 0.07
Nodes (27): AuthModal, CartDrawer, LoginModal, AuthModal(), AuthModalProps, extractOtpFromText(), otpCooldownExpiries, IMPORTANT: only depend on isOpen here — NOT subView. If we depend on subView, (+19 more)
### Community 200 - "application/README.md"
Cohesion: 0.50
@ -1101,7 +1097,7 @@ Cohesion: 0.67
Nodes (3): Shabnam Font README, Shabnam Font Sample, Vazir Font
### Community 298 - ".initiateOrderPayment"
Cohesion: 0.18
Cohesion: 0.20
Nodes (10): ApiPropertyOptional, IsOptional, IsString, ZibalCallbackQueryDto, ApiBadRequestResponse, ApiOkResponse, Req, Res (+2 more)
### Community 317 - "revalidate/route.ts"
@ -1111,22 +1107,22 @@ Nodes (3): GET(), handleRevalidate(), POST()
## Knowledge Gaps
- **1355 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1350 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **116 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
- **97 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `Roles()` connect `Roles` to `PaymentService`, `B2BService`, `ContactService`, `FaqService`, `CmsController`, `tickets.controller.ts`, `WholesaleService`, `SmsService`, `SslController`, `BannersService`, `ProductsService`, `CreateReviewDto`, `TestimonialsService`, `IngredientsService`, `JwtAuthGuard`, `prescriptions.controller.ts`, `SmartAdvisorService`, `MenuService`?**
- **Why does `Roles()` connect `Roles` to `WholesaleApplyDto`, `B2BController`, `ContactController`, `FaqService`, `CmsController`, `tickets.controller.ts`, `SmsService`, `SslController`, `BannersController`, `RevalidationService`, `reviews.controller.ts`, `TestimonialsController`, `IngredientsController`, `JwtAuthGuard`, `prescriptions.controller.ts`, `SmartAdvisorService`, `MenuService`?**
_High betweenness centrality (0.093) - this node is a cross-community bridge._
- **Why does `ApiResponse` connect `BlogsController` to `WikiController`, `UsersService`, `PetsController`, `ProductsService`, `src/services/api.ts`, `auth.controller.ts`, `OrdersService`?**
- **Why does `ApiResponse` connect `src/services/api.ts` to `HomeController`, `WikiController`, `BlogsController`, `PetsController`, `RevalidationService`, `UsersService`, `AuthController`, `OrdersService`?**
_High betweenness centrality (0.066) - this node is a cross-community bridge._
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `PetsController`, `CmsController`, `tickets.controller.ts`, `PaginationDto`, `PetsController`, `ProductsService`, `UsersService`, `DoctorQueryDto`, `admin.controller.ts`, `prescriptions.controller.ts`, `auth.controller.ts`, `admin.module.ts`?**
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `CmsController`, `tickets.controller.ts`, `reviews.controller.ts`, `PaginationDto`, `PrismaService`, `PetsController`, `RevalidationService`, `auth.controller.ts`, `admin.controller.ts`, `prescriptions.controller.ts`, `admin.module.ts`?**
_High betweenness centrality (0.030) - this node is a cross-community bridge._
- **What connects `$schema`, `collection`, `sourceRoot` to the rest of the system?**
_1355 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `app.module.ts` be split into smaller, more focused modules?**
_Cohesion score 0.06848357791754019 - nodes in this community are weakly interconnected._
_Cohesion score 0.06531986531986532 - nodes in this community are weakly interconnected._
- **Should `WikiController` be split into smaller, more focused modules?**
_Cohesion score 0.12554112554112554 - nodes in this community are weakly interconnected._
- **Should `productService.ts` be split into smaller, more focused modules?**
_Cohesion score 0.06093189964157706 - nodes in this community are weakly interconnected._
_Cohesion score 0.061955965181771634 - nodes in this community are weakly interconnected._

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff