diff --git a/backend/src/settings/settings.service.ts b/backend/src/settings/settings.service.ts index 48c5d4d..4bb0af4 100644 --- a/backend/src/settings/settings.service.ts +++ b/backend/src/settings/settings.service.ts @@ -571,6 +571,28 @@ export class SettingsService implements OnModuleInit { if (obj.ogImageUrl) { updates['ogImageUrl'] = String(obj.ogImageUrl); } + if ('googleSiteVerification' in obj) { + updates['googleSiteVerification'] = String(obj.googleSiteVerification ?? ''); + updates['google_site_verification'] = String(obj.googleSiteVerification ?? ''); + } + if ('googleAnalyticsId' in obj) { + updates['googleAnalyticsId'] = String(obj.googleAnalyticsId ?? ''); + updates['ga_measurement_id'] = String(obj.googleAnalyticsId ?? ''); + } + if ('googleTagManagerId' in obj) { + updates['googleTagManagerId'] = String(obj.googleTagManagerId ?? ''); + updates['gtm_id'] = String(obj.googleTagManagerId ?? ''); + } + if ('enableGoogleAnalytics' in obj) { + updates['enableGoogleAnalytics'] = String(Boolean(obj.enableGoogleAnalytics)); + } + if ('enableGoogleTagManager' in obj) { + updates['enableGoogleTagManager'] = String(Boolean(obj.enableGoogleTagManager)); + } + if ('clarityProjectId' in obj) { + updates['clarityProjectId'] = String(obj.clarityProjectId ?? ''); + updates['clarity_project_id'] = String(obj.clarityProjectId ?? ''); + } if (Object.keys(updates).length > 0) { await this.updateBulkUiTexts(updates); diff --git a/frontend/admin-panel/src/pages/SeoSettingsPage.tsx b/frontend/admin-panel/src/pages/SeoSettingsPage.tsx index 1be3dc2..cf98671 100644 --- a/frontend/admin-panel/src/pages/SeoSettingsPage.tsx +++ b/frontend/admin-panel/src/pages/SeoSettingsPage.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { Globe, Save, Sliders, Monitor, Sparkles } from 'lucide-react'; +import { Globe, Save, Sliders, Monitor, Sparkles, Search, BarChart3, Code2, CheckCircle2, HelpCircle, Activity, ExternalLink, ShieldCheck } from 'lucide-react'; import { toast } from 'react-hot-toast'; import api from '../services/api'; import Spinner from '../components/ui/Spinner'; @@ -19,6 +19,12 @@ export default function SeoSettingsPage() { keywords: 'مکمل سگ, مکمل گربه, داروی حیوانات, Canina Germany', canonicalBaseUrl: 'https://canina.ir', ogImageUrl: '/assets/images/regenerated_image_1779109861747.png', + googleSiteVerification: '', + googleAnalyticsId: '', + googleTagManagerId: '', + enableGoogleAnalytics: true, + enableGoogleTagManager: true, + clarityProjectId: '', }); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); @@ -41,6 +47,12 @@ export default function SeoSettingsPage() { keywords: data.keywords || prev.keywords, canonicalBaseUrl: data.canonicalBaseUrl || prev.canonicalBaseUrl, ogImageUrl: data.ogImageUrl || prev.ogImageUrl, + googleSiteVerification: data.googleSiteVerification || data.google_site_verification || '', + googleAnalyticsId: data.googleAnalyticsId || data.ga_measurement_id || '', + googleTagManagerId: data.googleTagManagerId || data.gtm_id || '', + enableGoogleAnalytics: data.enableGoogleAnalytics !== undefined ? Boolean(data.enableGoogleAnalytics) : true, + enableGoogleTagManager: data.enableGoogleTagManager !== undefined ? Boolean(data.enableGoogleTagManager) : true, + clarityProjectId: data.clarityProjectId || data.clarity_project_id || '', })); } }) @@ -286,6 +298,279 @@ export default function SeoSettingsPage() { + {/* Section 3: Google Services & Webmaster Analytics */} +
+
+
+ +

+ سرویس‌های تحلیل و وبمستر گوگل (Google Services & Analytics) +

+
+ + اتصال آنی به فرانت‌استور (Zero Build Delay) ⚡ + +
+ + {/* Grid of Webmaster Cards */} +
+ + {/* Google Search Console Card */} +
+
+
+
+ GSC +
+
+

+ گوگل سرچ کنسول (Google Search Console) +

+

تأیید مالکیت دامنه در نتایج جستجوی گوگل

+
+
+ {seo.googleSiteVerification ? ( + + + متصل + + ) : ( + + تنظیم نشده + + )} +
+ +
+ + { + let val = e.target.value.trim(); + // Auto-extract content if user pasted full + const match = val.match(/content=["']([^"']+)["']/); + if (match && match[1]) { + val = match[1]; + } + setSeo({ ...seo, googleSiteVerification: val }); + }} + placeholder="مثال: abcdef1234567890 یا کل تگ " + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-blue-500 outline-none text-xs font-mono" + dir="ltr" + /> +

+ می‌توانید کل تگ <meta name="google-site-verification"...> یا فقط مقدار داخل content را پیست کنید. +

+
+ + {seo.googleSiteVerification && ( +
+ متاتگ تزریق شده در سایت: + + <meta name="google-site-verification" content="{seo.googleSiteVerification}" /> + +
+ )} +
+ + {/* Google Analytics 4 (GA4) Card */} +
+
+
+
+ GA4 +
+
+

+ گوگل آنالیتیکس ۴ (Google Analytics 4) +

+

رهگیری بازدیدها، نرخ تبدیل و رفتار مشتریان

+
+
+ +
+ +
+ + setSeo({ ...seo, googleAnalyticsId: e.target.value.trim() })} + placeholder="G-XXXXXXXXXX" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-amber-500 outline-none text-xs font-mono font-bold" + dir="ltr" + /> +

+ این شناسه با پیشوند G- از مسیر Admin > Data Streams در گوگل آنالیتیکس قابل دریافت است. +

+
+ +
+ {seo.googleAnalyticsId && seo.enableGoogleAnalytics !== false ? ( + + + اسکریپت gtag.js فعال و در حال رهگیری است + + ) : ( + + {!seo.googleAnalyticsId ? 'شناسه GA4 هنوز ثبت نشده است.' : 'آمارگیر غیرفعال شده است.'} + + )} +
+
+ + {/* Google Tag Manager (GTM) Card */} +
+
+
+
+ GTM +
+
+

+ گوگل تگ منیجر (Google Tag Manager) +

+

مدیریت متمرکز تگ‌ها، ایونت‌ها و تبلیغات گوگل ادز

+
+
+ +
+ +
+ + setSeo({ ...seo, googleTagManagerId: e.target.value.trim() })} + placeholder="GTM-XXXXXXX" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-blue-500 outline-none text-xs font-mono font-bold" + dir="ltr" + /> +

