213 lines
6.7 KiB
TypeScript
213 lines
6.7 KiB
TypeScript
import type { Metadata } from 'next';
|
|
import './globals.css';
|
|
import NextTopLoader from 'nextjs-toploader';
|
|
import ClientLayout from './ClientLayout';
|
|
import AnalyticsDeferred from '../components/AnalyticsDeferred';
|
|
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>
|
|
<head>
|
|
<link rel="preconnect" href="https://api.canina.ir" crossOrigin="anonymous" />
|
|
<link rel="dns-prefetch" href="https://api.canina.ir" />
|
|
<link rel="preconnect" href="https://www.googletagmanager.com" crossOrigin="anonymous" />
|
|
<link rel="dns-prefetch" href="https://www.googletagmanager.com" />
|
|
<link rel="preconnect" href="https://scripts.clarity.ms" crossOrigin="anonymous" />
|
|
<link rel="dns-prefetch" href="https://scripts.clarity.ms" />
|
|
</head>
|
|
<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>
|
|
|
|
{/* Deferred Analytics (GTM, GA4, Clarity) to unblock Main Thread */}
|
|
<AnalyticsDeferred
|
|
gtmId={isGtmEnabled ? gtmId : undefined}
|
|
gaId={isGaEnabled ? gaId : undefined}
|
|
clarityId={clarityId}
|
|
/>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|