219 lines
7.2 KiB
TypeScript
219 lines
7.2 KiB
TypeScript
import { Footer } from '@/components/layout/Footer';
|
|
import { Header } from '@/components/layout/Header';
|
|
import JsonLd from '@/components/JsonLd';
|
|
import SkipLink from '@/components/SkipLink';
|
|
import AnalyticsShell from '@/components/analytics/AnalyticsShell';
|
|
import { Metadata, Viewport } from 'next';
|
|
import { NextIntlClientProvider } from 'next-intl';
|
|
import { getMessages } from 'next-intl/server';
|
|
import '../../styles/globals.css';
|
|
import { SITE_URL } from '@/lib/schema';
|
|
import FeedbackClientWrapper from '@/components/FeedbackClientWrapper';
|
|
|
|
import { setRequestLocale } from 'next-intl/server';
|
|
import { Inter } from 'next/font/google';
|
|
import { mapFileSlugToTranslated } from '@/lib/slugs';
|
|
import { cookies } from 'next/headers';
|
|
|
|
import { TransitionProvider } from '@/components/providers/TransitionProvider';
|
|
|
|
import { DynamicInitialLoader as InitialLoader } from '@/components/providers/DynamicInitialLoader';
|
|
import { DynamicMobileBottomNav as MobileBottomNav } from '@/components/layout/DynamicMobileBottomNav';
|
|
|
|
import { DynamicCorporateBackground as CorporateBackground } from '@/components/decorations/DynamicCorporateBackground';
|
|
const corporateBackground = null; // Removed to prevent line shifting
|
|
|
|
const inter = Inter({
|
|
subsets: ['latin'],
|
|
display: 'swap',
|
|
variable: '--font-inter',
|
|
});
|
|
|
|
export async function generateMetadata(props: {
|
|
params: Promise<{ locale: string }>;
|
|
}): Promise<Metadata> {
|
|
const params = await props.params;
|
|
const { locale } = params;
|
|
|
|
const baseUrl = SITE_URL;
|
|
|
|
return {
|
|
title: {
|
|
template: '%s | E-TIB',
|
|
default: 'E-TIB | Die Experten für Kabelnetzbau',
|
|
},
|
|
description: 'Ihr Partner für Kabelleitungsnetzbau, Horizontalspülbohrungen, Planung und Vermessung in Guben und überregional.',
|
|
metadataBase: new URL(baseUrl),
|
|
manifest: '/manifest.webmanifest',
|
|
alternates: {
|
|
canonical: `/${locale}`,
|
|
languages: {
|
|
'de': `/de`,
|
|
'en': `/en`,
|
|
'x-default': `/de`,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
export const viewport: Viewport = {
|
|
width: 'device-width',
|
|
initialScale: 1,
|
|
maximumScale: 5,
|
|
userScalable: true,
|
|
viewportFit: 'cover',
|
|
themeColor: '#117c61',
|
|
};
|
|
|
|
export async function generateStaticParams() {
|
|
return [{ locale: 'de' }, { locale: 'en' }];
|
|
}
|
|
|
|
export default async function Layout(props: {
|
|
children: React.ReactNode;
|
|
params: Promise<{ locale: string }>;
|
|
}) {
|
|
const params = await props.params;
|
|
const { locale } = params;
|
|
const { children } = props;
|
|
const supportedLocales = ['en', 'de'];
|
|
const localeStr = (typeof locale === 'string' ? locale : '').trim();
|
|
const safeLocale = supportedLocales.includes(localeStr) ? localeStr : 'en';
|
|
|
|
setRequestLocale(safeLocale);
|
|
|
|
let messages: Record<string, any> = {};
|
|
try {
|
|
messages = await getMessages();
|
|
} catch (err) {
|
|
console.error('[Layout] Failed to load messages:', err);
|
|
messages = {};
|
|
}
|
|
|
|
const navLinks = [
|
|
{
|
|
label: safeLocale === 'de' ? 'Kompetenzen' : 'Competencies',
|
|
url: `/${safeLocale}/${await mapFileSlugToTranslated('kompetenzen', safeLocale)}`
|
|
},
|
|
{
|
|
label: safeLocale === 'de' ? 'Über uns' : 'About Us',
|
|
url: `/${safeLocale}/${await mapFileSlugToTranslated('ueber-uns', safeLocale)}`,
|
|
children: [
|
|
{ label: safeLocale === 'de' ? 'Firma' : 'Company', url: `/${safeLocale}/${await mapFileSlugToTranslated('ueber-uns', safeLocale)}` },
|
|
{ label: safeLocale === 'de' ? 'Unser Team' : 'Our Team', url: `/${safeLocale}/team` },
|
|
{ label: safeLocale === 'de' ? 'Zertifikate' : 'Certificates', url: `/${safeLocale}/zertifikate` },
|
|
{ label: safeLocale === 'de' ? 'Standorte' : 'Locations', url: `/${safeLocale}/standorte` }
|
|
]
|
|
},
|
|
{
|
|
label: safeLocale === 'de' ? 'Referenzen' : 'References',
|
|
url: `/${safeLocale}/referenzen`
|
|
},
|
|
{
|
|
label: safeLocale === 'de' ? 'Karriere' : 'Career',
|
|
url: `/${safeLocale}/${safeLocale === 'de' ? 'karriere' : 'career'}`
|
|
},
|
|
{
|
|
label: safeLocale === 'de' ? 'Messen' : 'Events',
|
|
url: `/${safeLocale}/${await mapFileSlugToTranslated('messen', safeLocale)}`
|
|
}
|
|
];
|
|
|
|
const companyInfo = {
|
|
contactEmail: 'info@e-tib.com',
|
|
contactPhone: '+49 (0) 3561 / 68577 33',
|
|
address: 'Gewerbestraße 22\n03172 Guben'
|
|
};
|
|
|
|
// Pick only the namespaces required by client components to reduce the hydration payload size
|
|
const clientKeys = [
|
|
'Footer',
|
|
'Navigation',
|
|
'Contact',
|
|
'Products',
|
|
'Team',
|
|
'Home',
|
|
'Error',
|
|
'StandardPage',
|
|
'Brochure',
|
|
'JobListingBlock',
|
|
'CallToAction',
|
|
'InteractiveGermanyMap',
|
|
'ReferencesSlider',
|
|
'CompanyTimeline',
|
|
'TeamGrid',
|
|
'AISearch',
|
|
'GrowthChart',
|
|
];
|
|
const clientMessages: Record<string, any> = {};
|
|
for (const key of clientKeys) {
|
|
if (messages[key]) {
|
|
clientMessages[key] = messages[key];
|
|
}
|
|
}
|
|
|
|
const { getServerAppServices } = await import('@/lib/services/create-services.server');
|
|
const serverServices = getServerAppServices();
|
|
|
|
try {
|
|
// Disable analytics in CI to prevent console noise/score penalties
|
|
if (process.env.NEXT_PUBLIC_CI === 'true') {
|
|
// Skip setting server context for analytics in CI
|
|
}
|
|
|
|
// Server-side analytics tracking removed to prevent duplicate/empty events.
|
|
// Client-side AnalyticsProvider handles all pageviews.
|
|
} catch {
|
|
if (process.env.NODE_ENV !== 'production' || !process.env.CI) {
|
|
console.warn(
|
|
'[Layout] Static generation detected or headers unavailable, skipping server-side analytics context',
|
|
);
|
|
}
|
|
}
|
|
|
|
// Read directly from process.env — bypasses all abstraction to guarantee correctness
|
|
const feedbackEnabled = process.env.NEXT_PUBLIC_FEEDBACK_ENABLED === 'true';
|
|
const hasSeenLoader = true; // Disabled to allow static generation and instant LCP (Lighthouse 100)
|
|
|
|
return (
|
|
<html
|
|
lang={safeLocale}
|
|
className={`scroll-smooth overflow-x-hidden ${inter.variable}`}
|
|
data-scroll-behavior="smooth"
|
|
suppressHydrationWarning
|
|
>
|
|
<head>
|
|
<link rel="icon" href="/favicon.ico" sizes="any" />
|
|
<link rel="apple-touch-icon" href="/apple-icon.png" sizes="180x180" />
|
|
|
|
</head>
|
|
<body className="relative flex flex-col min-h-screen font-sans antialiased overflow-x-hidden selection:bg-primary/90 selection:text-white" suppressHydrationWarning>
|
|
<NextIntlClientProvider messages={clientMessages} locale={safeLocale}>
|
|
<TransitionProvider>
|
|
<CorporateBackground />
|
|
{!hasSeenLoader && <InitialLoader shouldShowLoader={!hasSeenLoader} />}
|
|
{/* PageTransitionShutter loaded dynamically in TransitionProvider */}
|
|
<SkipLink />
|
|
<Header navLinks={navLinks} />
|
|
<MobileBottomNav navLinks={navLinks} currentLocale={safeLocale} />
|
|
|
|
<main
|
|
id="main-content"
|
|
className="flex-grow overflow-visible"
|
|
tabIndex={-1}
|
|
>
|
|
{children}
|
|
</main>
|
|
|
|
<Footer companyInfo={companyInfo} />
|
|
<JsonLd />
|
|
<AnalyticsShell />
|
|
|
|
<FeedbackClientWrapper />
|
|
</TransitionProvider>
|
|
</NextIntlClientProvider>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|