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
All checks were successful
Deploy Canina / deploy (push) Successful in 1m44s
This commit is contained in:
parent
df634fdbf5
commit
70d58e7086
@ -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',
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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] ??
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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",
|
||||
}
|
||||
};
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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 />;
|
||||
|
||||
@ -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 />;
|
||||
}
|
||||
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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 />;
|
||||
}
|
||||
|
||||
|
||||
@ -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: [
|
||||
|
||||
@ -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";
|
||||
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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 />;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 />;
|
||||
}
|
||||
|
||||
|
||||
@ -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 = [
|
||||
|
||||
@ -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 />;
|
||||
}
|
||||
|
||||
|
||||
@ -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",
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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 (
|
||||
|
||||
267
frontend/application/lib/seo.ts
Normal file
267
frontend/application/lib/seo.ts
Normal 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,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -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
4529
graphify-out/2026-08-19/.graphify_analysis.json
Normal file
4529
graphify-out/2026-08-19/.graphify_analysis.json
Normal file
File diff suppressed because it is too large
Load Diff
296
graphify-out/2026-08-19/.graphify_labels.json
Normal file
296
graphify-out/2026-08-19/.graphify_labels.json
Normal 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"
|
||||
}
|
||||
1
graphify-out/2026-08-19/.graphify_semantic_marker
Normal file
1
graphify-out/2026-08-19/.graphify_semantic_marker
Normal file
@ -0,0 +1 @@
|
||||
{"output_tokens": 7105}
|
||||
1068
graphify-out/2026-08-19/GRAPH_REPORT.md
Normal file
1068
graphify-out/2026-08-19/GRAPH_REPORT.md
Normal file
File diff suppressed because it is too large
Load Diff
114673
graphify-out/2026-08-19/graph.json
Normal file
114673
graphify-out/2026-08-19/graph.json
Normal file
File diff suppressed because it is too large
Load Diff
3362
graphify-out/2026-08-19/manifest.json
Normal file
3362
graphify-out/2026-08-19/manifest.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -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._
|
||||
@ -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": []}
|
||||
2
graphify-out/cache/stat-index.json
vendored
2
graphify-out/cache/stat-index.json
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
14315
graphify-out/graph.json
14315
graphify-out/graph.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user