All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 34s
Build & Deploy / 🧪 QA (push) Successful in 1m10s
Build & Deploy / 🏗️ Build (push) Successful in 2m13s
Build & Deploy / 🚀 Deploy (push) Successful in 27s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 45s
Build & Deploy / 🔔 Notify (push) Successful in 2s
188 lines
9.3 KiB
TypeScript
188 lines
9.3 KiB
TypeScript
import { Container, Badge, Heading } from '@/components/ui';
|
|
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
|
import { Metadata } from 'next';
|
|
import { notFound } from 'next/navigation';
|
|
import { standorteData, getStandortById } from '@/lib/standorte-data';
|
|
import { SITE_URL } from '@/lib/schema';
|
|
import Image from 'next/image';
|
|
import { MapPin, Phone, Mail, Navigation, CheckCircle2, ArrowUpRight } from 'lucide-react';
|
|
import { getButtonClasses, ButtonOverlay } from '@/components/ui/Button';
|
|
import TrackedLink from '@/components/analytics/TrackedLink';
|
|
import { AnimatedGlossyBorder } from '@/components/ui/AnimatedGlossyBorder';
|
|
|
|
interface PageProps {
|
|
params: Promise<{
|
|
locale: string;
|
|
slug: string;
|
|
}>;
|
|
}
|
|
|
|
export async function generateStaticParams() {
|
|
const locales = ['de', 'en'];
|
|
const params: { locale: string; slug: string }[] = [];
|
|
|
|
for (const locale of locales) {
|
|
for (const standort of standorteData) {
|
|
params.push({ locale, slug: standort.id });
|
|
}
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
|
const { locale, slug } = await params;
|
|
if (locale !== 'de' && locale !== 'en') return {};
|
|
|
|
const standort = getStandortById(slug);
|
|
if (!standort) return {};
|
|
|
|
return {
|
|
title: `${standort.name} | E-TIB Gruppe`,
|
|
description: standort.description[locale],
|
|
alternates: {
|
|
canonical: `${SITE_URL}/${locale}/standorte/${slug}`,
|
|
languages: {
|
|
de: `${SITE_URL}/de/standorte/${slug}`,
|
|
en: `${SITE_URL}/en/standorte/${slug}`,
|
|
'x-default': `${SITE_URL}/en/standorte/${slug}`,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
export default async function StandortDetail(props: { params: Promise<{ locale: string; slug: string }> }) {
|
|
const { locale, slug } = await props.params;
|
|
const safeLocale = locale === 'de' ? 'de' : 'en';
|
|
setRequestLocale(safeLocale);
|
|
const t = await getTranslations('StandardPage');
|
|
|
|
const standort = getStandortById(slug);
|
|
if (!standort) {
|
|
notFound();
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-col min-h-screen bg-white">
|
|
{/* Generic Hero Section to match standard pages */}
|
|
<section className="bg-primary-dark text-white pt-40 pb-12 md:pt-56 md:pb-16 min-h-[40vh] md:min-h-[45vh] flex flex-col justify-end relative overflow-hidden">
|
|
<div className="absolute inset-0 opacity-20 pointer-events-none">
|
|
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-accent/50 via-transparent to-transparent" />
|
|
</div>
|
|
<Container className="relative z-10">
|
|
<div className="max-w-4xl">
|
|
<Badge variant="accent" className="mb-4 md:mb-6 shadow-sm flex inline-flex items-center gap-2">
|
|
<MapPin className="w-4 h-4" />
|
|
{standort.type === 'hq' ? (safeLocale === 'de' ? 'Hauptsitz' : 'Headquarters') : (safeLocale === 'de' ? 'Niederlassung' : 'Branch')}
|
|
</Badge>
|
|
<Heading level={1} variant="white" className="mb-0 drop-shadow-sm">
|
|
{standort.name}
|
|
</Heading>
|
|
</div>
|
|
</Container>
|
|
</section>
|
|
|
|
{/* Main Content Area */}
|
|
<Container className="py-16 md:py-24">
|
|
{/* Excerpt/Lead paragraph */}
|
|
<div className="mb-16 max-w-4xl mx-auto">
|
|
<p className="text-xl md:text-2xl text-text-primary leading-relaxed font-medium border-l-4 border-primary pl-8 py-2 italic">
|
|
{standort.description[safeLocale]}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Main content in prose style */}
|
|
<div className="max-w-4xl mx-auto">
|
|
<div className="prose prose-lg md:prose-xl prose-neutral max-w-none prose-headings:font-heading prose-headings:font-bold prose-headings:tracking-tight prose-h1:text-4xl prose-h2:text-3xl prose-h2:mt-12 prose-h2:border-b prose-h2:border-neutral-200 prose-h2:pb-4 prose-h3:text-2xl prose-p:text-text-secondary prose-p:leading-relaxed prose-a:text-primary prose-a:font-medium prose-a:underline prose-a:underline-offset-4 prose-a:decoration-primary/30 hover:prose-a:decoration-primary prose-strong:text-text-primary prose-strong:font-semibold prose-li:text-text-secondary prose-ul:list-disc prose-ol:list-decimal">
|
|
|
|
<img className="rounded-2xl shadow-2xl my-12 max-w-full h-auto border border-neutral-100" src={standort.image} alt={standort.name} />
|
|
|
|
<h2 className="mt-16 mb-6 border-b border-neutral-100 pb-4">{safeLocale === 'de' ? 'Unsere Leistungen vor Ort' : 'Our Local Services'}</h2>
|
|
<ul className="list-none mb-8 space-y-3 text-base md:text-lg text-text-secondary font-medium max-w-3xl">
|
|
{standort.keyFeatures[safeLocale].map((feature, idx) => (
|
|
<li key={idx} className="relative pl-6 before:content-[''] before:absolute before:left-0 before:top-[0.6em] before:w-2 before:h-2 before:bg-primary/50 before:rounded-sm">
|
|
{feature}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<h2 className="mt-16 mb-6 border-b border-neutral-100 pb-4">{safeLocale === 'de' ? 'Kontakt & Anfahrt' : 'Contact & Directions'}</h2>
|
|
|
|
<p className="text-base md:text-lg text-text-secondary leading-[1.8] mb-6 font-medium max-w-3xl">
|
|
<strong>{safeLocale === 'de' ? 'Adresse:' : 'Address:'}</strong><br/>
|
|
{standort.address.street}<br/>
|
|
{standort.address.city}
|
|
</p>
|
|
<p className="text-base md:text-lg text-text-secondary leading-[1.8] mb-6 font-medium max-w-3xl">
|
|
<TrackedLink
|
|
href={`https://maps.google.com/?q=${encodeURIComponent(`${standort.address.street}, ${standort.address.city}`)}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-primary hover:text-primary-dark underline decoration-primary/30 underline-offset-4 hover:decoration-primary transition-all font-bold flex items-center gap-1 w-fit"
|
|
eventProperties={{ location: 'standort_maps_link', slug: standort.id }}
|
|
>
|
|
{safeLocale === 'de' ? 'In Google Maps öffnen' : 'Open in Google Maps'}
|
|
<ArrowUpRight className="w-4 h-4 inline-block" />
|
|
</TrackedLink>
|
|
</p>
|
|
|
|
<p className="text-base md:text-lg text-text-secondary leading-[1.8] mb-6 font-medium max-w-3xl">
|
|
<strong>{safeLocale === 'de' ? 'Telefon:' : 'Phone:'}</strong><br/>
|
|
<a href={`tel:${standort.contact.phone.replace(/[^0-9+]/g, '')}`} className="text-primary hover:text-primary-dark underline decoration-primary/30 underline-offset-4 hover:decoration-primary transition-all font-bold">
|
|
{standort.contact.phone}
|
|
</a>
|
|
</p>
|
|
|
|
<p className="text-base md:text-lg text-text-secondary leading-[1.8] mb-6 font-medium max-w-3xl">
|
|
<strong>Email:</strong><br/>
|
|
<a href={`mailto:${standort.contact.email}`} className="text-primary hover:text-primary-dark underline decoration-primary/30 underline-offset-4 hover:decoration-primary transition-all font-bold">
|
|
{standort.contact.email}
|
|
</a>
|
|
</p>
|
|
|
|
{standort.gallery && standort.gallery.length > 0 && (
|
|
<>
|
|
<h2 className="mt-16 mb-6 border-b border-neutral-100 pb-4">Galerie</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 not-prose my-12">
|
|
{standort.gallery.map((img, idx) => (
|
|
<div key={idx} className="relative aspect-[4/3] rounded-2xl overflow-hidden bg-neutral-100 border border-neutral-200 shadow-md hover:shadow-xl transition-shadow duration-300">
|
|
<Image src={img} alt={`Gallery ${idx + 1}`} fill className="object-cover transition-transform duration-700 hover:scale-105" />
|
|
</div>
|
|
))}
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Container>
|
|
|
|
{/* Support Section */}
|
|
<Container>
|
|
<div className="mt-12 mb-24 p-8 md:p-12 bg-primary-dark rounded-3xl text-white shadow-2xl relative overflow-hidden group animate-slight-fade-in-from-bottom">
|
|
<div className="absolute top-0 right-0 w-64 h-full bg-accent/5 -skew-x-12 translate-x-1/2 transition-transform group-hover:translate-x-1/3" />
|
|
<div className="relative z-10 max-w-2xl">
|
|
<h3 className="text-2xl md:text-3xl font-bold mb-4">{t('needHelp')}</h3>
|
|
<p className="text-lg text-white/70 mb-8">{t('supportTeamAvailable')}</p>
|
|
<TrackedLink
|
|
href={`/${locale}/${locale === 'de' ? 'kontakt' : 'contact'}`}
|
|
className={getButtonClasses('accent', 'lg')}
|
|
eventProperties={{
|
|
location: 'standort_page_support_cta',
|
|
page_slug: slug,
|
|
}}
|
|
>
|
|
<span className="relative z-10 flex items-center justify-center gap-2 transition-colors duration-500 group-hover/btn:text-primary-dark">
|
|
{t('contactUs')}
|
|
<span className="ml-2 transition-transform group-hover/btn:translate-x-1">
|
|
→
|
|
</span>
|
|
</span>
|
|
<ButtonOverlay variant="accent" />
|
|
</TrackedLink>
|
|
</div>
|
|
</div>
|
|
</Container>
|
|
</div>
|
|
);
|
|
}
|