feat(seo): unify dynamic SEO metadata and title formatting across backend, admin and application
All checks were successful
Deploy Canina / deploy (push) Successful in 1m44s

This commit is contained in:
parsa aghaei 2026-08-19 15:03:10 +03:30
parent df634fdbf5
commit 70d58e7086
39 changed files with 133392 additions and 7694 deletions

View File

@ -161,4 +161,94 @@ export const DEFAULT_UI_TEXTS: Record<string, string> = {
footer_about: 'درباره ما',
footer_contact: 'تماس با ما',
footer_privacy: 'حریم خصوصی و قوانین',
// === Global SEO & Title Formatting ===
brand_name_en: 'Canina Iran',
title_separator: '|',
title_position: 'suffix',
defaultMetaTitle: 'کنینا ایران | نماینده رسمی مکمل‌های دارویی Canina آلمان',
defaultMetaDescription: 'فروشگاه تخصصی و نماینده انحصاری مکمل‌های درمانی و تقویتی سگ و گربه کنینا آلمان با فرمولاسیون دارویی و تاییدیه بالینی.',
canonicalBaseUrl: 'https://canina.ir',
ogImageUrl: '/assets/images/regenerated_image_1779109861747.png',
// === Page-by-Page SEO Metadata Defaults ===
home_meta_title: 'کنینا ایران | نماینده رسمی مکمل‌های دارویی Canina آلمان',
home_meta_desc: 'فروشگاه تخصصی و نماینده انحصاری مکمل‌های درمانی و تقویتی سگ و گربه کنینا آلمان با فرمولاسیون دارویی و تاییدیه بالینی.',
home_keywords: 'کنینا, مکمل سگ, مکمل گربه, مکمل دارویی حیوانات, Canina Germany',
home_canonical: 'https://canina.ir',
shop_meta_title: 'فروشگاه تخصصی مکمل‌های دارویی سگ و گربه',
shop_meta_desc: 'خرید آنلاین انواع مکمل‌های درمانی مفاصل، پوست و مو، گوارش و ایمنی، رشد و ویتامینه سگ و گربه با ضمانت اصالت کنینا آلمان.',
shop_keywords: 'خرید مکمل سگ, خرید مکمل گربه, مکمل مفاصل پت, پت شاپ تخصصی دارویی',
shop_canonical: 'https://canina.ir/shop',
catalog_meta_title: 'کاتالوگ رسمی مکمل‌های درمانی و دارویی',
catalog_meta_desc: 'مشاهده و مطالعه کاتالوگ جامع دارویی و مکمل‌های تغذیه‌ای تخصصی کنینا آلمان با قابلیت ورق‌زدن تعاملی و ثبت پیش‌خرید.',
catalog_keywords: 'کاتالوگ کنینا, کاتالوگ محصولات Canina, راهنمای مکمل‌های دارویی پت',
catalog_canonical: 'https://canina.ir/catalog',
blog_meta_title: 'مجله سلامت و دانشنامه مقالات تخصصی پت',
blog_meta_desc: 'جدیدترین مقالات علمی، راهنمای تغذیه، بیماری‌های سگ و گربه و توصیه‌های بالینی متخصصان و دامپزشکان کنینا.',
blog_keywords: 'مقالات سگ و گربه, مجله سلامت حیوانات, تغذیه پت, بیماریهای سگ و گربه',
blog_canonical: 'https://canina.ir/blog',
wiki_meta_title: 'دانشنامه علمی ترکیبات و اصطلاحات دارویی',
wiki_meta_desc: 'مرجع تخصصی اثرات بالینی، مواد تشکیل‌دهنده، ویتامین‌ها و ترکیبات ارگانیک به کار رفته در مکمل‌های دارویی کنینا آلمان.',
wiki_keywords: 'دانشنامه دارویی پت, ترکیبات مکمل سگ, گلوکوزامین سگ, صدف سبز نیوزیلند',
wiki_canonical: 'https://canina.ir/wiki',
about_meta_title: 'درباره شرکت کنینا ایران و اصالت آلمانی',
about_meta_desc: 'داستان کنینا ایران و بیش از ۴۰ سال سابقه درخشان شرکت Canina pharma GmbH آلمان در تولید مکمل‌های فوق‌تخصصی دامپزشکی.',
about_keywords: 'درباره کنینا, شرکت Canina pharma, تاریخچه مکمل کنینا, استاندارد دارویی آلمان',
about_canonical: 'https://canina.ir/about',
contact_meta_title: 'تماس با ما و مشاوره تخصصی دامپزشکی',
contact_meta_desc: 'راه‌های ارتباطی با دفتر مرکزی کنینا ایران، مشاوره تلفنی رایگان، پشتیبانی آنلاین و نشانی.',
contact_keywords: 'تماس با کنینا, پشتیبانی کنینا ایران, مشاوره دارویی پت',
contact_canonical: 'https://canina.ir/contact',
b2b_meta_title: 'پرتال همکاران تجاری، کلینیک‌ها و پت‌شاپ‌ها',
b2b_meta_desc: 'ثبت‌نام و خرید عمده مکمل‌های دارویی کنینا ویژه کلینیک‌های دامپزشکی، بیمارستان‌ها و پت‌شاپ‌های سراسر کشور با تخفیف سازمانی.',
b2b_keywords: 'خرید عمده مکمل پت, نمایندگی کنینا, فروش سازمانی دامپزشکی',
b2b_canonical: 'https://canina.ir/b2b',
videos_meta_title: 'ویدئوهای آموزشی و توصیه‌های بالینی دامپزشکان',
videos_meta_desc: 'مشاهده ویدئوهای تخصصی نحوه مصرف مکمل‌های درمانی، نظرات دامپزشکان و راهکارهای بالینی تصویری.',
videos_keywords: 'ویدیوهای آموزشی پت, راهنمای مصرف مکمل سگ, فیلم های دامپزشکی',
videos_canonical: 'https://canina.ir/videos',
search_meta_title: 'جستجوی هوشمند مکمل‌ها و دانشنامه سلامت',
search_meta_desc: 'جستجوی سریع در کاتالوگ محصولات، بیماری‌ها، علائم بالینی و مقالات سلامت کنینا ایران.',
search_keywords: 'جستجوی داروی پت, سرچ مکمل کنینا',
search_canonical: 'https://canina.ir/search',
dashboard_meta_title: 'داشبورد کاربری و پیگیری سفارشات',
dashboard_meta_desc: 'مشاهده تاریخچه خریدها، وضعیت کیف پول، پیگیری آنلاین بسته‌های پستی و مدیریت پت‌ها.',
dashboard_keywords: 'داشبورد کاربری, پروفایل کنینا',
dashboard_canonical: 'https://canina.ir/dashboard',
profile_pets_meta_title: 'پرونده الکترونیک سلامت پت',
profile_pets_meta_desc: 'مدیریت و ثبت پرونده سلامت سگ و گربه، تنظیم یادآورهای دوز دارو و تاریخچه تغذیه مکمل‌ها.',
profile_pets_keywords: 'پرونده سلامت سگ, یادآور داروی پت, شناسنامه گربه',
profile_pets_canonical: 'https://canina.ir/profile',
checkout_meta_title: 'تسویه‌حساب و پرداخت امن',
checkout_meta_desc: 'انتخاب آدرس تحویل، روش ارسال و تسویه‌حساب سریع و امن سفارشات مکمل‌های کنینا.',
checkout_keywords: 'خرید آنلاین مکمل, پرداخت سفارش',
checkout_canonical: 'https://canina.ir/checkout',
track_meta_title: 'پیگیری آنلاین سفارش و مرسوله پستی',
track_meta_desc: 'پیگیری وضعیت لحظه‌ای پردازش، بسته‌بندی و کد رهگیری پست پیشتاز سفارشات کنینا ایران.',
track_keywords: 'رهگیری پست پیشتاز, پیگیری سفارش کنینا',
track_canonical: 'https://canina.ir/track',
trust_seals_meta_title: 'مجوزهای رسمی، گواهی اصالت و اینماد',
trust_seals_meta_desc: 'بررسی تاییدیه‌های رسمی سازمان دامپزشکی کشور، گواهی استاندارد آلمان و اینماد رسمی کنینا ایران.',
trust_seals_keywords: 'اینماد کنینا, مجوز دامپزشکی مکمل, اصالت کالا کنینا',
trust_seals_canonical: 'https://canina.ir/trust-seals',
privacy_meta_title: 'حریم خصوصی و شرایط استفاده',
privacy_meta_desc: 'سیاست‌های حفاظت از اطلاعات شخصی کاربران، امنیت داده‌های پزشکی و قوانین استفاده از خدمات کنینا ایران.',
privacy_keywords: 'قوانین کنینا, شرایط مرجوعی, حریم خصوصی',
privacy_canonical: 'https://canina.ir/privacy',
};

View File

@ -394,6 +394,68 @@ export class SettingsService implements OnModuleInit {
};
}
if (category === 'seo') {
const texts = await this.getUiTexts();
const map = new Map<string, string>();
texts.forEach((t) => map.set(t.key, t.value));
const setting = await this.prisma.setting.findFirst({
where: { category: 'seo' },
});
const storedObj =
setting && typeof setting.value === 'object' && setting.value !== null
? (setting.value as Record<string, any>)
: {};
return {
brandNameFa:
storedObj.brandNameFa ||
storedObj.brand_name_fa ||
map.get('brand_name_fa') ||
map.get('BRAND_LOGO_TEXT_FA') ||
'کنینا ایران',
brandNameEn:
storedObj.brandNameEn ||
storedObj.brand_name_en ||
map.get('brand_name_en') ||
map.get('BRAND_LOGO_TEXT_EN') ||
'Canina Iran',
titleSeparator:
storedObj.titleSeparator ||
storedObj.title_separator ||
map.get('title_separator') ||
'|',
titlePosition:
storedObj.titlePosition ||
storedObj.title_position ||
map.get('title_position') ||
'suffix',
defaultMetaTitle:
storedObj.defaultMetaTitle ||
map.get('defaultMetaTitle') ||
map.get('home_meta_title') ||
'کنینا ایران | نماینده رسمی مکمل‌های دارویی Canina آلمان',
defaultMetaDescription:
storedObj.defaultMetaDescription ||
map.get('defaultMetaDescription') ||
map.get('home_meta_desc') ||
'فروشگاه تخصصی و نماینده انحصاری مکمل‌های درمانی و تقویتی سگ و گربه کنینا آلمان با فرمولاسیون دارویی و تاییدیه بالینی.',
keywords:
storedObj.keywords ||
map.get('keywords') ||
map.get('home_keywords') ||
'مکمل سگ, مکمل گربه, مکمل دارویی حیوانات, Canina Germany',
canonicalBaseUrl:
storedObj.canonicalBaseUrl ||
map.get('canonicalBaseUrl') ||
'https://canina.ir',
ogImageUrl:
storedObj.ogImageUrl ||
map.get('ogImageUrl') ||
'/assets/images/regenerated_image_1779109861747.png',
};
}
const setting = await this.prisma.setting.findFirst({
where: { category },
});
@ -436,6 +498,47 @@ export class SettingsService implements OnModuleInit {
await this.updateBulkUiTexts(updates);
}
// If seo config is updated, sync individual keys into uiText for unified frontend consumption
if (category === 'seo' && typeof value === 'object' && value !== null) {
const obj = value as Record<string, any>;
const updates: Record<string, string> = {};
if (obj.brandNameFa || obj.brand_name_fa) {
const brand = String(obj.brandNameFa || obj.brand_name_fa);
updates['brand_name_fa'] = brand;
updates['BRAND_LOGO_TEXT_FA'] = brand;
}
if (obj.brandNameEn || obj.brand_name_en) {
const brandEn = String(obj.brandNameEn || obj.brand_name_en);
updates['brand_name_en'] = brandEn;
}
if (obj.titleSeparator || obj.title_separator) {
updates['title_separator'] = String(obj.titleSeparator || obj.title_separator);
}
if (obj.titlePosition || obj.title_position) {
updates['title_position'] = String(obj.titlePosition || obj.title_position);
}
if (obj.defaultMetaTitle) {
updates['defaultMetaTitle'] = String(obj.defaultMetaTitle);
}
if (obj.defaultMetaDescription) {
updates['defaultMetaDescription'] = String(obj.defaultMetaDescription);
}
if (obj.keywords) {
updates['keywords'] = String(obj.keywords);
}
if (obj.canonicalBaseUrl) {
updates['canonicalBaseUrl'] = String(obj.canonicalBaseUrl);
}
if (obj.ogImageUrl) {
updates['ogImageUrl'] = String(obj.ogImageUrl);
}
if (Object.keys(updates).length > 0) {
await this.updateBulkUiTexts(updates);
}
}
return res;
}

View File

