feat: Zaktualizuj sekcję hero, nawigację, kalkulator i formularz zgłoszenia awarii

This commit is contained in:
MichalC
2026-06-28 15:03:36 +02:00
parent 002a325dfb
commit 59808f1243
22 changed files with 1465 additions and 345 deletions
+9 -3
View File
@@ -10,9 +10,10 @@ const resend = new Resend(process.env.RESEND_API_KEY);
const contactFormSchema = z.object({
name: z.string().min(2, "Imię musi mieć min. 2 znaki"),
phone: z.string().min(9, "Wprowadź poprawny numer telefonu"),
email: z.string().email("Niepoprawny adres e-mail"),
subject: z.string().min(1, "Wybierz temat"),
message: z.string().min(10, "Wiadomość musi mieć min. 10 znaków"),
email: z.union([z.literal(''), z.string().email("Niepoprawny adres e-mail")]).optional(),
city: z.string().min(1, "Wybierz miasto"),
subject: z.string().optional(),
message: z.string().optional(),
consent: z.boolean().refine((val) => val === true, "Zgoda RODO jest wymagana"),
});
@@ -23,6 +24,7 @@ export async function submitContactForm(prevState: any, formData: FormData) {
name: formData.get("name"),
phone: formData.get("phone"),
email: formData.get("email"),
city: formData.get("city"),
subject: formData.get("subject"),
message: formData.get("message"),
consent: formData.get("consent") === "true" || formData.get("consent") === "on",
@@ -57,6 +59,10 @@ export async function submitContactForm(prevState: any, formData: FormData) {
<td style="padding: 8px 0; border-bottom: 1px solid #f1f5f9;"><strong>E-mail:</strong></td>
<td style="padding: 8px 0; border-bottom: 1px solid #f1f5f9;"><a href="mailto:${validatedData.email}" style="color: #0F2A47; text-decoration: none;">${validatedData.email}</a></td>
</tr>
<tr>
<td style="padding: 8px 0; border-bottom: 1px solid #f1f5f9;"><strong>Miasto:</strong></td>
<td style="padding: 8px 0; border-bottom: 1px solid #f1f5f9;">${validatedData.city}</td>
</tr>
<tr>
<td style="padding: 8px 0; border-bottom: 1px solid #f1f5f9;"><strong>Temat:</strong></td>
<td style="padding: 8px 0; border-bottom: 1px solid #f1f5f9;">${validatedData.subject}</td>
+37
View File
@@ -7,6 +7,8 @@ import { Testimonials } from "@/components/sections/Testimonials";
import { BlogPreview } from "@/components/sections/BlogPreview";
import { CtaBanner } from "@/components/sections/CtaBanner";
import { companyDetails } from "@/content/company";
import { Wrench, Phone, ArrowRight } from "lucide-react";
import Link from "next/link";
export default function Home() {
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://thermcool.pl';
@@ -139,6 +141,41 @@ export default function Home() {
<WhyUs />
{/* ProjectsSection will go here in Group B */}
<Testimonials />
{/* Serwis LG */}
<section className="py-16 bg-white">
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="bg-primary rounded-2xl p-8 md:p-10 flex flex-col md:flex-row items-start md:items-center gap-6">
<div className="flex-1">
<div className="inline-flex items-center gap-2 px-3 py-1 bg-white/10 rounded-full text-white text-xs font-semibold mb-4">
<Wrench className="size-3.5" />
Specjalizacja
</div>
<h2 className="text-2xl md:text-3xl font-extrabold text-white font-heading mb-3">
Serwisujemy LG —<br />tam gdzie inni mówią nie
</h2>
<p className="text-white/75 text-sm leading-relaxed max-w-lg">
Większość firm odmawia naprawy klimatyzatorów LG. My mamy doświadczenie
i narzędzia żeby skutecznie diagnozować i naprawiać te urządzenia
na terenie całego Dolnego Śląska.
</p>
</div>
<div className="flex flex-col gap-3 shrink-0">
<Link href="/serwis-lg">
<button className="flex items-center gap-2 px-6 py-3 bg-accent text-white rounded-xl font-bold hover:bg-accent/90 transition-colors whitespace-nowrap">
Dowiedz się więcej <ArrowRight className="size-4" />
</button>
</Link>
<a href={`tel:${companyDetails.phoneRaw}`}>
<button className="flex items-center gap-2 px-6 py-3 bg-white/10 text-white rounded-xl font-bold hover:bg-white/20 transition-colors border border-white/20 whitespace-nowrap">
<Phone className="size-4" /> Zadzwoń teraz
</button>
</a>
</div>
</div>
</div>
</section>
<BlogPreview />
<CtaBanner bgVariant="accent-light" />
</>
+200
View File
@@ -0,0 +1,200 @@
import type { Metadata } from 'next';
import Link from 'next/link';
import { Check, Phone, Wrench, AlertTriangle, ArrowRight } from 'lucide-react';
import { companyDetails } from '@/content/company';
export const metadata: Metadata = {
title: 'Serwis klimatyzacji LG Wrocław — naprawa i przegląd | ThermCool',
description: 'Serwisujemy klimatyzatory LG na Dolnym Śląsku. Naprawa, przegląd, odgrzybianie — tam gdzie inne firmy odmawiają. Zadzwoń i umów wizytę.',
alternates: { canonical: '/serwis-lg' },
openGraph: {
title: 'Serwis klimatyzacji LG Wrocław | ThermCool',
description: 'Specjalizujemy się w serwisie klimatyzatorów LG. Naprawa, diagnostyka, przegląd na Dolnym Śląsku.',
},
};
const faqItems = [
{
q: 'Dlaczego inne firmy odmawiają serwisu klimatyzacji LG?',
a: 'Klimatyzatory LG wymagają specjalistycznej wiedzy i dedykowanych narzędzi diagnostycznych. Wiele firm unika tych urządzeń ze względu na złożoność układów elektronicznych i trudniejszy dostęp do części zamiennych. My posiadamy odpowiednie doświadczenie i narzędzia.',
},
{
q: 'Ile kosztuje przegląd klimatyzatora LG?',
a: 'Koszt przeglądu klimatyzatora LG zaczyna się od 250 zł i obejmuje czyszczenie filtrów, sprawdzenie ciśnienia czynnika chłodniczego, kontrolę układu elektrycznego i test działania. Dokładna wycena zależy od modelu i stanu urządzenia.',
},
{
q: 'Jak długo trwa naprawa klimatyzatora LG?',
a: 'Standardowy przegląd i czyszczenie to 1-2 godziny. Naprawa usterek zależy od rodzaju problemu — prostsze naprawy wykonujemy podczas jednej wizyty, przy bardziej skomplikowanych usterkach czas realizacji ustalamy indywidualnie po diagnostyce.',
},
{
q: 'Czy przyjeżdżacie poza Wrocław?',
a: 'Tak, obsługujemy cały Dolny Śląsk — Legnicę, Wałbrzych, Jelenią Górę, Lubin, Świdnicę i okoliczne miejscowości. Koszt dojazdu ustalamy indywidualnie.',
},
{
q: 'Czy naprawiacie wszystkie modele LG?',
a: 'Serwisujemy większość klimatyzatorów LG dostępnych na rynku, w tym modele z serii Artcool, Standard Plus, Dualcool i MultiSplit. W przypadku bardzo starych lub rzadkich modeli skontaktuj się z nami przed umówieniem wizyty.',
},
];
export default function SerwisLGPage() {
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Service',
name: 'Serwis klimatyzacji LG',
provider: {
'@type': 'HVACBusiness',
name: companyDetails.name,
telephone: companyDetails.phone,
address: {
'@type': 'PostalAddress',
addressLocality: companyDetails.address.city,
addressRegion: companyDetails.address.region,
addressCountry: 'PL',
},
},
areaServed: { '@type': 'AdministrativeArea', name: 'Dolny Śląsk' },
description: 'Serwis, naprawa i przegląd klimatyzatorów LG na Dolnym Śląsku.',
mainEntityOfPage: {
'@type': 'FAQPage',
mainEntity: faqItems.map(f => ({
'@type': 'Question',
name: f.q,
acceptedAnswer: { '@type': 'Answer', text: f.a },
})),
},
};
return (
<>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<main>
{/* Hero */}
<section className="bg-primary text-white py-16 md:py-24">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="inline-flex items-center gap-2 px-3 py-1.5 bg-white/10 rounded-full text-sm font-medium mb-6">
<Wrench className="size-4" />
Specjalistyczny serwis LG
</div>
<h1 className="text-3xl md:text-5xl font-extrabold font-heading leading-tight mb-6">
Serwis klimatyzacji LG<br />
<span className="text-accent">tam gdzie inni mówią nie</span>
</h1>
<p className="text-lg text-white/80 mb-8 max-w-2xl">
Klimatyzatory LG mają opinię trudnych w serwisie — większość firm odmawia ich naprawy.
My mamy odpowiednie doświadczenie i narzędzia, żeby skutecznie diagnozować
i naprawiać te urządzenia na terenie Dolnego Śląska.
</p>
<div className="flex flex-col sm:flex-row gap-4">
<a href={`tel:${companyDetails.phoneRaw}`}>
<button className="flex items-center gap-2 px-6 py-3 bg-accent text-white rounded-xl font-bold hover:bg-accent/90 transition-colors">
<Phone className="size-5" /> Zadzwoń: {companyDetails.phone}
</button>
</a>
<Link href="/kontakt">
<button className="flex items-center gap-2 px-6 py-3 bg-white/10 text-white rounded-xl font-bold hover:bg-white/20 transition-colors border border-white/20">
Napisz do nas <ArrowRight className="size-5" />
</button>
</Link>
</div>
</div>
</section>
{/* Dlaczego my */}
<section className="py-16 bg-white">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-start gap-4 p-5 bg-orange-50 border border-orange-200 rounded-2xl mb-10">
<AlertTriangle className="size-6 text-orange-500 shrink-0 mt-0.5" />
<div>
<p className="font-bold text-primary mb-1">Dlaczego inne firmy odmawiają serwisu LG?</p>
<p className="text-sm text-text-muted">
Klimatyzatory LG wymagają specjalistycznej wiedzy i dedykowanych narzędzi diagnostycznych.
Wiele firm serwisowych unika tych urządzeń ze względu na złożoność układów elektronicznych
i trudniejszy dostęp do oryginalnych części zamiennych. My podjęliśmy to wyzwanie
i dziś skutecznie naprawiamy to, od czego inni odchodzą.
</p>
</div>
</div>
<h2 className="text-2xl font-extrabold text-primary font-heading mb-8">Co robimy dla Twojego LG?</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{[
{ title: 'Przegląd i czyszczenie', desc: 'Czyszczenie filtrów, lameli, tacy ociekowej. Sprawdzenie szczelności i ciśnienia czynnika R32/R410A.' },
{ title: 'Diagnostyka usterek', desc: 'Odczyt kodów błędów, pomiary elektryczne, identyfikacja uszkodzonych podzespołów.' },
{ title: 'Naprawa układu chłodniczego', desc: 'Uzupełnianie czynnika chłodniczego, naprawa nieszczelności, wymiana sprężarki.' },
{ title: 'Naprawa elektroniki', desc: 'Wymiana płyt głównych, modułów WiFi, czujników temperatury i sterowników.' },
{ title: 'Odgrzybianie i dezynfekcja', desc: 'Profesjonalne odgrzybianie klimatyzatora — eliminacja grzybów i bakterii z układu nawiewu.' },
{ title: 'Serwis gwarancyjny i pogwarancyjny', desc: 'Obsługujemy urządzenia zarówno w trakcie jak i po wygaśnięciu gwarancji producenta.' },
].map((item, i) => (
<div key={i} className="flex gap-3 p-4 bg-slate-50 rounded-xl">
<Check className="size-5 text-accent shrink-0 mt-0.5" />
<div>
<p className="font-bold text-primary text-sm">{item.title}</p>
<p className="text-xs text-text-muted mt-0.5">{item.desc}</p>
</div>
</div>
))}
</div>
</div>
</section>
{/* Obsługiwane serie LG */}
<section className="py-16 bg-slate-50">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-2xl font-extrabold text-primary font-heading mb-6">
Obsługiwane serie klimatyzatorów LG
</h2>
<div className="flex flex-wrap gap-2">
{['LG Artcool', 'LG Standard Plus', 'LG Dualcool', 'LG MultiSplit', 'LG LGRED°', 'LG Inverter V', 'LG UV Nano', 'LG Deluxe'].map(s => (
<span key={s} className="px-4 py-2 bg-white border border-border rounded-xl text-sm font-semibold text-primary">
{s}
</span>
))}
</div>
<p className="text-xs text-text-muted mt-4">
Nie widzisz swojego modelu? Zadzwoń — prawdopodobnie też go obsługujemy.
</p>
</div>
</section>
{/* FAQ */}
<section className="py-16 bg-white">
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-2xl font-extrabold text-primary font-heading mb-8">
Często zadawane pytania
</h2>
<div className="flex flex-col gap-4">
{faqItems.map((item, i) => (
<details key={i} className="group bg-slate-50 rounded-xl border border-border overflow-hidden">
<summary className="flex items-center justify-between p-5 cursor-pointer font-semibold text-primary text-sm list-none">
{item.q}
<ArrowRight className="size-4 text-text-muted group-open:rotate-90 transition-transform shrink-0 ml-3" />
</summary>
<div className="px-5 pb-5 text-sm text-text-muted leading-relaxed border-t border-border pt-4">
{item.a}
</div>
</details>
))}
</div>
</div>
</section>
{/* CTA */}
<section className="py-16 bg-primary text-white">
<div className="max-w-2xl mx-auto px-4 text-center">
<h2 className="text-2xl font-extrabold font-heading mb-4">
Masz problem z klimatyzatorem LG?
</h2>
<p className="text-white/80 mb-8">
Zadzwoń lub napisz — odpowiadamy szybko i przyjeżdżamy na Dolny Śląsk.
</p>
<a href={`tel:${companyDetails.phoneRaw}`}>
<button className="flex items-center gap-2 px-8 py-4 bg-accent text-white rounded-xl font-bold text-lg hover:bg-accent/90 transition-colors mx-auto">
<Phone className="size-5" /> {companyDetails.phone}
</button>
</a>
</div>
</section>
</main>
</>
);
}
+2
View File
@@ -21,6 +21,8 @@ export default function sitemap(): MetadataRoute.Sitemap {
'/regulamin',
'/polityka-prywatnosci',
'/reklamacje',
'/serwis-lg',
'/zgloszenie-awarii',
'/dostawa',
'/gwarancja',
].map((route) => ({
+14 -1
View File
@@ -3,6 +3,7 @@ import { notFound } from 'next/navigation';
import { getProductBySlug, getProducts } from '@/content/products';
import { companyDetails } from '@/content/company';
import { ProductPageClient } from '@/components/shop/ProductPageClient';
import { getProductVariants } from '@/content/product-variants';
interface Props {
params: Promise<{ slug: string }>;
@@ -28,6 +29,13 @@ export default async function ProductPage({ params }: Props) {
const product = getProductBySlug(slug);
if (!product) notFound();
// Pobierz wszystkie produkty serii dla wariantów
const variants = getProductVariants(slug);
const allProducts = getProducts();
const seriesProducts = variants
? allProducts.filter(p => getProductVariants(p.slug)?.series === variants.series)
: [product];
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://thermcool.pl';
const productJsonLd = product.price ? {
@@ -54,7 +62,12 @@ export default async function ProductPage({ params }: Props) {
{productJsonLd && (
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(productJsonLd) }} />
)}
<ProductPageClient product={product} companyPhone={companyDetails.phone} companyPhoneRaw={companyDetails.phoneRaw} />
<ProductPageClient
product={product}
seriesProducts={seriesProducts}
companyPhone={companyDetails.phone}
companyPhoneRaw={companyDetails.phoneRaw}
/>
</>
);
}
+2 -2
View File
@@ -23,8 +23,8 @@ export default function ShopPage() {
subtitle={`${splitCount} klimatyzatorów split i ${multiCount} jednostek multisplit. Gree, Rotenso, Haier, Daikin, LG, Samsung i inne marki. Z montażem lub bez.`}
ctaText="Zapytaj o montaż"
ctaLink="/kontakt"
secondaryCtaText={undefined}
secondaryCtaLink={undefined}
secondaryCtaText=""
secondaryCtaLink=""
backgroundImage="https://images.unsplash.com/photo-1621905251189-08b45d6a269e?w=1600&q=80"
/>
<ProductsGrid products={products} brands={brands} />
+59
View File
@@ -0,0 +1,59 @@
'use server';
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function submitAwariaForm(data: {
isUrgent: boolean;
brand: string;
model: string;
errorCode: string;
problemType: string;
description: string;
warranty: string;
lastService: string;
name: string;
phone: string;
city: string;
preferredDate: string;
preferredTime: string;
}) {
try {
await resend.emails.send({
from: process.env.RESEND_FROM_EMAIL || '[email protected]',
to: process.env.RESEND_TO_EMAIL || '[email protected]',
subject: `${data.isUrgent ? '🚨 PILNE — ' : ''}Zgłoszenie serwisowe: ${data.brand} — ${data.city}`,
html: `
<div style="font-family:Arial,sans-serif;max-width:600px;margin:0 auto;">
<div style="background:${data.isUrgent ? '#ef4444' : '#0F2A47'};padding:20px;text-align:center;">
<h1 style="color:#fff;margin:0;font-size:20px;">
${data.isUrgent ? '🚨 PILNE — ' : ''}Zgłoszenie serwisowe ThermCool
</h1>
</div>
<div style="padding:24px;">
<h2 style="font-size:16px;margin-top:0;">Dane klienta</h2>
<p><strong>Imię:</strong> ${data.name}</p>
<p><strong>Telefon:</strong> <a href="tel:${data.phone}">${data.phone}</a></p>
<p><strong>Miasto:</strong> ${data.city}</p>
${data.preferredDate ? `<p><strong>Preferowany termin:</strong> ${data.preferredDate} ${data.preferredTime || ''}</p>` : ''}
<h2 style="font-size:16px;">Urządzenie</h2>
<p><strong>Marka:</strong> ${data.brand}</p>
${data.model ? `<p><strong>Model:</strong> ${data.model}</p>` : ''}
${data.warranty ? `<p><strong>Gwarancja:</strong> ${data.warranty}</p>` : ''}
${data.lastService ? `<p><strong>Ostatni serwis:</strong> ${data.lastService}</p>` : ''}
<h2 style="font-size:16px;">Problem</h2>
<p><strong>Typ problemu:</strong> ${data.problemType}</p>
${data.errorCode ? `<p><strong>Kod błędu:</strong> ${data.errorCode}</p>` : ''}
${data.description ? `<p><strong>Opis:</strong> ${data.description}</p>` : ''}
</div>
</div>
`,
});
return { success: true };
} catch {
return { success: false };
}
}
+12
View File
@@ -0,0 +1,12 @@
import type { Metadata } from 'next';
import { AwariaClient } from '@/components/awaria/AwariaClient';
export const metadata: Metadata = {
title: 'Zgłoś awarię klimatyzacji | Serwis ThermCool Wrocław',
description: 'Zgłoś awarię klimatyzatora online. Szybka diagnostyka, kod błędu, preferowany termin wizyty. Obsługujemy Wrocław i cały Dolny Śląsk.',
alternates: { canonical: '/zgloszenie-awarii' },
};
export default function ZgloszenieAwariiPage() {
return <AwariaClient />;
}