feat: implement compare functionality, checkout flow, and add 30 blog posts
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
import { Metadata } from 'next';
|
||||
import { notFound } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { MapPin, Clock, Phone, ArrowRight, ThermometerSun } from 'lucide-react';
|
||||
import { getCityBySlug, getAllCitySlugs, dolnySlaskCities } from '@/content/cities';
|
||||
import { HeroSection } from '@/components/sections/HeroSection';
|
||||
import { ProcessSteps } from '@/components/sections/ProcessSteps';
|
||||
import { PricingTable } from '@/components/sections/PricingTable';
|
||||
import { WhyUs } from '@/components/sections/WhyUs';
|
||||
import { FaqAccordion } from '@/components/sections/FaqAccordion';
|
||||
import { CtaBanner } from '@/components/sections/CtaBanner';
|
||||
import { Testimonials } from '@/components/sections/Testimonials';
|
||||
import { acProcessSteps } from '@/content/process';
|
||||
import { companyDetails } from '@/content/company';
|
||||
|
||||
interface Props {
|
||||
params: Promise<{ miasto: string }>;
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return getAllCitySlugs().map((c) => ({ miasto: c.slug }));
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const { miasto } = await params;
|
||||
const city = getCityBySlug(miasto);
|
||||
if (!city) return { title: 'Nie znaleziono' };
|
||||
return {
|
||||
title: city.seoTitle,
|
||||
description: city.seoDescription,
|
||||
alternates: { canonical: `/klimatyzacja/${city.slug}` },
|
||||
openGraph: {
|
||||
title: city.seoTitle,
|
||||
description: city.seoDescription,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default async function MiastoPage({ params }: Props) {
|
||||
const { miasto } = await params;
|
||||
const city = getCityBySlug(miasto);
|
||||
if (!city) notFound();
|
||||
|
||||
const otherCities = dolnySlaskCities.filter((c) => c.slug !== city.slug);
|
||||
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "LocalBusiness",
|
||||
"name": companyDetails.name,
|
||||
"telephone": companyDetails.phone,
|
||||
"email": companyDetails.email,
|
||||
"url": `${process.env.NEXT_PUBLIC_BASE_URL || 'https://thermcool.pl'}/klimatyzacja/${city.slug}`,
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": companyDetails.address.street,
|
||||
"addressLocality": companyDetails.address.city,
|
||||
"postalCode": companyDetails.address.zipCode,
|
||||
"addressCountry": "PL",
|
||||
},
|
||||
"areaServed": { "@type": "City", "name": city.name },
|
||||
"priceRange": "$$",
|
||||
};
|
||||
|
||||
return (
|
||||
<main>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||
/>
|
||||
|
||||
{/* HERO */}
|
||||
<HeroSection
|
||||
eyebrow={`Klimatyzacja ${city.name} · Dolny Śląsk · F-Gaz`}
|
||||
title={`Montaż klimatyzacji w ${city.nameDopelniacz}`}
|
||||
subtitle={city.heroSubtitle}
|
||||
ctaText="Zamów bezpłatną wycenę"
|
||||
ctaLink="/kontakt"
|
||||
secondaryCtaText="Zobacz klimatyzatory"
|
||||
secondaryCtaLink="/sklep"
|
||||
backgroundImage={city.heroImage}
|
||||
features={[
|
||||
'Bezpłatny audyt i dobór',
|
||||
'Uprawnienia F-Gaz',
|
||||
'Gwarancja do 5 lat',
|
||||
`Dojazd do ${city.nameDopelniacz2}`,
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* INTRO + FAKTY LOKALNE */}
|
||||
<section className="py-16 md:py-20 bg-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-start">
|
||||
<div>
|
||||
<span className="text-xs font-semibold uppercase tracking-wider text-accent font-heading">
|
||||
Klimatyzacja {city.name}
|
||||
</span>
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-primary font-heading mt-2 mb-4">
|
||||
Profesjonalny montaż klimatyzacji w {city.nameDopelniacz}
|
||||
</h2>
|
||||
<p className="text-text-muted leading-relaxed mb-4">{city.introText}</p>
|
||||
<p className="text-text-muted leading-relaxed">{city.localFacts}</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
{/* Highlights */}
|
||||
<div className="p-5 rounded-xl bg-slate-50 border border-border flex gap-4 items-start">
|
||||
<Clock className="size-5 text-accent shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="font-semibold text-primary text-sm">Czas montażu</p>
|
||||
<p className="text-text-muted text-sm mt-1">Standardowy montaż split w {city.nameDopelniacz} trwa 4–8 godzin. Multisplit — 1–2 dni robocze.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5 rounded-xl bg-slate-50 border border-border flex gap-4 items-start">
|
||||
<MapPin className="size-5 text-accent shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="font-semibold text-primary text-sm">Zasięg: {city.name} i okolice</p>
|
||||
<p className="text-text-muted text-sm mt-1">{city.distanceFromHQ === 'siedziba firmy' ? 'To miasto, w którym mieści się nasza siedziba.' : `Jesteśmy ${city.distanceFromHQ}. Obsługujemy miasto i okoliczne gminy w promieniu 30 km.`}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5 rounded-xl bg-accent-light border border-accent/20 flex gap-4 items-start">
|
||||
<Phone className="size-5 text-accent shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="font-semibold text-primary text-sm">Bezpłatna wycena</p>
|
||||
<p className="text-text-muted text-sm mt-1">Zadzwoń lub napisz — wyceniamy bezpłatnie, bez zobowiązań. Odpowiadamy w ciągu godziny.</p>
|
||||
<a href={`tel:${companyDetails.phoneRaw}`} className="inline-flex items-center gap-1.5 text-accent font-semibold text-sm mt-2 hover:underline">
|
||||
{companyDetails.phone} <ArrowRight className="size-3.5" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* HIGHLIGHT KLIMATYZACJI */}
|
||||
<section className="py-12 bg-slate-50 border-t border-b border-border">
|
||||
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||
<p className="text-base md:text-lg text-text-muted leading-relaxed italic">
|
||||
{city.acHighlight}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* PROCES MONTAŻU */}
|
||||
<ProcessSteps
|
||||
steps={acProcessSteps}
|
||||
eyebrow="Jak Działamy"
|
||||
title={`Montaż klimatyzacji w ${city.nameDopelniacz} — krok po kroku`}
|
||||
subtitle="Od pierwszego kontaktu do przekazania gotowej instalacji. Każdy etap pod kontrolą."
|
||||
/>
|
||||
|
||||
{/* CENNIK */}
|
||||
<PricingTable />
|
||||
|
||||
{/* POMPY CIEPŁA CTA */}
|
||||
<section className="py-16 bg-primary">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10 items-center">
|
||||
<div>
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-accent/20 border border-accent/30 text-white text-xs font-semibold uppercase tracking-wider mb-4 font-heading">
|
||||
<ThermometerSun className="size-3.5 text-accent" />
|
||||
Pompy ciepła {city.name}
|
||||
</div>
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-white font-heading leading-tight mb-4">
|
||||
Pompa ciepła w {city.nameDopelniacz}
|
||||
</h2>
|
||||
<p className="text-white/80 leading-relaxed mb-6">{city.heatPumpTeaser}</p>
|
||||
<Link href="/pompy-ciepla">
|
||||
<button className="btn btn-primary font-bold text-white flex items-center gap-2">
|
||||
Dowiedz się więcej o pompach ciepła
|
||||
<ArrowRight className="size-4" />
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{[
|
||||
{ label: 'Dofinansowanie', value: 'do 135 000 zł', sub: 'z programu Czyste Powietrze' },
|
||||
{ label: 'Oszczędność', value: 'do 60%', sub: 'niższe koszty ogrzewania' },
|
||||
{ label: 'Sprawność', value: 'COP 4–5', sub: 'na każdy 1 kW energii' },
|
||||
{ label: 'Gwarancja', value: '5 lat', sub: 'na urządzenie i montaż' },
|
||||
].map((item, idx) => (
|
||||
<div key={idx} className="p-4 rounded-xl bg-white/10 border border-white/20 text-center">
|
||||
<p className="text-xs text-white/60 uppercase tracking-wider font-heading mb-1">{item.label}</p>
|
||||
<p className="text-2xl font-bold text-accent font-heading">{item.value}</p>
|
||||
<p className="text-xs text-white/70 mt-1">{item.sub}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* DLACZEGO MY */}
|
||||
<WhyUs />
|
||||
|
||||
{/* OPINIE */}
|
||||
<Testimonials />
|
||||
|
||||
{/* FAQ */}
|
||||
<FaqAccordion category="klimatyzacja" />
|
||||
|
||||
{/* INNE MIASTA */}
|
||||
<section className="py-16 bg-slate-50 border-t border-border">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-8">
|
||||
<span className="text-xs font-semibold uppercase tracking-wider text-accent font-heading">
|
||||
Zasięg działania
|
||||
</span>
|
||||
<h2 className="text-xl md:text-2xl font-bold text-primary font-heading mt-2">
|
||||
Klimatyzacja na Dolnym Śląsku — inne miasta
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex flex-wrap justify-center gap-3">
|
||||
{otherCities.map((c) => (
|
||||
<Link
|
||||
key={c.slug}
|
||||
href={`/klimatyzacja/${c.slug}`}
|
||||
className="px-4 py-2 rounded-full bg-white border border-border text-sm font-medium text-primary hover:border-accent hover:text-accent transition-colors shadow-sm"
|
||||
>
|
||||
Klimatyzacja {c.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* KOŃCOWE CTA */}
|
||||
<CtaBanner
|
||||
title={`Potrzebujesz klimatyzacji w ${city.nameDopelniacz}?`}
|
||||
subtitle="Odpowiemy w ciągu godziny i umówimy bezpłatny audyt w dogodnym terminie."
|
||||
ctaText="Zadzwoń lub napisz"
|
||||
ctaLink="/kontakt"
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -11,9 +11,10 @@ import { WhyUs } from '@/components/sections/WhyUs';
|
||||
import { FaqAccordion } from '@/components/sections/FaqAccordion';
|
||||
import { CtaBanner } from '@/components/sections/CtaBanner';
|
||||
import { acProcessSteps } from '@/content/process';
|
||||
|
||||
import { dolnySlaskCities } from '@/content/cities';
|
||||
import Link from 'next/link';
|
||||
export const metadata: Metadata = {
|
||||
title: 'Montaż i Serwis Klimatyzacji | AirProject',
|
||||
title: 'Montaż i Serwis Klimatyzacji | ThermCool',
|
||||
description: 'Profesjonalny montaż klimatyzacji typu split i multisplit. Posiadamy uprawnienia F-gaz. Bezpłatny audyt, dobór urządzeń i sprawdzony serwis.',
|
||||
alternates: {
|
||||
canonical: '/klimatyzacja',
|
||||
@@ -82,6 +83,34 @@ export default function KlimatyzacjaPage() {
|
||||
|
||||
<FaqAccordion category="klimatyzacja" />
|
||||
|
||||
{/* Sekcja miast dla SEO */}
|
||||
<section id="miasta" className="py-16 bg-slate-50 border-t border-border">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-10">
|
||||
<span className="text-xs font-semibold uppercase tracking-wider text-accent font-heading">
|
||||
Zasięg działania
|
||||
</span>
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-primary font-heading mt-2">
|
||||
Montaż klimatyzacji na Dolnym Śląsku
|
||||
</h2>
|
||||
<p className="text-text-muted mt-3 max-w-xl mx-auto">
|
||||
Działamy na terenie całego Dolnego Śląska. Wybierz swoje miasto i dowiedz się więcej:
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap justify-center gap-3">
|
||||
{dolnySlaskCities.map((city) => (
|
||||
<Link
|
||||
key={city.slug}
|
||||
href={`/klimatyzacja/${city.slug}`}
|
||||
className="px-4 py-2 rounded-full bg-white border border-border text-sm font-medium text-primary hover:border-accent hover:text-accent transition-colors shadow-sm"
|
||||
>
|
||||
Klimatyzacja {city.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<CtaBanner
|
||||
title="Niestandardowa instalacja?"
|
||||
subtitle="Potrzebujesz klimatyzacji w zabytkowym budynku lub masz specyficzne wymagania architektoniczne? Skontaktuj się z nami po indywidualną wycenę."
|
||||
|
||||
Reference in New Issue
Block a user