+ شناسه با پیشوند GTM- در بالای داشبورد کانتینر تگ منیجر درج شده است. +

+
+ +
+ {seo.googleTagManagerId && seo.enableGoogleTagManager !== false ? ( + + + کانتینر GTM در Head و Noscript لود می‌شود + + ) : ( + + {!seo.googleTagManagerId ? 'شناسه GTM ثبت نشده است.' : 'تگ منیجر غیرفعال شده است.'} + + )} +
+
+ + {/* Microsoft Clarity Card */} +
+
+
+
+ MS +
+
+

+ مایکروسافت کلاریتی (Microsoft Clarity) +

+

ضبط جلسات، نقشه حرارتی (Heatmap) و تحلیل کلیک‌ها

+
+
+ {seo.clarityProjectId ? ( + + + فعال + + ) : ( + + اختیاری + + )} +
+ +
+ + setSeo({ ...seo, clarityProjectId: e.target.value.trim() })} + placeholder="مثال: abcde12345" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-orange-500 outline-none text-xs font-mono" + dir="ltr" + /> +

+ ابزار کاملاً رایگان مایکروسافت برای مشاهده رفتار خرید کاربران در صفحه محصول و چک‌اوت. +

+
+
+ +
+
+
diff --git a/frontend/admin-panel/src/types/admin.ts b/frontend/admin-panel/src/types/admin.ts index 3bf0fa0..fbf8f71 100644 --- a/frontend/admin-panel/src/types/admin.ts +++ b/frontend/admin-panel/src/types/admin.ts @@ -185,6 +185,12 @@ export interface SeoSettings { keywords: string; canonicalBaseUrl: string; ogImageUrl: string; + googleSiteVerification?: string; + googleAnalyticsId?: string; + googleTagManagerId?: string; + enableGoogleAnalytics?: boolean; + enableGoogleTagManager?: boolean; + clarityProjectId?: string; } export interface FinancialSettings { diff --git a/frontend/application/app/layout.tsx b/frontend/application/app/layout.tsx index 211288c..0432385 100644 --- a/frontend/application/app/layout.tsx +++ b/frontend/application/app/layout.tsx @@ -1,9 +1,11 @@ import type { Metadata } from 'next'; +import Script from 'next/script'; import './globals.css'; import NextTopLoader from 'nextjs-toploader'; import ClientLayout from './ClientLayout'; import { getSeoConfig } from '../lib/seo'; import { vazirmatn, lalezar } from '../lib/fonts'; +import { generateOrganizationSchema } from '../lib/schema'; export async function generateMetadata(): Promise { const config = await getSeoConfig(); @@ -43,6 +45,9 @@ export async function generateMetadata(): Promise { authors: [{ name: brandName }], creator: brandName, publisher: brandName, + verification: config.googleSiteVerification ? { + google: config.googleSiteVerification, + } : undefined, robots: isStaging ? { index: false, follow: false, @@ -96,13 +101,13 @@ export async function generateMetadata(): Promise { }; } -import { generateOrganizationSchema } from '../lib/schema'; - -export default function RootLayout({ +export default async function RootLayout({ children, }: { children: React.ReactNode; }) { + const config = await getSeoConfig(); + const orgSchema = generateOrganizationSchema({ name: 'کنینا ایران', alternateName: 'Canina Iran (Canina pharma GmbH)', @@ -144,9 +149,29 @@ export default function RootLayout({ const safeJsonLd = JSON.stringify([orgSchema, webSiteSchema]).replace(/ + {/* Google Tag Manager (noscript fallback) */} + {isGtmEnabled && ( +