canina/frontend/application/app/layout.tsx
parsa aghaei f0457b5748
Some checks failed
Deploy Canina / deploy (push) Successful in 2m14s
E2E Playwright Tests / Run Full E2E & Security Suites (push) Failing after 7s
feat: complete Google Search Console and Analytics integration across admin panel and storefront
2026-09-02 11:34:10 +03:30

256 lines
7.9 KiB
TypeScript

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<Metadata> {
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;
const templateStr =
position === 'prefix'
? `${brandName} ${separator} %s`
: position === 'none'
? '%s'
: `%s ${separator} ${brandName}`;
const isStaging =
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 {
metadataBase: new URL(siteUrl),
title: {
template: templateStr,
default: titleDefault,
},
description: descriptionDefault,
keywords: keywordsList,
authors: [{ name: brandName }],
creator: brandName,
publisher: brandName,
verification: config.googleSiteVerification ? {
google: config.googleSiteVerification,
} : undefined,
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,
}
},
alternates: {
canonical: '/',
},
formatDetection: {
email: false,
address: false,
telephone: false,
},
openGraph: {
title: titleDefault,
description: descriptionDefault,
url: siteUrl,
siteName: brandName,
locale: 'fa_IR',
type: 'website',
images: [
{
url: ogImg,
width: 1200,
height: 630,
alt: titleDefault,
},
],
},
twitter: {
card: 'summary_large_image',
title: titleDefault,
description: descriptionDefault,
images: [ogImg],
},
};
}
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const config = await getSeoConfig();
const orgSchema = generateOrganizationSchema({
name: 'کنینا ایران',
alternateName: 'Canina Iran (Canina pharma GmbH)',
url: 'https://canina.ir',
logo: 'https://canina.ir/assets/images/logo.png',
description: 'فروشگاه تخصصی و نماینده انحصاری مکمل‌های دارویی و تقویتی سگ و گربه کنینا آلمان با فرمولاسیون دارویی و تاییدیه بالینی سازمان دامپزشکی کشور.',
telephone: '+989211231517',
email: 'caninairan@gmail.com',
address: {
streetAddress: 'تهران، جردن، خیابان سعیدی، ساختمان کنینا، طبقه ۵، واحد ۱۹',
addressLocality: 'تهران',
addressRegion: 'تهران',
postalCode: '1967812345',
addressCountry: 'IR',
},
sameAs: [
'https://instagram.com/caninairan',
'https://t.me/caninairan',
'https://www.canina.de',
],
});
const webSiteSchema = {
'@context': 'https://schema.org',
'@type': 'WebSite',
'@id': 'https://canina.ir/#website',
url: 'https://canina.ir',
name: 'کنینا ایران | نماینده رسمی مکمل‌های دارویی Canina آلمان',
inLanguage: 'fa-IR',
publisher: {
'@id': 'https://canina.ir/#organization',
},
potentialAction: {
'@type': 'SearchAction',
target: 'https://canina.ir/shop?search={search_term_string}',
'query-input': 'required name=search_term_string',
},
};
const safeJsonLd = JSON.stringify([orgSchema, webSiteSchema]).replace(/</g, '\\u003c');
const gaId = config.googleAnalyticsId?.trim();
const isGaEnabled = Boolean(gaId && config.enableGoogleAnalytics !== false);
const gtmId = config.googleTagManagerId?.trim();
const isGtmEnabled = Boolean(gtmId && config.enableGoogleTagManager !== false);
const clarityId = config.clarityProjectId?.trim();
return (
<html lang="fa" dir="rtl" className={`${vazirmatn.variable} ${lalezar.variable}`} suppressHydrationWarning>
<body className="min-h-screen bg-medical-gray-50 font-sans" suppressHydrationWarning>
{/* Google Tag Manager (noscript fallback) */}
{isGtmEnabled && (
<noscript>
<iframe
src={`https://www.googletagmanager.com/ns.html?id=${gtmId}`}
height="0"
width="0"
style={{ display: 'none', visibility: 'hidden' }}
/>
</noscript>
)}
<NextTopLoader
color="#0284c7"
initialPosition={0.08}
crawlSpeed={200}
height={3}
crawl={true}
showSpinner={false}
easing="ease"
speed={200}
shadow="0 0 12px rgba(2,132,199,0.7), 0 0 4px #0284c7"
zIndex={99999}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: safeJsonLd }}
/>
<ClientLayout>
{children}
</ClientLayout>
{/* Google Tag Manager Script */}
{isGtmEnabled && (
<Script
id="google-tag-manager"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','${gtmId}');
`,
}}
/>
)}
{/* Google Analytics 4 (GA4) Scripts */}
{isGaEnabled && (
<>
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${gaId}`}
/>
<Script
id="google-analytics"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gaId}', {
page_path: window.location.pathname,
});
`,
}}
/>
</>
)}
{/* Microsoft Clarity Script */}
{clarityId && (
<Script
id="microsoft-clarity"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "${clarityId}");
`,
}}
/>
)}
</body>
</html>
);
}