@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { Globe, Save } from 'lucide-react';
import { Globe, Save, Sliders, Monitor, Sparkles } from 'lucide-react';
import { toast } from 'react-hot-toast';
import api from '../services/api';
import Spinner from '../components/ui/Spinner';
@ -8,11 +8,15 @@ import type { SeoSettings } from '../types/admin';
export default function SeoSettingsPage() {
const [seo, setSeo] = useState<SeoSettings>({
defaultMetaTitle: 'کنینا ایران | مکمل‌های دارویی آلمانی حیوانات خانگی',
defaultMetaDescription: 'نمایندگی رسمی محصولات کنینا فارما آلمان در ایران',
keywords: 'مکمل سگ, مکمل گربه, کنهیدروکس, کنینا',
brandNameFa: 'کنینا ایران',
brandNameEn: 'Canina Iran',
titleSeparator: '|',
titlePosition: 'suffix',
defaultMetaTitle: 'کنینا ایران | نماینده رسمی مکمل‌های دارویی Canina آلمان',
defaultMetaDescription: 'فروشگاه تخصصی و نماینده انحصاری مکمل‌های درمانی و تقویتی سگ و گربه کنینا آلمان با فرمولاسیون دارویی و تاییدیه بالینی.',
keywords: 'مکمل سگ, مکمل گربه, داروی حیوانات, Canina Germany',
canonicalBaseUrl: 'https://canina.ir',
ogImageUrl: 'https://canina.ir/og-default.jpg',
ogImageUrl: '/assets/images/regenerated_image_1779109861747.png',
});
const [isLoading, setIsLoading] = useState(true);
const [isSaving, setIsSaving] = useState(false);
@ -26,6 +30,10 @@ export default function SeoSettingsPage() {
const data = res.data?.data || res.data;
if (data) {
setSeo(prev => ({
brandNameFa: data.brandNameFa || data.brand_name_fa || prev.brandNameFa,
brandNameEn: data.brandNameEn || data.brand_name_en || prev.brandNameEn,
titleSeparator: data.titleSeparator || data.title_separator || prev.titleSeparator,
titlePosition: data.titlePosition || data.title_position || prev.titlePosition,
defaultMetaTitle: data.defaultMetaTitle || prev.defaultMetaTitle,
defaultMetaDescription: data.defaultMetaDescription || prev.defaultMetaDescription,
keywords: data.keywords || prev.keywords,
@ -50,7 +58,7 @@ export default function SeoSettingsPage() {
try {
setIsSaving(true);
await api.patch('/settings/seo', seo);
toast.success('تنظیمات سئو با موفقیت بروزرسانی شد');
toast.success('تنظیمات سئو و ساختار عناوین با موفقیت ذخیره شد');
} catch (err) {
console.error('Failed to update SEO settings:', err);
toast.error('خطا در بروزرسانی تنظیمات سئو');
@ -59,25 +67,156 @@ export default function SeoSettingsPage() {
}
};
// Sample formatted preview
const previewSamplePageTitle = 'فروشگاه مکمل‌های تخصصی سگ و گربه';
const brandName = seo.brandNameFa || 'کنینا ایران';
const sep = seo.titleSeparator || '|';
const pos = seo.titlePosition || 'suffix';
const previewFormattedTitle =
pos === 'prefix'
? `${brandName} ${sep} ${previewSamplePageTitle}`
: pos === 'none'
? previewSamplePageTitle
: `${previewSamplePageTitle} ${sep} ${brandName}`;
return (
<div className="space-y-6">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<h2 className="text-2xl font-black text-gray-900 flex items-center gap-2">
<Globe className="w-6 h-6 text-purple-600" />
تنظیمات عمومی سئو (SEO Settings)
تنظیمات عمومی سئو و فرمت عناوین صفحات (Global SEO & Titles)
</h2>
<p className="text-gray-500 font-medium mt-1">تنظیم متا دیتاهای پیشفرض، OpenGraph و کلمات کلیدی اصلی فروشگاه</p>
<p className="text-gray-500 font-medium mt-1">
پیکربندی هوشمند و یکپارچه نام برند، ساختار عناوین تب مرورگر، متادیتاهای گوگل و کلمات کلیدی
</p>
</div>
</div>
{/* Live Preview Box */}
<div className="bg-gradient-to-r from-purple-900 via-indigo-900 to-slate-900 text-white p-6 rounded-3xl shadow-lg border border-purple-800/40 relative overflow-hidden">
<div className="relative z-10 space-y-3">
<div className="flex items-center gap-2 text-purple-300 text-xs font-bold uppercase tracking-wider">
<Sparkles className="w-4 h-4 text-amber-400" />
پیشنمایش زنده ساختار عنوان در تب مرورگر و نتایج گوگل
</div>
<div className="bg-white/10 backdrop-blur-md border border-white/20 rounded-2xl p-4 space-y-2" dir="rtl">
<div className="flex items-center gap-2 text-xs text-purple-200">
<span className="w-3 h-3 rounded-full bg-emerald-400 inline-block animate-pulse" />
<span>عنوان نمونه برای صفحه داخلی:</span>
</div>
<p className="text-lg font-black text-white select-all">
{previewFormattedTitle}
</p>
<p className="text-xs text-purple-300/80">
الگوی اعمال شده: {pos === 'suffix' ? 'عنوان صفحه + جداکننده + نام برند (استاندارد توصیه‌شده گوگل)' : pos === 'prefix' ? 'نام برند + جداکننده + عنوان صفحه' : 'فقط عنوان صفحه بدون نام برند'}
</p>
</div>
</div>
</div>
{isLoading ? (
<div className="flex justify-center p-12"><Spinner size="lg" className="text-purple-600" /></div>
) : (
<form onSubmit={handleSave} className="bg-white p-6 rounded-2xl shadow-sm border border-gray-200 space-y-6">
<form onSubmit={handleSave} className="bg-white p-6 sm:p-8 rounded-3xl shadow-sm border border-gray-200 space-y-8">
{/* Section 1: Brand & Title Format Settings */}
<div className="space-y-4">
<div className="flex items-center gap-2 pb-2 border-b border-gray-100">
<Sliders className="w-5 h-5 text-purple-600" />
<h3 className="text-base font-black text-gray-900">
تنظیمات فرمت عنوان صفحات و نام برند (Title & Brand Scheme)
</h3>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">
نام رسمی برند در عنوان صفحات (فارسی) *
</label>
<input
required
type="text"
value={seo.brandNameFa || ''}
onChange={(e) => setSeo({ ...seo, brandNameFa: e.target.value })}
placeholder="مثال: کنینا ایران"
className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none font-bold text-sm"
/>
<span className="text-xs text-gray-400 mt-1 block">نام برند که در انتهای یا ابتدای عنوان تمام صفحات درج میشود</span>
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">
نام رسمی برند (انگلیسی)
</label>
<input
type="text"
value={seo.brandNameEn || ''}
onChange={(e) => setSeo({ ...seo, brandNameEn: e.target.value })}
placeholder="مثال: Canina Iran"
className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none font-bold text-sm"
dir="ltr"
/>
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">
کاراکتر جداکننده عنوان (Separator) *
</label>
<div className="flex gap-2">
{['|', '-', '•', '—', '/'].map((char) => (
<button
key={char}
type="button"
onClick={() => setSeo({ ...seo, titleSeparator: char })}
className={`px-3 py-2 rounded-xl text-sm font-bold border cursor-pointer transition-all ${
seo.titleSeparator === char
? 'bg-purple-600 text-white border-purple-600 shadow-xs'
: 'bg-gray-50 text-gray-700 border-gray-200 hover:bg-gray-100'
}`}
>
{char}
</button>
))}
<input
type="text"
value={seo.titleSeparator || '|'}
onChange={(e) => setSeo({ ...seo, titleSeparator: e.target.value })}
className="w-16 text-center px-2 py-2 rounded-xl border border-gray-200 focus:border-purple-500 outline-none font-bold text-sm"
maxLength={3}
/>
</div>
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">
نحوه قرارگیری نام برند در عنوان (Title Format) *
</label>
<select
value={seo.titlePosition || 'suffix'}
onChange={(e) => setSeo({ ...seo, titlePosition: e.target.value as any })}
className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none font-bold text-sm bg-white cursor-pointer"
>
<option value="suffix">پسوند: عنوان صفحه | نام برند (استاندارد توصیهشده گوگل)</option>
<option value="prefix">پیشوند: نام برند | عنوان صفحه</option>
<option value="none">فقط عنوان صفحه (بدون نام برند)</option>
</select>
</div>
</div>
</div>
{/* Section 2: Default Meta Tags */}
<div className="space-y-4 pt-4 border-t border-gray-100">
<div className="flex items-center gap-2 pb-2">
<Globe className="w-5 h-5 text-purple-600" />
<h3 className="text-base font-black text-gray-900">
متادیتای پیشفرض سئو (Default SEO Meta Tags)
</h3>
</div>
<div>
<label className="block text-sm font-bold text-gray-700 mb-1">عنوان متای پیشفرض (defaultMetaTitle) *</label>
<label className="block text-sm font-bold text-gray-700 mb-1">عنوان متای پیشفرض صفحه اصلی (defaultMetaTitle) *</label>
<input
required
type="text"
@ -85,6 +224,7 @@ export default function SeoSettingsPage() {
onChange={(e) => setSeo({ ...seo, defaultMetaTitle: e.target.value })}
className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none font-bold text-sm"
/>
<span className="text-xs text-gray-400 mt-1 block">در صورت عدم تعیین عنوان اختصاصی برای یک صفحه، از این عنوان استفاده خواهد شد.</span>
</div>
<div>
@ -94,7 +234,7 @@ export default function SeoSettingsPage() {
rows={3}
value={seo.defaultMetaDescription}
onChange={(e) => setSeo({ ...seo, defaultMetaDescription: e.target.value })}
className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm"
className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm leading-relaxed"
/>
</div>
@ -104,7 +244,7 @@ export default function SeoSettingsPage() {
type="text"
value={seo.keywords}
onChange={(e) => setSeo({ ...seo, keywords: e.target.value })}
placeholder="با کاما جدا کنید (مثال: مکمل سگ, مکمل گربه)"
placeholder="با کاما جدا کنید (مثال: مکمل سگ, مکمل گربه, کنینا)"
className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm"
/>
</div>
@ -134,9 +274,9 @@ export default function SeoSettingsPage() {
<button
type="button"
onClick={() => setIsMediaSelectorOpen(true)}
className="px-3 py-3 bg-purple-50 text-purple-600 hover:bg-purple-100 rounded-xl font-bold text-xs shrink-0"
className="px-3 py-3 bg-purple-50 text-purple-600 hover:bg-purple-100 rounded-xl font-bold text-xs shrink-0 cursor-pointer"
>
رسانه
انتخاب از رسانه
</button>
</div>
</div>
@ -147,10 +287,10 @@ export default function SeoSettingsPage() {
<button
type="submit"
disabled={isSaving}
className="bg-purple-600 hover:bg-purple-700 text-white font-bold py-3 px-8 rounded-xl transition-colors flex items-center gap-2 shadow-md shadow-purple-200"
className="bg-purple-600 hover:bg-purple-700 text-white font-bold py-3 px-8 rounded-xl transition-colors flex items-center gap-2 shadow-md shadow-purple-200 cursor-pointer"
>
{isSaving ? <Spinner size="sm" /> : <Save className="w-5 h-5" />}
ذخیره تنظیمات سئو
ذخیره تنظیمات سئو و ساختار عناوین
</button>
</div>
</form>

View File

@ -365,7 +365,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/shop',
icon: ShoppingBag,
sections: [
createSeoSection('shop', 'فروشگاه', 'فروشگاه مکمل‌های تخصصی سگ و گربه | کنینا ایران', 'خرید آنلاین انواع مکمل‌های دارویی مفاصل، پوست و مو، گوارش و ویتامینه سگ و گربه با تضمین اصالت کالا و مشاوره بالینی.'),
createSeoSection('shop', 'فروشگاه', 'فروشگاه تخصصی مکمل‌های دارویی سگ و گربه', 'خرید آنلاین انواع مکمل‌های درمانی مفاصل، پوست و مو، گوارش و ویتامینه سگ و گربه با تضمین اصالت کالا و مشاوره بالینی.'),
{
id: 'shop_header',
title: 'هدر و فیلترهای فروشگاه',
@ -386,7 +386,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/catalog',
icon: BookOpen,
sections: [
createSeoSection('catalog', 'کاتالوگ دیجیتال', 'کاتالوگ آنلاین و تعاملی محصولات دارویی کنینا | Canina Flipbook', 'مشاهده و مطالعه کاتالوگ جامع دارویی و مکمل‌های تغذیه‌ای تخصصی کنینا با قابلیت تورق تعاملی و ثبت پیش‌خرید.'),
createSeoSection('catalog', 'کاتالوگ دیجیتال', 'کاتالوگ رسمی مکمل‌های درمانی و دارویی', 'مشاهده و مطالعه کاتالوگ جامع دارویی و مکمل‌های تغذیه‌ای تخصصی کنینا با قابلیت تورق تعاملی و ثبت پیش‌خرید.'),
{
id: 'catalog_header',
title: 'محتوا و راهنمای کاتالوگ',
@ -405,7 +405,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/blog',
icon: FileText,
sections: [
createSeoSection('blog', 'وبلاگ', 'وبلاگ تخصصی و راهنمای سلامت حیوانات خانگی | کنینا ایران', 'جدیدترین مقالات، راهنماهای تغذیه، بیماری‌های مفاصل و سلامت پت توسط تیم دامپزشکی کنینا.'),
createSeoSection('blog', 'وبلاگ', 'مجله سلامت و دانشنامه مقالات تخصصی پت', 'جدیدترین مقالات، راهنماهای تغذیه، بیماری‌های مفاصل و سلامت پت توسط تیم دامپزشکی کنینا.'),
{
id: 'blog_header',
title: 'هدر و بنر وبلاگ',
@ -424,7 +424,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/wiki',
icon: Pill,
sections: [
createSeoSection('wiki', 'دانشنامه', 'دانشنامه علمی ترکیبات و اصطلاحات دارویی پت | کنینا ایران', 'مرجع تخصصی اثرات بالینی، مواد تشکیل‌دهنده، ویتامین‌ها و ترکیبات گیاهی به کار رفته در محصولات کنینا.'),
createSeoSection('wiki', 'دانشنامه', 'دانشنامه علمی ترکیبات و اصطلاحات دارویی', 'مرجع تخصصی اثرات بالینی، مواد تشکیل‌دهنده، ویتامین‌ها و ترکیبات گیاهی به کار رفته در محصولات کنینا.'),
{
id: 'wiki_header',
title: 'هدر و ناوبری دانشنامه',
@ -443,7 +443,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/about',
icon: Award,
sections: [
createSeoSection('about', 'درباره ما', 'درباره شرکت کنینا | تاریخچه، اصالت و استانداردهای دارویی آلمان', 'آشنایی با تاریخچه شرکت Canina Pharma آلمان، تعهد به کیفیت بیولوژیک و بیش از ۳۵ سال سابقه پیشرو در سلامت حیوانات خانگی.'),
createSeoSection('about', 'درباره ما', 'درباره شرکت کنینا ایران و اصالت آلمانی', 'آشنایی با تاریخچه شرکت Canina Pharma آلمان، تعهد به کیفیت بیولوژیک و بیش از ۴۰ سال سابقه پیشرو در سلامت حیوانات خانگی.'),
{
id: 'about_content',
title: 'متن و داستان برند',
@ -464,7 +464,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/contact',
icon: PhoneCall,
sections: [
createSeoSection('contact', 'تماس با ما', 'تماس با کنینا ایران | مشاوره تخصصی، نشانی و پشتیبانی مشتریان', 'راه‌های ارتباط با دفتر مرکزی کنینا در ایران، ثبت پیام، مشاوره رایگان دارویی و شماره تماس کارشناسان.'),
createSeoSection('contact', 'تماس با ما', 'تماس با ما و مشاوره تخصصی دامپزشکی', 'راه‌های ارتباط با دفتر مرکزی کنینا در ایران، ثبت پیام، مشاوره رایگان دارویی و شماره تماس کارشناسان.'),
{
id: 'contact_info',
title: 'اطلاعات تماس و پیام‌ها',
@ -483,7 +483,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/b2b',
icon: Building2,
sections: [
createSeoSection('b2b', 'همکاران تجاری B2B', 'پرتال فروش عمده و نمایندگی کلینیک‌ها و پت‌شاپ‌ها | کنینا ایران', 'ثبت درخواست عاملیت فروش، خرید عمده با تخفیف سازمانی ویژه دامپزشکان، کلینیک‌ها و پت‌شاپ‌های سراسر کشور.'),
createSeoSection('b2b', 'همکاران تجاری B2B', 'پرتال همکاران تجاری، کلینیک‌ها و پت‌شاپ‌ها', 'ثبت درخواست عاملیت فروش، خرید عمده با تخفیف سازمانی ویژه دامپزشکان، کلینیک‌ها و پت‌شاپ‌های سراسر کشور.'),
{
id: 'b2b_landing',
title: 'صفحه معرفی و شرایط همکاری B2B',
@ -503,7 +503,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/videos',
icon: Video,
sections: [
createSeoSection('videos', 'ویدئوهای آموزشی', 'ویدئوهای آموزشی و تجربیات بالینی مکمل‌های پت | کنینا ایران', 'مشاهده ویدئوهای تخصصی نحوه مصرف مکمل‌ها، نظرات دامپزشکان و راهکارهای درمانی تصویری.'),
createSeoSection('videos', 'ویدئوهای آموزشی', 'ویدئوهای آموزشی و توصیه‌های بالینی دامپزشکان', 'مشاهده ویدئوهای تخصصی نحوه مصرف مکمل‌ها، نظرات دامپزشکان و راهکارهای درمانی تصویری.'),
{
id: 'videos_hub',
title: 'هدر و متن صفحه ویدئوها',
@ -521,7 +521,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/search',
icon: Search,
sections: [
createSeoSection('search', 'جستجو', 'جستجوی هوشمند مکمل‌ها و دانشنامه | کنینا ایران', 'جستجوی سریع محصولات، مکمل‌های تخصصی، بیماری‌ها و علائم بالینی سگ و گربه.'),
createSeoSection('search', 'جستجو', 'جستجوی هوشمند مکمل‌ها و دانشنامه سلامت', 'جستجوی سریع محصولات، مکمل‌های تخصصی، بیماری‌ها و علائم بالینی سگ و گربه.'),
{
id: 'search_content',
title: 'متون راهنما و کادر جستجو',
@ -539,7 +539,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/dashboard',
icon: User,
sections: [
createSeoSection('dashboard', 'داشبورد کاربر', 'داشبورد کاربری و پیگیری سفارشات | کنینا ایران', 'مشاهده تاریخچه خریدها، وضعیت کیف پول، پیگیری آنلاین بسته‌های پستی و مدیریت پت‌ها.'),
createSeoSection('dashboard', 'داشبورد کاربر', 'داشبورد کاربری و پیگیری سفارشات', 'مشاهده تاریخچه خریدها، وضعیت کیف پول، پیگیری آنلاین بسته‌های پستی و مدیریت پت‌ها.'),
{
id: 'dashboard_content',
title: 'عناوین و پیام‌های داشبورد',
@ -557,7 +557,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/profile/pets',
icon: Heart,
sections: [
createSeoSection('profile_pets', 'پرونده سلامت پت', 'پرونده الکترونیک سلامت و یادآور داروهای پت | کنینا ایران', 'مدیریت و ثبت پرونده سلامت سگ و گربه، تنظیم یادآورهای دوز دارو و تاریخچه تغذیه مکمل‌ها.'),
createSeoSection('profile_pets', 'پرونده سلامت پت', 'پرونده الکترونیک سلامت پت', 'مدیریت و ثبت پرونده سلامت سگ و گربه، تنظیم یادآورهای دوز دارو و تاریخچه تغذیه مکمل‌ها.'),
{
id: 'pet_profiles_info',
title: 'عناوین پرونده سلامت و یادآورها',
@ -575,7 +575,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/checkout',
icon: CreditCard,
sections: [
createSeoSection('checkout', 'تسویه‌حساب', کمیل سفارش و درگاه پرداخت امن | کنینا ایران', 'انتخاب آدرس تحویل، انتخاب روش پرداخت و تسویه‌حساب سریع و امن سفارشات کنینا.'),
createSeoSection('checkout', 'تسویه‌حساب', سویه‌حساب و پرداخت امن', 'انتخاب آدرس تحویل، انتخاب روش پرداخت و تسویه‌حساب سریع و امن سفارشات کنینا.'),
{
id: 'checkout_content',
title: 'پیام‌ها و راهنمای تسویه',
@ -594,7 +594,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/track',
icon: Truck,
sections: [
createSeoSection('track', 'پیگیری سفارش', 'رهگیری آنلاین مرسوله پستی | کنینا ایران', 'پیگیری وضعیت بسته‌بندی، کد رهگیری پست پیشتاز و تاریخچه تحویل سفارشات.'),
createSeoSection('track', 'پیگیری سفارش', 'پیگیری آنلاین سفارش و مرسوله پستی', 'پیگیری وضعیت بسته‌بندی، کد رهگیری پست پیشتاز و تاریخچه تحویل سفارشات.'),
{
id: 'track_content',
title: 'متن و کادر رهگیری',
@ -612,7 +612,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/trust-seals',
icon: Shield,
sections: [
createSeoSection('trust_seals', 'مجوزها و اصالت', 'مجوزهای بهداشتی، گواهی اصالت و اینماد | کنینا ایران', 'بررسی تاییدیه‌های سازمان دامپزشکی، گواهی استاندارد آلمان و اینماد رسمی کنینا.'),
createSeoSection('trust_seals', 'مجوزها و اصالت', 'مجوزهای رسمی، گواهی اصالت و اینماد', 'بررسی تاییدیه‌های سازمان دامپزشکی، گواهی استاندارد آلمان و اینماد رسمی کنینا.'),
{
id: 'trust_seals_content',
title: 'متن مجوزها و گواهی‌ها',
@ -630,7 +630,7 @@ const SITE_PAGES: AppSitePage[] = [
path: '/privacy',
icon: ShieldCheck,
sections: [
createSeoSection('privacy', 'حریم خصوصی', 'سیاست حفظ حریم خصوصی و شرایط استفاده | کنینا ایران', 'قوانین خرید، عودت کالا، حفاظت از اطلاعات کاربران و شرایط استفاده از خدمات کنینا.'),
createSeoSection('privacy', 'حریم خصوصی', 'حریم خصوصی و شرایط استفاده', 'قوانین خرید، عودت کالا، حفاظت از اطلاعات کاربران و شرایط استفاده از خدمات کنینا.'),
{
id: 'privacy_content',
title: 'متن قوانین و مقررات',
@ -1097,17 +1097,35 @@ export default function UITexts() {
const seoDescKey = `${page.id}_meta_desc`;
const seoCanonicalKey = `${page.id}_canonical`;
const pageMetaTitle =
const rawPageMetaTitle =
edits[seoTitleKey] ??
texts[seoTitleKey] ??
page.sections.find((s) => s.id === 'seo')?.fields[0]?.defaultVal ??
`${page.title} | کنینا ایران`;
page.title;
const brandName = texts['brand_name_fa'] || 'کنینا ایران';
const titleSep = texts['title_separator'] || '|';
const titlePos = texts['title_position'] || 'suffix';
const formatPreviewTitle = (raw: string, isHome = false): string => {
if (!raw || !raw.trim()) return brandName;
const clean = raw.trim();
if (isHome) return clean;
const escapedBrand = brandName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const brandRegex = new RegExp(`(\\s*[-|•]\\s*${escapedBrand}|${escapedBrand}\\s*[-|•]\\s*)`, 'gi');
const baseTitle = clean.replace(brandRegex, '').trim();
if (titlePos === 'none') return baseTitle || clean;
if (titlePos === 'prefix') return `${brandName} ${titleSep} ${baseTitle || clean}`;
return `${baseTitle || clean} ${titleSep} ${brandName}`;
};
const pageMetaTitle = formatPreviewTitle(rawPageMetaTitle, page.id === 'home');
const pageMetaDesc =
edits[seoDescKey] ??
texts[seoDescKey] ??
page.sections.find((s) => s.id === 'seo')?.fields[1]?.defaultVal ??
`مشاهده و بررسی آنلاین ${page.title} در وب‌سایت رسمی کنینا ایران با ضمانت اصالت کالا و مشاوره دارویی.`;
`مشاهده و بررسی آنلاین ${page.title} در وب‌سایت رسمی ${brandName} با ضمانت اصالت کالا و مشاوره دارویی.`;
const pageCanonical =
edits[seoCanonicalKey] ??

View File

@ -176,6 +176,10 @@ export interface PartnerAccount {
}
export interface SeoSettings {
brandNameFa?: string;
brandNameEn?: string;
titleSeparator?: string;
titlePosition?: 'suffix' | 'prefix' | 'none';
defaultMetaTitle: string;
defaultMetaDescription: string;
keywords: string;

View File

@ -1,21 +1,14 @@
import type { Metadata } from "next";
import { ShieldCheck, History, Award, CheckCircle } from "lucide-react";
import { getPageMetadata } from "../../lib/seo";
export const metadata: Metadata = {
title: "درباره ما - میراث ۴۰ ساله کانیتا آلمان در ایران",
description: "داستان کنینا ایران و میراث ۴۰ ساله شرکت Canina pharma GmbH آلمان در تولید مکمل‌های فوق‌تخصصی دامپزشکی با گرید دارویی اختصاصی.",
alternates: {
canonical: "/about",
},
openGraph: {
title: "درباره ما - کنینا ایران",
description: "داستان کنینا ایران و میراث ۴۰ ساله شرکت Canina pharma GmbH آلمان در تولید مکمل‌های فوق‌تخصصی دامپزشکی.",
url: "https://canina.ir/about",
siteName: "کنینا ایران",
locale: "fa_IR",
type: "website",
}
};
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('about', {
canonicalPath: '/about',
fallbackTitle: 'درباره شرکت کنینا ایران و اصالت آلمانی',
fallbackDesc: 'داستان کنینا ایران و بیش از ۴۰ سال سابقه درخشان شرکت Canina pharma GmbH آلمان در تولید مکمل‌های فوق‌تخصصی دامپزشکی.',
});
}
export default function AboutPage() {
return (

View File

@ -29,26 +29,35 @@ async function getBlog(slug: string) {
export const revalidate = 60; // ensure SSR dynamic revalidation works correctly
import { getSeoConfig, formatPageTitle } from "../../../lib/seo";
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const { slug } = await params;
const blog = await getBlog(slug);
if (!blog) return { title: 'مقاله یافت نشد | کنینا ایران' };
const config = await getSeoConfig();
if (!blog) {
return {
title: formatPageTitle('مقاله یافت نشد', config.brandNameFa, config.titleSeparator, config.titlePosition),
};
}
const title = `${blog.title} | مجله سلامت کنینا`;
const description = blog.excerpt.substring(0, 150).trim();
const title = formatPageTitle(blog.title, config.brandNameFa, config.titleSeparator, config.titlePosition, false);
const description = blog.excerpt.substring(0, 155).trim() || config.defaultMetaDescription;
const canonicalUrl = `${config.canonicalBaseUrl.replace(/\/$/, '')}/blog/${slug}`;
return {
title,
description,
alternates: {
canonical: `/blog/${slug}`,
canonical: canonicalUrl,
},
openGraph: {
title,
description,
images: [blog.image],
url: `/blog/${slug}`,
siteName: "کنینا ایران",
images: [blog.image || config.ogImageUrl],
url: canonicalUrl,
siteName: config.brandNameFa,
type: "article",
}
};

View File

@ -1,21 +1,14 @@
import BlogPage from "../../components/BlogPage";
import type { Metadata } from 'next';
import { getPageMetadata } from "../../lib/seo";
export const metadata: Metadata = {
title: "مجله سلامت حیوانات خانگی و مقالات علمی - کنینا ایران",
description: "آخرین مقالات، آموزش‌های دامپزشکی و اخبار علمی روز دنیا در زمینه نگهداری، تغذیه تخصصی و سلامت سگ و گربه.",
alternates: {
canonical: "/blog",
},
openGraph: {
title: "مجله سلامت حیوانات خانگی - کنینا ایران",
description: "آخرین مقالات، آموزش‌ها و اخبار علمی در زمینه نگهداری، تغذیه و سلامت سگ و گربه.",
url: "https://canina.ir/blog",
siteName: "کنینا ایران",
locale: "fa_IR",
type: "website",
}
};
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('blog', {
canonicalPath: '/blog',
fallbackTitle: 'مجله سلامت و دانشنامه مقالات تخصصی پت',
fallbackDesc: 'جدیدترین مقالات علمی، راهنمای تغذیه، بیماری‌های سگ و گربه و توصیه‌های بالینی متخصصان و دامپزشکان کنینا.',
});
}
async function getBlogs() {
const rawApiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:4001/api';

View File

@ -1,21 +1,14 @@
import type { Metadata } from "next";
import CatalogClient from "./CatalogClient";
import { getPageMetadata } from "../../lib/seo";
export const metadata: Metadata = {
title: "کاتالوگ محصولات Canina - راهنمای مکمل‌های درمانی سگ و گربه",
description: "کاتالوگ جامع محصولات آلمانی کنینا شامل راهنمای ترکیبات، گرید دارویی، دوز مصرفی و موارد درمان بالینی مفصل، پوست و مو و رشد پت‌ها.",
alternates: {
canonical: "/catalog",
},
openGraph: {
title: "کاتالوگ رسمی مکمل‌های درمانی Canina Pharma Germany",
description: "دانلود و مشاهده آنلاین کاتالوگ مکمل‌های دارویی حیوانات خانگی کنینا آلمان",
url: "https://canina.ir/catalog",
siteName: "کنینا ایران",
locale: "fa_IR",
type: "website",
}
};
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('catalog', {
canonicalPath: '/catalog',
fallbackTitle: 'کاتالوگ رسمی مکمل‌های درمانی و دارویی',
fallbackDesc: 'مشاهده و مطالعه کاتالوگ جامع دارویی و مکمل‌های تغذیه‌ای تخصصی کنینا آلمان با قابلیت ورق‌زدن تعاملی و ثبت پیش‌خرید.',
});
}
export default function CatalogPage() {
return <CatalogClient />;

View File

@ -1,5 +1,17 @@
import type { Metadata } from 'next';
import CheckoutPage from "../../components/CheckoutPage";
import { getPageMetadata } from "../../lib/seo";
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('checkout', {
canonicalPath: '/checkout',
fallbackTitle: 'تسویه‌حساب و پرداخت امن',
fallbackDesc: 'انتخاب آدرس تحویل، روش ارسال و تسویه‌حساب سریع و امن سفارشات مکمل‌های کنینا.',
noIndex: true,
});
}
export default function Checkout() {
return <CheckoutPage />;
}

View File

@ -1,21 +1,14 @@
import type { Metadata } from "next";
import ContactFormClient from "@/components/ContactFormClient";
import { getPageMetadata } from "@/lib/seo";
export const metadata: Metadata = {
title: "تماس با ما - نمایندگی رسمی مکمل‌های کنینا آلمان در ایران",
description: "راه‌های ارتباطی، تلفن تماس ویژه، آدرس دفتر مرکزی و فرم تماس مستقیم با نمایندگی انحصاری کانیتا آلمان در ایران.",
alternates: {
canonical: "/contact",
},
openGraph: {
title: "تماس با ما - کنینا ایران",
description: "راه‌های ارتباطی با نمایندگی رسمی مکمل‌های دامپزشکی کنینا در ایران.",
url: "https://canina.ir/contact",
siteName: "کنینا ایران",
locale: "fa_IR",
type: "website",
}
};
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('contact', {
canonicalPath: '/contact',
fallbackTitle: 'تماس با ما و مشاوره تخصصی دامپزشکی',
fallbackDesc: 'راه‌های ارتباطی با دفتر مرکزی کنینا ایران، مشاوره تلفنی رایگان، پشتیبانی آنلاین و نشانی.',
});
}
export default function ContactPage() {
return (

View File

@ -1,5 +1,17 @@
import type { Metadata } from 'next';
import UserDashboard from "../../components/UserDashboard";
import { getPageMetadata } from "../../lib/seo";
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('dashboard', {
canonicalPath: '/dashboard',
fallbackTitle: 'داشبورد کاربری و پیگیری سفارشات',
fallbackDesc: 'مشاهده تاریخچه خریدها، وضعیت کیف پول، پیگیری آنلاین بسته‌های پستی و مدیریت پت‌ها.',
noIndex: true,
});
}
export default function DashboardPage() {
return <UserDashboard />;
}

View File

@ -1,40 +1,25 @@
import type { Metadata } from 'next';
import './globals.css';
import ClientLayout from './ClientLayout';
import { getSeoConfig } from '../lib/seo';
export async function generateMetadata(): Promise<Metadata> {
let seoSettings: {
defaultMetaTitle?: string;
defaultMetaDescription?: string;
keywords?: string | string[];
canonicalBaseUrl?: string;
ogImageUrl?: string;
} | null = null;
const config = await getSeoConfig();
const siteUrl = config.canonicalBaseUrl || process.env.NEXT_PUBLIC_SITE_URL || 'https://canina.ir';
const titleDefault = config.defaultMetaTitle;
const descriptionDefault = config.defaultMetaDescription;
const keywordsList = config.keywords;
const ogImg = config.ogImageUrl;
const brandName = config.brandNameFa;
const separator = config.titleSeparator;
const position = config.titlePosition;
try {
const apiBase = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4001/api';
const res = await fetch(`${apiBase}/settings/seo`, { next: { revalidate: 60 } });
if (res.ok) {
seoSettings = await res.json();
}
} catch (err) {
console.error('[SEO] Error fetching dynamic SEO settings:', err);
}
const siteUrl = seoSettings?.canonicalBaseUrl || process.env.NEXT_PUBLIC_SITE_URL || 'https://canina.ir';
const titleDefault = seoSettings?.defaultMetaTitle || 'کنینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی';
const descriptionDefault = seoSettings?.defaultMetaDescription || 'تدارک هوشمندانه سلامت برای همراهان وفادار شما. کنینا ایران واردکننده انحصاری مکمل‌های درمانی با گرید دارویی اختصاصی از آلمان.';
let keywordsList: string[] = ['مکمل حیوانات خانگی', 'مکمل سگ', 'مکمل گربه', 'کنینا', 'داروی حیوانات', 'Canina Iran'];
if (seoSettings?.keywords) {
if (Array.isArray(seoSettings.keywords)) {
keywordsList = seoSettings.keywords;
} else if (typeof seoSettings.keywords === 'string') {
keywordsList = seoSettings.keywords.split(',').map(k => k.trim());
}
}
const ogImg = seoSettings?.ogImageUrl || '/assets/images/regenerated_image_1779109861747.png';
const templateStr =
position === 'prefix'
? `${brandName} ${separator} %s`
: position === 'none'
? '%s'
: `%s ${separator} ${brandName}`;
const isStaging =
process.env.NEXT_PUBLIC_APP_ENV === 'staging' ||
@ -48,14 +33,14 @@ export async function generateMetadata(): Promise<Metadata> {
return {
metadataBase: new URL(siteUrl),
title: {
template: '%s | کنینا ایران',
template: templateStr,
default: titleDefault,
},
description: descriptionDefault,
keywords: keywordsList,
authors: [{ name: 'Canina Iran' }],
creator: 'Canina Iran',
publisher: 'Canina Iran',
authors: [{ name: brandName }],
creator: brandName,
publisher: brandName,
robots: isStaging ? {
index: false,
follow: false,
@ -88,7 +73,7 @@ export async function generateMetadata(): Promise<Metadata> {
title: titleDefault,
description: descriptionDefault,
url: siteUrl,
siteName: 'کنینا ایران',
siteName: brandName,
locale: 'fa_IR',
type: 'website',
images: [

View File

@ -1,35 +1,12 @@
import type { Metadata } from "next";
import HomeClient from "./HomeClient";
import { getPageMetadata } from "../lib/seo";
export const metadata: Metadata = {
title: "کنینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی",
description: "تدارک هوشمندانه سلامت برای همراهان وفادار شما. کنینا ایران واردکننده انحصاری مکمل‌های درمانی با گرید دارویی اختصاصی از آلمان با پروانه رسمی دامپزشکی.",
alternates: {
canonical: "/",
},
openGraph: {
title: "کنینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی",
description: "واردکننده انحصاری مکمل‌های درمانی با گرید دارویی اختصاصی از آلمان",
url: "https://canina.ir",
siteName: "کنینا ایران",
locale: "fa_IR",
type: "website",
images: [
{
url: "/assets/images/regenerated_image_1779109861747.png",
width: 1200,
height: 630,
alt: "کنینا ایران - مکمل‌های درمانی حیوانات خانگی",
}
]
},
twitter: {
card: "summary_large_image",
title: "کنینا ایران - مکمل‌های درمانی حیوانات خانگی با گرید دارویی اختصاصی",
description: "تدارک هوشمندانه سلامت برای همراهان وفادار شما.",
images: ["/assets/images/regenerated_image_1779109861747.png"],
}
};
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('home', {
canonicalPath: '/',
});
}
import { HomeApiResponse } from "../lib/types";

View File

@ -1,21 +1,14 @@
import type { Metadata } from "next";
import { Shield } from "lucide-react";
import { getPageMetadata } from "../../lib/seo";
export const metadata: Metadata = {
title: "حریم خصوصی کاربران - کنینا ایران",
description: "سیاست‌های حفاظت از اطلاعات شخصی کاربران، امنیت داده‌های پزشکی و پرونده‌های درمانی پت در وب‌سایت کنینا ایران.",
alternates: {
canonical: "/privacy",
},
openGraph: {
title: "حریم خصوصی کاربران - کنینا ایران",
description: "سیاست‌های حفاظت از اطلاعات شخصی کاربران در وب‌سایت کنینا ایران.",
url: "https://canina.ir/privacy",
siteName: "کنینا ایران",
locale: "fa_IR",
type: "website",
}
};
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('privacy', {
canonicalPath: '/privacy',
fallbackTitle: 'حریم خصوصی و شرایط استفاده',
fallbackDesc: 'سیاست‌های حفاظت از اطلاعات شخصی کاربران، امنیت داده‌های پزشکی و قوانین استفاده از خدمات کنینا ایران.',
});
}
export default function PrivacyPage() {
return (

View File

@ -1,5 +1,17 @@
import type { Metadata } from 'next';
import PetProfile from "../../components/PetProfile";
import { getPageMetadata } from "../../lib/seo";
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('profile_pets', {
canonicalPath: '/profile',
fallbackTitle: 'پرونده الکترونیک سلامت پت',
fallbackDesc: 'مدیریت و ثبت پرونده سلامت سگ و گربه، تنظیم یادآورهای دوز دارو و تاریخچه تغذیه مکمل‌ها.',
noIndex: true,
});
}
export default function ProfilePage() {
return <PetProfile />;
}

View File

@ -1,10 +1,14 @@
import SearchResultsPage from "../../components/SearchResultsPage";
import SearchResultsPage from "../../components/SearchResultsPage";
import type { Metadata } from 'next';
import { getPageMetadata } from "../../lib/seo";
export const metadata: Metadata = {
title: 'جستجوی محصولات',
description: 'جستجوی مکمل‌های درمانی حیوانات خانگی، داروها و مقالات سلامت در کنینا ایران.',
};
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('search', {
canonicalPath: '/search',
fallbackTitle: 'جستجوی هوشمند مکمل‌ها و دانشنامه سلامت',
fallbackDesc: 'جستجوی سریع در کاتالوگ محصولات، بیماری‌ها، علائم بالینی و مقالات سلامت کنینا ایران.',
});
}
export default async function SearchPage({ searchParams }: { searchParams: Promise<{ q?: string }> }) {
const params = await searchParams;

View File

@ -2,25 +2,29 @@ import ProductPage from "../../../components/ProductPage";
import { productService } from "../../../lib/services/productService";
import type { Metadata } from 'next';
import { Suspense } from 'react';
import { getSeoConfig, formatPageTitle } from "../../../lib/seo";
export async function generateMetadata(
{ params }: { params: Promise<{ slug: string }> }
): Promise<Metadata> {
const resolvedParams = await params;
const product = await productService.getProductBySlug(resolvedParams.slug);
const config = await getSeoConfig();
if (!product) {
return {
title: 'خرید مکمل درمانی پت | کنینا ایران',
title: formatPageTitle('مکمل درمانی یافت نشد', config.brandNameFa, config.titleSeparator, config.titlePosition),
};
}
const nameFa = product.nameFa || product.name;
const nameEn = product.nameEn || "";
const title = nameFa.includes("Canina") ? nameFa : `${nameFa} (${nameEn}) | برند کنینا آلمان`;
const description = (product.shortDescription || product.description || '').substring(0, 150).trim();
const nameEn = product.nameEn ? ` (${product.nameEn})` : '';
const rawTitle = `${nameFa}${nameEn}`;
const title = formatPageTitle(rawTitle, config.brandNameFa, config.titleSeparator, config.titlePosition, false);
const description = (product.shortDescription || product.description || '').substring(0, 155).trim() || config.defaultMetaDescription;
const rawKeywords = (product as unknown as Record<string, unknown>).keywords;
const keywords = typeof rawKeywords === 'string' ? rawKeywords.split(',') : product.main_ingredients;
const keywords = typeof rawKeywords === 'string' ? rawKeywords.split(',').map(k => k.trim()) : (product.main_ingredients || config.keywords);
const canonicalUrl = `${config.canonicalBaseUrl.replace(/\/$/, '')}/shop/${resolvedParams.slug}`;
return {
title,
@ -29,13 +33,13 @@ export async function generateMetadata(
openGraph: {
title,
description,
images: [product.image],
url: `/shop/${resolvedParams.slug}`,
siteName: "کنینا ایران",
images: [product.image || config.ogImageUrl],
url: canonicalUrl,
siteName: config.brandNameFa,
type: "website",
},
alternates: {
canonical: `/shop/${resolvedParams.slug}`,
canonical: canonicalUrl,
}
};
}

View File

@ -1,21 +1,14 @@
import type { Metadata } from "next";
import ArchivePage from "../../components/ArchivePage";
import { getPageMetadata } from "../../lib/seo";
export const metadata: Metadata = {
title: "فروشگاه تخصصی مکمل‌های دارویی - کنینا ایران",
description: "خرید آنلاین انواع مکمل‌های درمانی، مفاصل و استخوان، گوارش و ایمنی، ویتامین‌ها و مراقبت‌های ویژه سگ و گربه با تضمین اصالت و گواهی سلامت دامپزشکی.",
alternates: {
canonical: "/shop",
},
openGraph: {
title: "فروشگاه تخصصی مکمل‌های دارویی - کنینا ایران",
description: "خرید آنلاین انواع مکمل‌های درمانی، مفاصل و استخوان، گوارش و ایمنی، ویتامین‌ها و مراقبت‌های ویژه سگ و گربه.",
url: "https://canina.ir/shop",
siteName: "کنینا ایران",
locale: "fa_IR",
type: "website",
}
};
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('shop', {
canonicalPath: '/shop',
fallbackTitle: 'فروشگاه تخصصی مکمل‌های دارویی سگ و گربه',
fallbackDesc: 'خرید آنلاین انواع مکمل‌های درمانی مفاصل، پوست و مو، گوارش و ایمنی، رشد و ویتامینه سگ و گربه با ضمانت اصالت کنینا آلمان.',
});
}
export default async function Shop({ searchParams }: { searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) {
const resolvedParams = await searchParams;

View File

@ -1,5 +1,16 @@
import type { Metadata } from 'next';
import OrderTracking from "../../components/OrderTracking";
import { getPageMetadata } from "../../lib/seo";
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('track', {
canonicalPath: '/track',
fallbackTitle: 'پیگیری آنلاین سفارش و مرسوله پستی',
fallbackDesc: 'پیگیری وضعیت لحظه‌ای پردازش، بسته‌بندی و کد رهگیری پست پیشتاز سفارشات کنینا ایران.',
});
}
export default function TrackPage() {
return <OrderTracking />;
}

View File

@ -1,12 +1,16 @@
import React from 'react';
import { Metadata } from 'next';
import type { Metadata } from 'next';
import EnamadBadge from '@/components/EnamadBadge';
import { ShieldCheck, Award, FileCheck, CheckCircle2, Building2 } from 'lucide-react';
import { getPageMetadata } from '@/lib/seo';
export const metadata: Metadata = {
title: 'نمادهای اعتماد و مجوزهای رسمی | کنینا ایران',
description: 'مشاهده نماد اعتماد الکترونیکی (اینماد)، مجوزهای سازمان دامپزشکی کشور و گواهینامه‌های اصالت کالاهای کنینا ایران.',
};
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('trust_seals', {
canonicalPath: '/trust-seals',
fallbackTitle: 'مجوزهای رسمی، گواهی اصالت و اینماد',
fallbackDesc: 'بررسی تاییدیه‌های رسمی سازمان دامپزشکی کشور، گواهی استاندارد آلمان و اینماد رسمی کنینا ایران.',
});
}
export default function TrustSealsPage() {
const licenses = [

View File

@ -1,5 +1,16 @@
import type { Metadata } from 'next';
import VideosPage from "../../components/VideosPage";
import { getPageMetadata } from "../../lib/seo";
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('videos', {
canonicalPath: '/videos',
fallbackTitle: 'ویدئوهای آموزشی و توصیه‌های بالینی دامپزشکان',
fallbackDesc: 'مشاهده ویدئوهای تخصصی نحوه مصرف مکمل‌های درمانی، نظرات دامپزشکان و راهکارهای بالینی تصویری.',
});
}
export default function Videos() {
return <VideosPage />;
}

View File

@ -15,14 +15,37 @@ async function getWikiTerm(key: string) {
}
}
import { getSeoConfig, formatPageTitle } from "../../../lib/seo";
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const { slug } = await params;
const term = await getWikiTerm(slug);
if (!term) return { title: 'ترکیب علمی یافت نشد' };
const config = await getSeoConfig();
if (!term) {
return {
title: formatPageTitle('ترکیب علمی یافت نشد', config.brandNameFa, config.titleSeparator, config.titlePosition),
};
}
const rawTitle = term.metaTitle || `ترکیب علمی ${term.term}`;
const title = formatPageTitle(rawTitle, config.brandNameFa, config.titleSeparator, config.titlePosition, false);
const description = term.metaDescription || term.definition?.replace(/<[^>]+>/g, '').substring(0, 155).trim() || config.defaultMetaDescription;
const canonicalUrl = `${config.canonicalBaseUrl.replace(/\/$/, '')}/wiki/${slug}`;
return {
title: term.metaTitle || term.term,
description: term.metaDescription || term.definition?.substring(0, 160),
title,
description,
alternates: {
canonical: canonicalUrl,
},
openGraph: {
title,
description,
url: canonicalUrl,
siteName: config.brandNameFa,
type: "article",
}
};
}

View File

@ -1,22 +1,15 @@
import { Suspense } from "react";
import IngredientWiki from "../../components/IngredientWiki";
import type { Metadata } from 'next';
import { getPageMetadata } from "../../lib/seo";
export const metadata: Metadata = {
title: "دانشنامه ترکیبات دارویی و علمی - کنینا ایران",
description: "اطلاعات کامل پزشکی در مورد مواد موثره، ویتامین‌ها و ترکیبات گیاهی نایاب به کار رفته در محصولات تخصصی کانیتا آلمان.",
alternates: {
canonical: "/wiki",
},
openGraph: {
title: "دانشنامه ترکیبات دارویی و علمی - کنینا ایران",
description: "اطلاعات کامل در مورد مواد موثره، ویتامین‌ها و ترکیبات گیاهی به کار رفته در محصولات تخصصی کنینا ایران.",
url: "https://canina.ir/wiki",
siteName: "کنینا ایران",
locale: "fa_IR",
type: "website",
}
};
export async function generateMetadata(): Promise<Metadata> {
return getPageMetadata('wiki', {
canonicalPath: '/wiki',
fallbackTitle: 'دانشنامه علمی ترکیبات و اصطلاحات دارویی',
fallbackDesc: 'مرجع تخصصی اثرات بالینی، مواد تشکیل‌دهنده، ویتامین‌ها و ترکیبات ارگانیک به کار رفته در مکمل‌های دارویی کنینا آلمان.',
});
}
export default function WikiPage() {
return (

View File

@ -0,0 +1,267 @@
import type { Metadata } from 'next';
export interface SeoConfig {
brandNameFa: string;
brandNameEn: string;
titleSeparator: string;
titlePosition: 'suffix' | 'prefix' | 'none';
defaultMetaTitle: string;
defaultMetaDescription: string;
keywords: string[];
canonicalBaseUrl: string;
ogImageUrl: string;
texts: Record<string, string>;
}
const DEFAULT_SEO_CONFIG: SeoConfig = {
brandNameFa: 'کنینا ایران',
brandNameEn: 'Canina Iran',
titleSeparator: '|',
titlePosition: 'suffix',
defaultMetaTitle: 'کنینا ایران | نماینده رسمی مکمل‌های دارویی Canina آلمان',
defaultMetaDescription: 'فروشگاه تخصصی و نماینده انحصاری مکمل‌های درمانی و تقویتی سگ و گربه کنینا آلمان با فرمولاسیون دارویی و تاییدیه بالینی.',
keywords: ['کنینا', 'مکمل حیوانات خانگی', 'مکمل سگ', 'مکمل گربه', 'داروی حیوانات', 'Canina Iran'],
canonicalBaseUrl: 'https://canina.ir',
ogImageUrl: '/assets/images/regenerated_image_1779109861747.png',
texts: {},
};
/**
* Fetch dynamic SEO settings and UI texts on server with 60-second revalidation
*/
export async function getSeoConfig(): Promise<SeoConfig> {
const apiBase =
process.env.INTERNAL_API_URL ||
process.env.NEXT_PUBLIC_API_URL ||
'http://localhost:4001/api';
try {
const res = await fetch(`${apiBase}/settings/ui-texts`, {
next: { revalidate: 60, tags: ['ui-texts', 'settings'] },
});
if (res.ok) {
const data = await res.json();
const texts: Record<string, string> = {};
if (Array.isArray(data)) {
data.forEach((item: { key: string; value: string }) => {
if (item?.key) texts[item.key] = item.value;
});
} else if (typeof data === 'object' && data !== null) {
Object.entries(data).forEach(([k, v]) => {
texts[k] = String(v);
});
}
const brandNameFa =
texts['brand_name_fa'] ||
texts['BRAND_LOGO_TEXT_FA'] ||
texts['site_logo_text_fa'] ||
DEFAULT_SEO_CONFIG.brandNameFa;
const brandNameEn =
texts['brand_name_en'] ||
texts['BRAND_LOGO_TEXT_EN'] ||
DEFAULT_SEO_CONFIG.brandNameEn;
const titleSeparator =
texts['title_separator'] || DEFAULT_SEO_CONFIG.titleSeparator;
const titlePositionRaw = texts['title_position']?.toLowerCase();
const titlePosition: 'suffix' | 'prefix' | 'none' =
titlePositionRaw === 'prefix' || titlePositionRaw === 'none'
? titlePositionRaw
: 'suffix';
const defaultMetaTitle =
texts['defaultMetaTitle'] ||
texts['home_meta_title'] ||
DEFAULT_SEO_CONFIG.defaultMetaTitle;
const defaultMetaDescription =
texts['defaultMetaDescription'] ||
texts['home_meta_desc'] ||
DEFAULT_SEO_CONFIG.defaultMetaDescription;
const rawKeywords = texts['keywords'] || texts['home_keywords'];
const keywords = rawKeywords
? rawKeywords.split(',').map((k) => k.trim()).filter(Boolean)
: DEFAULT_SEO_CONFIG.keywords;
const canonicalBaseUrl =
texts['canonicalBaseUrl'] ||
process.env.NEXT_PUBLIC_SITE_URL ||
DEFAULT_SEO_CONFIG.canonicalBaseUrl;
const ogImageUrl =
texts['ogImageUrl'] || DEFAULT_SEO_CONFIG.ogImageUrl;
return {
brandNameFa,
brandNameEn,
titleSeparator,
titlePosition,
defaultMetaTitle,
defaultMetaDescription,
keywords,
canonicalBaseUrl,
ogImageUrl,
texts,
};
}
} catch (error) {
console.warn('[SEO] Unable to fetch dynamic settings from backend, using fallback:', error);
}
return DEFAULT_SEO_CONFIG;
}
/**
* Uniform Page Title Formatter according to Store Brand Configuration
*/
export function formatPageTitle(
rawTitle: string,
brandName = 'کنینا ایران',
separator = '|',
position: 'suffix' | 'prefix' | 'none' = 'suffix',
isHomePage = false
): string {
if (!rawTitle || !rawTitle.trim()) {
return brandName;
}
const clean = rawTitle.trim();
// If this is the home page and has a custom full title configured, return it directly
if (isHomePage) {
return clean;
}
// Check if the title already explicitly contains the brand name
const escapedBrand = brandName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const brandRegex = new RegExp(`(\\s*[-|•]\\s*${escapedBrand}|${escapedBrand}\\s*[-|•]\\s*)`, 'gi');
// Strip existing repeated brand affix if present so we can format cleanly
const baseTitle = clean.replace(brandRegex, '').trim();
if (position === 'none') {
return baseTitle || clean;
}
if (position === 'prefix') {
return `${brandName} ${separator} ${baseTitle || clean}`;
}
// Default: 'suffix' -> "{PageTitle} {separator} {BrandName}"
return `${baseTitle || clean} ${separator} ${brandName}`;
}
export interface PageMetadataOptions {
fallbackTitle?: string;
fallbackDesc?: string;
fallbackKeywords?: string[];
canonicalPath?: string;
ogImage?: string;
isDynamicEntity?: boolean; // For PDP / Blog / Wiki detail
noIndex?: boolean;
}
/**
* Generate standard Next.js Metadata object dynamically from server settings
*/
export async function getPageMetadata(
pageKey: string,
options: PageMetadataOptions = {}
): Promise<Metadata> {
const config = await getSeoConfig();
const { texts, brandNameFa, titleSeparator, titlePosition, canonicalBaseUrl, ogImageUrl } = config;
const isHome = pageKey === 'home';
const rawTitle =
texts[`${pageKey}_meta_title`] ||
options.fallbackTitle ||
config.defaultMetaTitle;
const title = options.isDynamicEntity
? formatPageTitle(rawTitle, brandNameFa, titleSeparator, titlePosition, false)
: formatPageTitle(rawTitle, brandNameFa, titleSeparator, titlePosition, isHome);
const description =
texts[`${pageKey}_meta_desc`] ||
options.fallbackDesc ||
config.defaultMetaDescription;
const rawKeywords = texts[`${pageKey}_keywords`];
const keywords = rawKeywords
? rawKeywords.split(',').map((k) => k.trim()).filter(Boolean)
: options.fallbackKeywords || config.keywords;
const explicitCanonical = texts[`${pageKey}_canonical`];
const relativePath = options.canonicalPath ?? (isHome ? '/' : `/${pageKey.replace(/_/g, '-')}`);
const canonicalUrl = explicitCanonical || `${canonicalBaseUrl.replace(/\/$/, '')}${relativePath}`;
const image = options.ogImage || ogImageUrl;
const isStaging =
options.noIndex ||
process.env.NEXT_PUBLIC_APP_ENV === 'staging' ||
process.env.NODE_ENV !== 'production' ||
(typeof process.env.NEXT_PUBLIC_SITE_URL === 'string' &&
(process.env.NEXT_PUBLIC_SITE_URL.includes('stage') ||
process.env.NEXT_PUBLIC_SITE_URL.includes('test') ||
process.env.NEXT_PUBLIC_SITE_URL.includes('dev')));
return {
title,
description,
keywords,
alternates: {
canonical: canonicalUrl,
},
openGraph: {
title,
description,
url: canonicalUrl,
siteName: brandNameFa,
locale: 'fa_IR',
type: 'website',
images: [
{
url: image,
width: 1200,
height: 630,
alt: title,
},
],
},
twitter: {
card: 'summary_large_image',
title,
description,
images: [image],
},
robots: isStaging
? {
index: false,
follow: false,
nocache: true,
googleBot: {
index: false,
follow: false,
noimageindex: true,
},
}
: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
};
}

View File

@ -1,16 +1,16 @@
{
"0": "OrdersService",
"0": ".retryPayment",
"1": "productService.ts",
"2": "CmsController",
"3": "app.module.ts",
"4": "reviews.controller.ts",
"4": "ReviewsService",
"5": "tickets.controller.ts",
"6": "UserDashboard.tsx",
"6": "userStore.ts",
"7": "MediaSelector.tsx",
"8": "SettingsController",
"9": "PetProfile.tsx",
"8": "PetProfile.tsx",
"9": "components/Skeleton.tsx",
"10": "DoctorsService",
"11": "useSettingsStore",
"11": "trust-seals/page.tsx",
"12": "adminRoutes.tsx",
"13": "PrismaService",
"14": "pets/pets.controller.ts",
@ -18,7 +18,7 @@
"16": "lib/services/api.ts",
"17": "CreateVideoDto",
"18": "src/services/api.ts",
"19": "auth.service.ts",
"19": "auth.controller.ts",
"20": "BE-001",
"21": "Roles",
"22": "FE-001",
@ -28,31 +28,31 @@
"26": "TEST-001",
"27": "DEVOPS-001",
"28": "DOC-001",
"29": "WholesaleApplyDto",
"30": "app-audit-verification.e2e-spec.js",
"29": "WholesaleService",
"30": "main.ts",
"31": "JwtAuthGuard",
"32": "ZibalService",
"33": "راهنمای تست سیستم (Software Testing)",
"34": "CategoriesController",
"35": "B2BService",
"36": "What You Must Do When Invoked",
"37": "userStore.ts",
"38": "UsersService",
"37": "toPersian",
"38": "UsersController",
"39": "What You Must Do When Invoked",
"40": "SslController",
"41": "PodcastPlayerModal.tsx",
"42": "IngredientsService",
"43": "MetricsController",
"44": "MediaController",
"43": "UsersService",
"44": "admin.module.ts",
"45": "Pagination.tsx",
"46": "SmsService",
"47": "orders.service.ts",
"47": "OrdersService",
"48": "PrescriptionsService",
"49": "SmartAdvisorService",
"50": "TestimonialsService",
"51": "Role & Core Objective",
"52": "ContactService",
"53": "PaymentController",
"52": "AuthController",
"53": "auth.module.ts",
"54": "compilerOptions",
"55": "Media.tsx",
"56": "compilerOptions",
@ -60,9 +60,9 @@
"58": "PaginationDto",
"59": "dependencies",
"60": "compilerOptions",
"61": "HomeClient.tsx",
"61": "useSettingsStore",
"62": "BlogsController",
"63": "SettingsService",
"63": "reviews.controller.ts",
"64": "BannersService",
"65": "Required Review Group Closures",
"66": "Coupons.tsx",
@ -79,7 +79,7 @@
"77": "scripts",
"78": "Role & Core Objective",
"79": "HomeController",
"80": "zibal.service.ts",
"80": "VetGallery.tsx",
"81": "devDependencies",
"82": "api",
"83": "Orders.tsx",
@ -111,6 +111,7 @@
"109": "Operational Rules & Boundaries",
"110": "AppService",
"111": "SmsLogQueryDto",
"112": "app-audit-verification.e2e-spec.js",
"113": "Vazirmatn Changelog",
"114": "Vazirmatn Font فونت وزیرمتن",
"115": "Operational Rules & Boundaries",
@ -127,6 +128,7 @@
"126": "Role & Core Objective",
"127": "orchestrate.py",
"128": "backend/package.json",
"129": "CreateOrderDto",
"130": "graphify reference: extra exports and benchmark",
"131": "Phase 2 Final Quality Gate Summary Report",
"132": "Task Modifications Log",
@ -134,12 +136,13 @@
"134": "ErrorBoundary",
"135": "application/package.json",
"136": "generate-openapi.js",
"137": "AdminTransactionFilterDto",
"138": "InitiatePaymentDto",
"137": "PetsService",
"138": "payment.controller.ts",
"139": "System Discovery",
"140": "Product Requirement Document (PRD)",
"141": "WikiController",
"143": "RedisService",
"142": "seo.ts",
"143": "auth.service.ts",
"144": "Baseline Command Plan & Reconciled Command History",
"145": "SmsSettingsPage.tsx",
"146": "ErrorPages.tsx",
@ -164,13 +167,13 @@
"165": "rebuild_honest_ledger.js",
"166": "validate_evidence_grade.js",
"167": "supertest",
"168": "blog/[slug]/page.tsx",
"168": "getSeoConfig",
"169": "API Contract Specification",
"170": "⚙️ Backend Technical Review (05_dev_backend)",
"171": "🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)",
"172": "🚀 SEO & Content Strategy Review (12_seo_content)",
"173": "@types/node",
"174": ".handleZibalCallback",
"174": "BlogsService",
"175": "seed-ui-texts.ts",
"176": "seed-wiki.ts",
"177": "update-blog.dto.ts",
@ -182,7 +185,7 @@
"183": "Raw Finding Verification & Disposition Report",
"184": "React + TypeScript + Vite",
"185": "Select.tsx",
"186": "wiki/[slug]/page.tsx",
"186": "AdminService",
"187": "ts-node",
"188": "application/README.md",
"189": "@types/express",
@ -208,13 +211,16 @@
"209": "sync_honest_manifest.js",
"210": "sync_manifest.js",
"211": "@types/multer",
"212": "WholesaleApplyDto",
"213": "@testing-library/jest-dom",
"214": "PetsService",
"215": "VerifyOtpDto",
"216": "FormField.tsx",
"217": "Input.tsx",
"218": "Textarea.tsx",
"219": "admin-panel/tsconfig.json",
"220": "about/page.tsx",
"221": "privacy/page.tsx",
"220": "getPageMetadata",
"221": "CreateHealthLogDto",
"222": "next.config.ts",
"223": "Shabnam Font README",
"224": "AGENTS.md",
@ -225,13 +231,16 @@
"229": "eslint-plugin-react-refresh",
"230": "@tailwindcss/postcss",
"231": "typescript",
"232": "AdminService",
"232": "Body",
"233": "@testing-library/react",
"234": "@types/react",
"235": "typescript",
"236": "vitest",
"237": "axios",
"238": "AddressDto",
"239": "blog/page.tsx",
"240": "ts-loader",
"241": "contact/page.tsx",
"242": "@types/bcrypt",
"243": "app.e2e-spec.js",
"244": "blog.entity.ts",
@ -286,11 +295,17 @@
"293": "typescript",
"294": "app-audit-verification.e2e-spec.d.ts",
"295": "app.e2e-spec.d.ts",
"297": "@eslint/eslintrc",
"296": "track/page.tsx",
"297": "ValidateCouponDto",
"298": "eslint-config-prettier",
"299": "catalog/page.tsx",
"300": "app/page.tsx",
"301": "videos/page.tsx",
"302": "RedisModule",
"303": "eslint-plugin-prettier",
"304": "eslint-config-next",
"305": "RouteErrorBoundary",
"309": "globals",
"310": "admin.module.ts",
"312": "tailwindcss",
"313": "@types/react-dom"
"310": "ReportsController",
"312": "tailwindcss"
}

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

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,32 +1,32 @@
# Graph Report - canina (2026-08-18)
# Graph Report - canina (2026-08-19)
## Corpus Check
- 521 files · ~742,721 words
- 522 files · ~745,025 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 3690 nodes · 6249 edges · 294 communities (196 shown, 98 thin omitted)
- 3702 nodes · 6325 edges · 309 communities (206 shown, 103 thin omitted)
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 230 edges (avg confidence: 0.79)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `47437883`
- Built from commit: `df634fdb`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
## Community Hubs (Navigation)
- OrdersService
- .retryPayment
- productService.ts
- CmsController
- app.module.ts
- reviews.controller.ts
- ReviewsService
- tickets.controller.ts
- UserDashboard.tsx
- userStore.ts
- MediaSelector.tsx
- SettingsController
- PetProfile.tsx
- components/Skeleton.tsx
- DoctorsService
- useSettingsStore
- trust-seals/page.tsx
- adminRoutes.tsx
- PrismaService
- pets/pets.controller.ts
@ -34,7 +34,7 @@
- lib/services/api.ts
- CreateVideoDto
- src/services/api.ts
- auth.service.ts
- auth.controller.ts
- BE-001
- Roles
- FE-001
@ -44,31 +44,31 @@
- TEST-001
- DEVOPS-001
- DOC-001
- WholesaleApplyDto
- app-audit-verification.e2e-spec.js
- WholesaleService
- main.ts
- JwtAuthGuard
- ZibalService
- راهنمای تست سیستم (Software Testing)
- CategoriesController
- B2BService
- What You Must Do When Invoked
- userStore.ts
- UsersService
- toPersian
- UsersController
- What You Must Do When Invoked
- SslController
- PodcastPlayerModal.tsx
- IngredientsService
- MetricsController
- MediaController
- UsersService
- admin.module.ts
- Pagination.tsx
- SmsService
- orders.service.ts
- OrdersService
- PrescriptionsService
- SmartAdvisorService
- TestimonialsService
- Role & Core Objective
- ContactService
- PaymentController
- AuthController
- auth.module.ts
- compilerOptions
- Media.tsx
- compilerOptions
@ -76,9 +76,9 @@
- PaginationDto
- dependencies
- compilerOptions
- HomeClient.tsx
- useSettingsStore
- BlogsController
- SettingsService
- reviews.controller.ts
- BannersService
- Required Review Group Closures
- Coupons.tsx
@ -95,7 +95,7 @@
- scripts
- Role & Core Objective
- HomeController
- zibal.service.ts
- VetGallery.tsx
- devDependencies
- api
- Orders.tsx
@ -127,6 +127,7 @@
- Operational Rules & Boundaries
- AppService
- SmsLogQueryDto
- app-audit-verification.e2e-spec.js
- Vazirmatn Changelog
- Vazirmatn Font فونت وزیرمتن
- Operational Rules & Boundaries
@ -143,6 +144,7 @@
- Role & Core Objective
- orchestrate.py
- backend/package.json
- CreateOrderDto
- graphify reference: extra exports and benchmark
- Phase 2 Final Quality Gate Summary Report
- Task Modifications Log
@ -150,12 +152,13 @@
- ErrorBoundary
- application/package.json
- generate-openapi.js
- AdminTransactionFilterDto
- InitiatePaymentDto
- PetsService
- payment.controller.ts
- System Discovery
- Product Requirement Document (PRD)
- WikiController
- RedisService
- seo.ts
- auth.service.ts
- Baseline Command Plan & Reconciled Command History
- SmsSettingsPage.tsx
- ErrorPages.tsx
@ -180,13 +183,13 @@
- rebuild_honest_ledger.js
- validate_evidence_grade.js
- supertest
- blog/[slug]/page.tsx
- getSeoConfig
- API Contract Specification
- ⚙️ Backend Technical Review (05_dev_backend)
- 🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)
- 🚀 SEO & Content Strategy Review (12_seo_content)
- @types/node
- .handleZibalCallback
- BlogsService
- seed-ui-texts.ts
- seed-wiki.ts
- update-blog.dto.ts
@ -198,7 +201,7 @@
- Raw Finding Verification & Disposition Report
- React + TypeScript + Vite
- Select.tsx
- wiki/[slug]/page.tsx
- AdminService
- ts-node
- application/README.md
- @types/express
@ -224,13 +227,16 @@
- sync_honest_manifest.js
- sync_manifest.js
- @types/multer
- WholesaleApplyDto
- @testing-library/jest-dom
- PetsService
- VerifyOtpDto
- FormField.tsx
- Input.tsx
- Textarea.tsx
- admin-panel/tsconfig.json
- about/page.tsx
- privacy/page.tsx
- getPageMetadata
- CreateHealthLogDto
- next.config.ts
- Shabnam Font README
- AGENTS.md
@ -241,12 +247,15 @@
- eslint-plugin-react-refresh
- @tailwindcss/postcss
- typescript
- AdminService
- Body
- @testing-library/react
- @types/react
- typescript
- vitest
- AddressDto
- blog/page.tsx
- ts-loader
- contact/page.tsx
- @types/bcrypt
- app.e2e-spec.js
- blog.entity.ts
@ -285,13 +294,19 @@
- NetworkBanner.tsx
- @types/supertest
- typescript
- @eslint/eslintrc
- track/page.tsx
- ValidateCouponDto
- eslint-config-prettier
- catalog/page.tsx
- app/page.tsx
- videos/page.tsx
- RedisModule
- eslint-plugin-prettier
- eslint-config-next
- RouteErrorBoundary
- globals
- admin.module.ts
- ReportsController
- tailwindcss
- @types/react-dom
## God Nodes (most connected - your core abstractions)
1. `PrismaService` - 81 edges
@ -302,8 +317,8 @@
6. `PaginationDto` - 39 edges
7. `AdminService` - 34 edges
8. `AdminController` - 33 edges
9. `JwtAuthGuard` - 32 edges
10. `toPersian()` - 32 edges
9. `getPageMetadata()` - 33 edges
10. `JwtAuthGuard` - 32 edges
## Surprising Connections (you probably didn't know these)
- `User Profile Photo` --conceptually_related_to--> `User Roles and Capabilities` [INFERRED]
@ -326,55 +341,51 @@
- **Rastikerdar Font Ecosystem** — frontend_application_public_fonts_shabnam_font_v5_0_1_readme, frontend_application_public_fonts_vazirmatn_v33_003_readme, vazir_font [EXTRACTED 0.95]
- **Canina Deployment Stack** — scripts_compose_prod, scripts_compose_stage [EXTRACTED 1.00]
## Communities (294 total, 98 thin omitted)
## Communities (309 total, 103 thin omitted)
### Community 0 - "OrdersService"
Cohesion: 0.07
Nodes (29): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+21 more)
### Community 0 - ".retryPayment"
Cohesion: 0.15
Nodes (11): ApiBadRequestResponse, ApiCreatedResponse, ApiNotFoundResponse, ApiOkResponse, ApiOperation, Body, Get, Param (+3 more)
### Community 1 - "productService.ts"
Cohesion: 0.06
Nodes (36): Blog(), getBlogs(), metadata, CatalogClient(), metadata, metadata, metadata, BlogPage() (+28 more)
Cohesion: 0.08
Nodes (28): BlogPost, CatalogPageSpread(), CatalogPageSpreadProps, CategoryMeta, getFormBadge(), getSpeciesBadge(), FlipbookCatalog(), FlipbookCatalogProps (+20 more)
### Community 2 - "CmsController"
Cohesion: 0.09
Nodes (24): CmsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+16 more)
### Community 3 - "app.module.ts"
Cohesion: 0.07
Nodes (34): B2BModule, Module, BannersModule, Module, CmsModule, Module, SmsModule, Global (+26 more)
Cohesion: 0.08
Nodes (30): B2BModule, Module, BannersModule, Module, CmsModule, Module, SmsModule, Global (+22 more)
### Community 4 - "reviews.controller.ts"
Cohesion: 0.07
Nodes (31): CreateReviewDto, ApiProperty, IsInt, IsNotEmpty, IsOptional, IsString, Min, ApiProperty (+23 more)
### Community 4 - "ReviewsService"
Cohesion: 0.12
Nodes (16): ReviewsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+8 more)
### Community 5 - "tickets.controller.ts"
Cohesion: 0.09
Nodes (29): AdminUpdateTicketDto, CreateTicketDto, ReplyTicketDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString (+21 more)
Nodes (31): AdminUpdateTicketDto, CreateTicketDto, ReplyTicketDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString (+23 more)
### Community 6 - "UserDashboard.tsx"
Cohesion: 0.09
Nodes (28): VerifyContent(), AddressModal(), AddressModalProps, BackButton(), BackButtonProps, DeleteConfirmModal(), DeleteConfirmModalProps, HeaderButton() (+20 more)
### Community 6 - "userStore.ts"
Cohesion: 0.10
Nodes (15): AddressModal(), AddressModalProps, SearchableSelect(), SearchableSelectProps, IRAN_PROVINCES, PROVINCE_CITIES, ApiErr, AuthResponse (+7 more)
### Community 7 - "MediaSelector.tsx"
Cohesion: 0.07
Nodes (27): ConfirmModal(), ConfirmModalProps, getFileType(), Media, MediaSelector(), MediaSelectorProps, BlogPost, Category (+19 more)
### Community 8 - "SettingsController"
Cohesion: 0.21
Nodes (6): SettingsController, ApiOkResponse, ApiTags, Controller, Get, Query
### Community 9 - "PetProfile.tsx"
### Community 8 - "PetProfile.tsx"
Cohesion: 0.14
Nodes (12): FeaturedProducts(), OrderRowSkeleton(), PetProfileSkeleton(), ProductCardSkeleton(), Skeleton(), SkeletonProps, mockProducts, HealthLog (+4 more)
Nodes (15): Header(), MENU_ICONS, OrderSuccess(), PrescriptionUploadModal(), PrescriptionUploadModalProps, CURRENT_SYMPTOMS, MEDICAL_HISTORIES, SmartAdvisorProps (+7 more)
### Community 9 - "components/Skeleton.tsx"
Cohesion: 0.29
Nodes (5): OrderRowSkeleton(), PetProfileSkeleton(), ProductCardSkeleton(), Skeleton(), SkeletonProps
### Community 10 - "DoctorsService"
Cohesion: 0.13
Nodes (15): DoctorsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+7 more)
### Community 11 - "useSettingsStore"
Cohesion: 0.14
Nodes (14): metadata, BrandLogo(), BrandLogoProps, EnamadBadge(), Footer(), Hero(), StatCounter(), MaintenancePage() (+6 more)
Cohesion: 0.12
Nodes (16): DoctorsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+8 more)
### Community 12 - "adminRoutes.tsx"
Cohesion: 0.09
@ -382,39 +393,39 @@ Nodes (16): App(), HeroBanner, VetTestimonial, ContactInfoItem, ContactSubmissio
### Community 13 - "PrismaService"
Cohesion: 0.08
Nodes (15): B2BWholesaleOrderItem, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions, SmsConfig, SmsLogQuery, CreateContactSubmissionDto, UpdateContactInfoItemDto (+7 more)
Nodes (16): ApiExcludeController, MetricsController, Controller, Get, Res, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions (+8 more)
### Community 14 - "pets/pets.controller.ts"
Cohesion: 0.13
Nodes (15): CreatePetDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional, IsString (+7 more)
Cohesion: 0.11
Nodes (19): CreatePetDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional, IsString (+11 more)
### Community 15 - "ProductsService"
Cohesion: 0.09
Nodes (19): GetProductsDto, ApiPropertyOptional, IsEnum, IsOptional, IsString, ProductsController, ApiOperation, ApiTags (+11 more)
Cohesion: 0.10
Nodes (17): GetProductsDto, ApiPropertyOptional, IsEnum, IsOptional, IsString, ProductsController, ApiOperation, ApiTags (+9 more)
### Community 16 - "lib/services/api.ts"
Cohesion: 0.06
Nodes (18): metadata, ContactFormClient(), ContactInfoItem, Testimonial, TestimonialsSection(), api, ApiErrorPayload, BASE_DOMAIN (+10 more)
Cohesion: 0.07
Nodes (26): ContactInfoItem, DeleteConfirmModal(), DeleteConfirmModalProps, OrderDetailsModal(), OrderDetailsModalProps, PRESET_AMOUNTS, TopUpModal(), TopUpModalProps (+18 more)
### Community 17 - "CreateVideoDto"
Cohesion: 0.09
Nodes (24): CreateVideoDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, UpdateVideoDto (+16 more)
Cohesion: 0.07
Nodes (32): CreateVideoDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, UpdateVideoDto (+24 more)
### Community 18 - "src/services/api.ts"
Cohesion: 0.10
Nodes (23): ProtectedRoute(), SEARCHABLE_PAGES, Topbar(), TopbarProps, Login(), B2BManager, SmartAdvisorManager, SystemSettingsPage (+15 more)
### Community 19 - "auth.service.ts"
Cohesion: 0.06
Nodes (45): AuthController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+37 more)
### Community 19 - "auth.controller.ts"
Cohesion: 0.08
Nodes (24): AdminLoginDto, ApiProperty, IsEmail, IsNotEmpty, IsString, MinLength, LoginDto, ApiProperty (+16 more)
### Community 20 - "BE-001"
Cohesion: 0.06
Nodes (32): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, BE-001, Category, Completion Statement, Confidence (+24 more)
### Community 21 - "Roles"
Cohesion: 0.25
Nodes (10): Roles(), ApiBearerAuth, ApiOperation, Body, Delete, Param, Patch, Post (+2 more)
Cohesion: 0.07
Nodes (29): Roles(), ContactController, Body, Controller, Get, Param, Post, Put (+21 more)
### Community 22 - "FE-001"
Cohesion: 0.06
@ -444,101 +455,101 @@ Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternati
Cohesion: 0.06
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Category, Completion Statement, Confidence, Dependencies (+23 more)
### Community 29 - "WholesaleApplyDto"
Cohesion: 0.10
Nodes (20): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto, ApiBearerAuth, ApiOperation (+12 more)
### Community 29 - "WholesaleService"
Cohesion: 0.14
Nodes (14): ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param, Post (+6 more)
### Community 30 - "app-audit-verification.e2e-spec.js"
Cohesion: 0.07
Nodes (22): AppModule, Module, CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable (+14 more)
### Community 30 - "main.ts"
Cohesion: 0.11
Nodes (11): AppModule, Module, CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable (+3 more)
### Community 31 - "JwtAuthGuard"
Cohesion: 0.21
Cohesion: 0.19
Nodes (7): JwtAuthGuard, Injectable, ROLES_KEY, RequestWithUser, RolesGuard, Injectable, UserReqPayload
### Community 32 - "ZibalService"
Cohesion: 0.14
Nodes (4): PaymentService, Injectable, Injectable, ZibalService
Cohesion: 0.05
Nodes (42): AdminTransactionFilterDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type, ApiPropertyOptional (+34 more)
### Community 33 - "راهنمای تست سیستم (Software Testing)"
Cohesion: 0.07
Nodes (25): اجرای بک‌اند, اجرای فرانت‌اند, اجرای پروژه در سرور با PM2, برای راه‌اندازی بک‌اند:, برای راه‌اندازی فرانت‌اند Next.js:, بیلد کردن پروژه (Building), ذخیره پروسه‌ها تا پس از ری‌استارت سرور قطع نشوند:, راه‌اندازی و انتشار سیستم (Setup & Deployment) (+17 more)
### Community 34 - "CategoriesController"
Cohesion: 0.10
Nodes (14): CategoriesController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+6 more)
Cohesion: 0.09
Nodes (17): CategoriesController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+9 more)
### Community 35 - "B2BService"
Cohesion: 0.14
Nodes (14): B2BController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+6 more)
Cohesion: 0.13
Nodes (15): B2BController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+7 more)
### Community 36 - "What You Must Do When Invoked"
Cohesion: 0.07
Nodes (26): For /graphify add and --watch, For /graphify query, For the commit hook and native CLAUDE.md integration, For --update and --cluster-only, /graphify, Honesty Rules, Interpreter guard for subcommands, Part A - Structural extraction for code files (+18 more)
### Community 37 - "userStore.ts"
Cohesion: 0.09
Nodes (32): ClientLayout(), ArchiveProductCard(), AuthModal(), AuthModalProps, extractOtpFromText(), B2BPortal(), CartDrawer(), CheckoutPage() (+24 more)
### Community 37 - "toPersian"
Cohesion: 0.14
Nodes (27): ClientLayout(), VerifyContent(), ArchiveProductCard(), AuthModal(), AuthModalProps, extractOtpFromText(), B2BPortal(), BackButton() (+19 more)
### Community 38 - "UsersService"
Cohesion: 0.06
Nodes (41): DEFAULT_JWT_REFRESH_SECRET, DEFAULT_JWT_SECRET, getJwtSecret(), AuthModule, Module, JwtPayload, JwtStrategy, Injectable (+33 more)
### Community 38 - "UsersController"
Cohesion: 0.21
Nodes (16): ApiBadRequestResponse, ApiBearerAuth, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+8 more)
### Community 39 - "What You Must Do When Invoked"
Cohesion: 0.07
Nodes (26): For /graphify add and --watch, For /graphify query, For the commit hook and native CLAUDE.md integration, For --update and --cluster-only, /graphify, Honesty Rules, Interpreter guard for subcommands, Part A - Structural extraction for code files (+18 more)
### Community 40 - "SslController"
Cohesion: 0.13
Nodes (14): SslController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Post (+6 more)
Cohesion: 0.11
Nodes (15): SslController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Post (+7 more)
### Community 41 - "PodcastPlayerModal.tsx"
Cohesion: 0.40
Nodes (3): PLAYBACK_RATES, PodcastPlayerModal(), PodcastPlayerModalProps
### Community 42 - "IngredientsService"
Cohesion: 0.13
Nodes (14): IngredientsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
### Community 43 - "MetricsController"
Cohesion: 0.29
Nodes (5): ApiExcludeController, MetricsController, Controller, Get, Res
### Community 44 - "MediaController"
Cohesion: 0.11
Nodes (14): MediaController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
Nodes (16): IngredientsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+8 more)
### Community 43 - "UsersService"
Cohesion: 0.12
Nodes (9): ApiPropertyOptional, IsEmail, IsOptional, IsString, MinLength, UpdateProfileDto, Injectable, UserAddressInput (+1 more)
### Community 44 - "admin.module.ts"
Cohesion: 0.10
Nodes (18): AdminModule, Module, MediaController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller (+10 more)
### Community 45 - "Pagination.tsx"
Cohesion: 0.13
Nodes (11): Pagination(), PaginationProps, Pet, GatewayHealth, Stats, Transaction, ProductItem, WikiTerm (+3 more)
### Community 47 - "orders.service.ts"
Cohesion: 0.24
Nodes (6): IsNotEmpty, IsNumber, IsString, ValidateCouponDto, OrdersModule, Module
### Community 47 - "OrdersService"
Cohesion: 0.16
Nodes (9): OrdersController, ApiBearerAuth, ApiTags, Controller, UseGuards, OrdersModule, Module, OrdersService (+1 more)
### Community 48 - "PrescriptionsService"
Cohesion: 0.14
Cohesion: 0.13
Nodes (14): PrescriptionsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+6 more)
### Community 49 - "SmartAdvisorService"
Cohesion: 0.13
Nodes (14): SmartAdvisorController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
Cohesion: 0.11
Nodes (16): SmartAdvisorController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+8 more)
### Community 50 - "TestimonialsService"
Cohesion: 0.13
Cohesion: 0.12
Nodes (14): TestimonialsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
### Community 51 - "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 52 - "ContactService"
Cohesion: 0.13
Nodes (11): ContactController, Body, Controller, Get, Param, Post, Put, Query (+3 more)
### Community 53 - "PaymentController"
### Community 52 - "AuthController"
Cohesion: 0.25
Nodes (13): PaymentController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+5 more)
Nodes (12): AuthController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+4 more)
### Community 53 - "auth.module.ts"
Cohesion: 0.15
Nodes (10): DEFAULT_JWT_REFRESH_SECRET, DEFAULT_JWT_SECRET, getJwtSecret(), AuthModule, Module, JwtPayload, JwtStrategy, Injectable (+2 more)
### Community 54 - "compilerOptions"
Cohesion: 0.06
@ -557,8 +568,8 @@ Cohesion: 0.15
Nodes (11): PetsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Controller, Delete, Get (+3 more)
### Community 58 - "PaginationDto"
Cohesion: 0.07
Nodes (22): BlogsModule, Module, BlogsService, Injectable, PaginationDto, SortOrder, ApiPropertyOptional, IsEnum (+14 more)
Cohesion: 0.12
Nodes (13): PaginationDto, SortOrder, ApiPropertyOptional, IsEnum, IsInt, IsOptional, IsString, Min (+5 more)
### Community 59 - "dependencies"
Cohesion: 0.05
@ -568,16 +579,20 @@ Nodes (41): dependencies, bcrypt, bcryptjs, class-transformer, class-validator,
Cohesion: 0.10
Nodes (20): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, lib, module, moduleDetection, moduleResolution, noEmit (+12 more)
### Community 61 - "HomeClient.tsx"
Cohesion: 0.12
Nodes (20): HomeClient(), HomeClientProps, getHomeData(), Home(), metadata, metadata, ArchivePage(), CATEGORY_MAP (+12 more)
### Community 61 - "useSettingsStore"
Cohesion: 0.09
Nodes (28): HomeClient(), HomeClientProps, CATEGORY_MAP, ICON_MAP, BannerPlacement(), BannerPlacementProps, BrandLogo(), BrandLogoProps (+20 more)
### Community 62 - "BlogsController"
Cohesion: 0.13
Nodes (15): BlogsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+7 more)
### Community 63 - "reviews.controller.ts"
Cohesion: 0.16
Nodes (13): CreateReviewDto, ApiProperty, IsInt, IsNotEmpty, IsOptional, IsString, Min, ApiProperty (+5 more)
### Community 64 - "BannersService"
Cohesion: 0.13
Cohesion: 0.12
Nodes (15): BannersController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+7 more)
### Community 65 - "Required Review Group Closures"
@ -601,19 +616,19 @@ Cohesion: 0.13
Nodes (14): ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete, Get (+6 more)
### Community 70 - "ApiOperation"
Cohesion: 0.26
Cohesion: 0.13
Nodes (8): ApiOperation, ApiQuery, Get, Query, AdminQueryDto, ApiPropertyOptional, IsOptional, IsString
### Community 71 - "PetsController"
Cohesion: 0.08
Nodes (32): CreateHealthLogDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, CreateReminderDto, ApiProperty (+24 more)
Cohesion: 0.14
Nodes (20): PetsController, ApiBadRequestResponse, ApiBearerAuth, ApiCreatedResponse, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags (+12 more)
### Community 72 - "seo.module.ts"
Cohesion: 0.16
Nodes (10): SeoController, ApiOperation, ApiTags, Controller, Get, Param, SeoModule, Module (+2 more)
### Community 73 - "admin.service.ts"
Cohesion: 0.19
Cohesion: 0.17
Nodes (14): CouponInput, CouponTargetInput, PaginationQuery, CreateUserDto, ApiProperty, ApiPropertyOptional, IsEmail, IsNotEmpty (+6 more)
### Community 74 - "🏢 AI Software Agency — Master Orchestration Protocol v3"
@ -640,13 +655,13 @@ Nodes (16): 1. Active Secret Scanning (All Modified Files), 2. Docker Container
Cohesion: 0.16
Nodes (10): HomeController, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, HomeModule, Module (+2 more)
### Community 80 - "zibal.service.ts"
Cohesion: 0.16
Nodes (9): GatewayHealthResult, IPaymentGateway, PaymentInquiryResult, PaymentRequestOptions, PaymentRequestResult, PaymentVerifyResult, ZibalInquiryResponse, ZibalRequestResponse (+1 more)
### Community 80 - "VetGallery.tsx"
Cohesion: 0.24
Nodes (8): DisplayVideoItem, FALLBACK_VIDEOS, TestimonialItem, PLAYBACK_RATES, VideoModalPlayer(), VideoModalPlayerProps, Video, videoService
### Community 81 - "devDependencies"
Cohesion: 0.13
Nodes (15): eslint-config-next, devDependencies, eslint, eslint-config-next, jsdom, tailwindcss, @tailwindcss/postcss, @types/node (+7 more)
Nodes (15): devDependencies, eslint, jsdom, tailwindcss, @tailwindcss/postcss, @types/node, @types/react-dom, @vitejs/plugin-react (+7 more)
### Community 82 - "api"
Cohesion: 0.29
@ -681,8 +696,8 @@ Cohesion: 0.10
Nodes (21): dependencies, axios, lucide-react, react, react-dom, react-hot-toast, react-router-dom, recharts (+13 more)
### Community 90 - "ProductPage.tsx"
Cohesion: 0.07
Nodes (25): ProductDetailModalProps, PLAYBACK_RATES, PodcastInlinePlayer(), PodcastInlinePlayerProps, CalculatorState, ICON_MAP, ProductPage(), useCalculatorStore (+17 more)
Cohesion: 0.12
Nodes (14): PLAYBACK_RATES, PodcastInlinePlayer(), PodcastInlinePlayerProps, CalculatorState, ICON_MAP, ProductPage(), useCalculatorStore, ProductReviews() (+6 more)
### Community 91 - "compilerOptions"
Cohesion: 0.06
@ -713,8 +728,8 @@ Cohesion: 0.15
Nodes (13): jest, collectCoverageFrom, coverageDirectory, moduleFileExtensions, rootDir, testEnvironment, testRegex, transform (+5 more)
### Community 98 - "AdminController"
Cohesion: 0.11
Nodes (9): AdminController, ApiBearerAuth, ApiTags, Body, Controller, Delete, Param, Put (+1 more)
Cohesion: 0.12
Nodes (8): AdminController, ApiBearerAuth, ApiTags, Controller, Delete, Param, Put, UseGuards
### Community 99 - "Comprehensive Change Log"
Cohesion: 0.15
@ -729,8 +744,8 @@ Cohesion: 0.10
Nodes (18): ICON_REGISTRY, IconPickerModal(), IconPickerModalProps, COLORS, RichTextEditor(), RichTextEditorProps, ToggleSwitch(), ToggleSwitchProps (+10 more)
### Community 102 - "BlogsController"
Cohesion: 0.21
Nodes (9): BlogsController, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, Param (+1 more)
Cohesion: 0.13
Nodes (13): BlogsController, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, Param (+5 more)
### Community 103 - "ProductDto"
Cohesion: 0.22
@ -764,6 +779,10 @@ Nodes (5): AppController, Controller, Get, AppService, Injectable
Cohesion: 0.25
Nodes (7): SmsLogQueryDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type
### Community 112 - "app-audit-verification.e2e-spec.js"
Cohesion: 0.17
Nodes (11): app_module_1, client_1, common_1, decimal_interceptor_1, fs, http_exception_filter_1, jwt_1, path (+3 more)
### Community 113 - "Vazirmatn Changelog"
Cohesion: 0.18
Nodes (10): 32.0.0, 32.1, 32.101, 32.102, 33.000, 33.001, 33.002, 33.003 (+2 more)
@ -790,7 +809,7 @@ Nodes (9): Compile and run the project, Deployment, Description, License, Projec
### Community 119 - "devDependencies"
Cohesion: 0.09
Nodes (23): devDependencies, eslint, @eslint/js, eslint-plugin-prettier, jest, @nestjs/schematics, @nestjs/testing, source-map-support (+15 more)
Nodes (23): devDependencies, eslint, @eslint/eslintrc, @eslint/js, jest, @nestjs/schematics, @nestjs/testing, source-map-support (+15 more)
### Community 120 - "Repository Map"
Cohesion: 0.20
@ -828,6 +847,10 @@ Nodes (8): cmd_next_task(), cmd_progress(), cmd_reset(), cmd_status(), cmd_unblo
Cohesion: 0.22
Nodes (8): author, description, license, name, prisma, seed, private, version
### Community 129 - "CreateOrderDto"
Cohesion: 0.22
Nodes (11): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+3 more)
### Community 130 - "graphify reference: extra exports and benchmark"
Cohesion: 0.22
Nodes (8): graphify reference: extra exports and benchmark, Step 6b - Wiki (only if --wiki flag), Step 7 - Neo4j export (only if --neo4j or --neo4j-push flag), Step 7a - FalkorDB export (only if --falkordb or --falkordb-push flag), Step 7b - SVG export (only if --svg flag), Step 7c - GraphML export (only if --graphml flag), Step 7d - MCP server (only if --mcp flag), Step 8 - Token reduction benchmark (only if total_words > 5000)
@ -856,12 +879,8 @@ Nodes (8): name, private, scripts, build, dev, lint, start, version
Cohesion: 0.25
Nodes (6): app_module_1, core_1, fs, path, swagger_1, yaml
### Community 137 - "AdminTransactionFilterDto"
Cohesion: 0.25
Nodes (7): AdminTransactionFilterDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type
### Community 138 - "InitiatePaymentDto"
Cohesion: 0.43
### Community 138 - "payment.controller.ts"
Cohesion: 0.42
Nodes (7): InitiatePaymentDto, InitiateWalletTopupDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString
### Community 139 - "System Discovery"
@ -876,9 +895,13 @@ Nodes (6): 1. Executive Vision, 2. Target Audience, 3. Functional Requirements,
Cohesion: 0.21
Nodes (9): ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, Param, Query (+1 more)
### Community 143 - "RedisService"
Cohesion: 0.10
Nodes (6): AuthService, Injectable, normalizeMobile(), RedisService, Injectable, UserAddressInput
### Community 142 - "seo.ts"
Cohesion: 0.20
Nodes (5): generateMetadata(), generateMetadata(), DEFAULT_SEO_CONFIG, PageMetadataOptions, SeoConfig
### Community 143 - "auth.service.ts"
Cohesion: 0.11
Nodes (8): AdminLoginInput, AuthService, LoginInput, RegisterInput, Injectable, normalizeMobile(), RedisService, Injectable
### Community 144 - "Baseline Command Plan & Reconciled Command History"
Cohesion: 0.29
@ -960,9 +983,9 @@ Nodes (4): evidenceList, ledger, mandatoryMap, mandatoryScope
Cohesion: 0.40
Nodes (4): activeFiles, errors, validationOutput, warnings
### Community 168 - "blog/[slug]/page.tsx"
Cohesion: 0.60
Nodes (4): BlogPostPage(), generateMetadata(), getBlog(), revalidate
### Community 168 - "getSeoConfig"
Cohesion: 0.24
Nodes (11): BlogPostPage(), generateMetadata(), getBlog(), revalidate, generateMetadata(), generateMetadata(), generateMetadata(), getWikiTerm() (+3 more)
### Community 169 - "API Contract Specification"
Cohesion: 0.50
@ -980,9 +1003,9 @@ 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 174 - ".handleZibalCallback"
Cohesion: 0.24
Nodes (8): ApiPropertyOptional, IsOptional, IsString, ZibalCallbackQueryDto, Query, Res, Headers, Ip
### Community 174 - "BlogsService"
Cohesion: 0.22
Nodes (3): BlogQuery, BlogsService, Injectable
### Community 180 - "graphify reference: add a URL and watch a folder"
Cohesion: 0.50
@ -1008,10 +1031,6 @@ Nodes (3): Expanding the ESLint configuration, React Compiler, React + TypeScrip
Cohesion: 0.50
Nodes (3): Select, SelectOption, SelectProps
### Community 186 - "wiki/[slug]/page.tsx"
Cohesion: 0.83
Nodes (3): generateMetadata(), getWikiTerm(), WikiTermPage()
### Community 188 - "application/README.md"
Cohesion: 0.50
Nodes (3): Deploy on Vercel, Getting Started, Learn More
@ -1024,45 +1043,81 @@ Nodes (3): COMPOSE_DOCKER_CLI_BUILD, DOCKER_BUILDKIT, deploy.sh script
Cohesion: 0.67
Nodes (3): ADR-AUTH-001: Admin Panel Authentication Architecture & Token Management, Master Task Backlog (Phase 3.3), Phase 3 Master Execution Plan
### Community 212 - "WholesaleApplyDto"
Cohesion: 0.29
Nodes (6): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto
### Community 214 - "PetsService"
Cohesion: 0.29
Nodes (3): PetQuery, PetsService, Injectable
### Community 215 - "VerifyOtpDto"
Cohesion: 0.29
Nodes (6): ApiProperty, IsNotEmpty, IsString, Matches, VerifyOtpDto, Length
### Community 220 - "getPageMetadata"
Cohesion: 0.13
Nodes (8): generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), ArchivePage(), getPageMetadata()
### Community 221 - "CreateHealthLogDto"
Cohesion: 0.29
Nodes (6): CreateHealthLogDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString
### Community 223 - "Shabnam Font README"
Cohesion: 0.67
Nodes (3): Shabnam Font README, Shabnam Font Sample, Vazir Font
### Community 232 - "AdminService"
Cohesion: 0.13
Nodes (3): Post, AdminService, Injectable
### Community 238 - "AddressDto"
Cohesion: 0.29
Nodes (7): AddressDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString
### Community 239 - "blog/page.tsx"
Cohesion: 0.50
Nodes (4): Blog(), generateMetadata(), getBlogs(), BlogPage()
### Community 243 - "app.e2e-spec.js"
Cohesion: 0.50
Nodes (3): app_module_1, supertest_1, testing_1
### Community 297 - "ValidateCouponDto"
Cohesion: 0.50
Nodes (4): IsNotEmpty, IsNumber, IsString, ValidateCouponDto
### Community 300 - "app/page.tsx"
Cohesion: 0.67
Nodes (3): generateMetadata(), getHomeData(), Home()
### Community 302 - "RedisModule"
Cohesion: 0.67
Nodes (3): RedisModule, Global, Module
### Community 305 - "RouteErrorBoundary"
Cohesion: 0.22
Nodes (3): Props, RouteErrorBoundary, State
### Community 310 - "admin.module.ts"
Cohesion: 0.05
Nodes (26): AdminModule, Module, BlogQuery, BlogsService, Injectable, CategoriesService, CategoryQuery, Injectable (+18 more)
### Community 310 - "ReportsController"
Cohesion: 0.16
Nodes (9): ReportsController, ApiBearerAuth, ApiOperation, ApiTags, Controller, Get, UseGuards, ReportsService (+1 more)
## Knowledge Gaps
- **1266 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1261 more)
- **1259 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1254 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **98 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
- **103 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 `BannersService`, `CmsController`, `B2BService`, `reviews.controller.ts`, `tickets.controller.ts`, `SslController`, `SettingsController`, `IngredientsService`, `ProductsService`, `PrescriptionsService`, `SmartAdvisorService`, `TestimonialsService`, `ContactService`, `PaymentController`, `WholesaleApplyDto`, `JwtAuthGuard`?**
- **Why does `Roles()` connect `Roles` to `BannersService`, `ZibalService`, `CmsController`, `B2BService`, `ReviewsService`, `tickets.controller.ts`, `SslController`, `IngredientsService`, `payment.controller.ts`, `ProductsService`, `PrescriptionsService`, `SmartAdvisorService`, `TestimonialsService`, `WholesaleService`, `reviews.controller.ts`, `JwtAuthGuard`?**
_High betweenness centrality (0.052) - this node is a cross-community bridge._
- **Why does `ApiResponse` connect `src/services/api.ts` to `OrdersService`, `BlogsController`, `PetsController`, `UsersService`, `WikiController`, `HomeController`, `ProductsService`, `auth.service.ts`?**
- **Why does `ApiResponse` connect `src/services/api.ts` to `BlogsController`, `PetsController`, `UsersController`, `WikiController`, `OrdersService`, `HomeController`, `ProductsService`, `AuthController`?**
_High betweenness centrality (0.046) - this node is a cross-community bridge._
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `CmsController`, `reviews.controller.ts`, `tickets.controller.ts`, `UsersService`, `WikiService`, `admin.service.ts`, `PrismaService`, `pets/pets.controller.ts`, `orders.service.ts`, `ProductsService`, `auth.service.ts`, `admin.module.ts`, `PaginationDto`?**
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `CategoriesController`, `CmsController`, `tickets.controller.ts`, `admin.service.ts`, `DoctorsService`, `payment.controller.ts`, `admin.module.ts`, `UsersService`, `pets/pets.controller.ts`, `OrdersService`, `ProductsService`, `CreateVideoDto`, `auth.controller.ts`, `ReportsController`, `reviews.controller.ts`?**
_High betweenness centrality (0.024) - this node is a cross-community bridge._
- **What connects `$schema`, `collection`, `sourceRoot` to the rest of the system?**
_1266 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `OrdersService` be split into smaller, more focused modules?**
_Cohesion score 0.07171717171717172 - nodes in this community are weakly interconnected._
_1259 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `productService.ts` be split into smaller, more focused modules?**
_Cohesion score 0.05888376856118792 - nodes in this community are weakly interconnected._
_Cohesion score 0.08078431372549019 - nodes in this community are weakly interconnected._
- **Should `CmsController` be split into smaller, more focused modules?**
_Cohesion score 0.0899854862119013 - nodes in this community are weakly interconnected._
_Cohesion score 0.0899854862119013 - nodes in this community are weakly interconnected._
- **Should `app.module.ts` be split into smaller, more focused modules?**
_Cohesion score 0.08456659619450317 - nodes in this community are weakly interconnected._

View File

@ -0,0 +1 @@
{"nodes": [{"id": "$graphify-root$_scripts_deploy_sh", "label": "deploy.sh", "file_type": "code", "source_file": "scripts/deploy.sh", "source_location": "L1", "metadata": {"language": "bash", "kind": "file"}}, {"id": "$graphify-root$_scripts_deploy_sh__entry", "label": "deploy.sh script", "file_type": "code", "source_file": "scripts/deploy.sh", "source_location": "L1", "metadata": {"language": "bash", "kind": "bash_entrypoint"}}, {"id": "$graphify-root$_scripts_deploy_docker_buildkit", "label": "DOCKER_BUILDKIT", "file_type": "code", "source_file": "scripts/deploy.sh", "source_location": "L4", "metadata": {"language": "bash", "kind": "code"}}, {"id": "$graphify-root$_scripts_deploy_compose_docker_cli_build", "label": "COMPOSE_DOCKER_CLI_BUILD", "file_type": "code", "source_file": "scripts/deploy.sh", "source_location": "L5", "metadata": {"language": "bash", "kind": "code"}}], "edges": [{"source": "$graphify-root$_scripts_deploy_sh", "target": "$graphify-root$_scripts_deploy_sh__entry", "relation": "contains", "confidence": "EXTRACTED", "source_file": "scripts/deploy.sh", "source_location": "L1", "weight": 1.0}, {"source": "$graphify-root$_scripts_deploy_sh", "target": "$graphify-root$_scripts_deploy_docker_buildkit", "relation": "defines", "confidence": "EXTRACTED", "source_file": "scripts/deploy.sh", "source_location": "L4", "weight": 1.0}, {"source": "$graphify-root$_scripts_deploy_sh", "target": "$graphify-root$_scripts_deploy_compose_docker_cli_build", "relation": "defines", "confidence": "EXTRACTED", "source_file": "scripts/deploy.sh", "source_location": "L5", "weight": 1.0}], "raw_calls": [{"language": "bash", "callee": "set", "caller_nid": "$graphify-root$_scripts_deploy_sh__entry", "source_file": "scripts/deploy.sh", "source_location": "L2"}, {"language": "bash", "callee": "docker", "caller_nid": "$graphify-root$_scripts_deploy_sh__entry", "source_file": "scripts/deploy.sh", "source_location": "L8"}, {"language": "bash", "callee": "true", "caller_nid": "$graphify-root$_scripts_deploy_sh__entry", "source_file": "scripts/deploy.sh", "source_location": "L8"}, {"language": "bash", "callee": "echo", "caller_nid": "$graphify-root$_scripts_deploy_sh__entry", "source_file": "scripts/deploy.sh", "source_location": "L18"}, {"language": "bash", "callee": "mkdir", "caller_nid": "$graphify-root$_scripts_deploy_sh__entry", "source_file": "scripts/deploy.sh", "source_location": "L38"}, {"language": "bash", "callee": "git", "caller_nid": "$graphify-root$_scripts_deploy_sh__entry", "source_file": "scripts/deploy.sh", "source_location": "L42"}, {"language": "bash", "callee": "cd", "caller_nid": "$graphify-root$_scripts_deploy_sh__entry", "source_file": "scripts/deploy.sh", "source_location": "L45"}, {"language": "bash", "callee": "cp", "caller_nid": "$graphify-root$_scripts_deploy_sh__entry", "source_file": "scripts/deploy.sh", "source_location": "L55"}, {"language": "bash", "callee": "sed", "caller_nid": "$graphify-root$_scripts_deploy_sh__entry", "source_file": "scripts/deploy.sh", "source_location": "L56"}, {"language": "bash", "callee": "sleep", "caller_nid": "$graphify-root$_scripts_deploy_sh__entry", "source_file": "scripts/deploy.sh", "source_location": "L76"}], "bash_sources": []}

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