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
+65 -44
View File
@@ -1,55 +1,76 @@
import { Star, Quote } from "lucide-react";
import { SectionHeading } from "../common/SectionHeading";
import { testimonials } from "@/content/testimonials";
'use client';
import useEmblaCarousel from 'embla-carousel-react';
import Autoplay from 'embla-carousel-autoplay';
import { Star, ChevronLeft, ChevronRight } from 'lucide-react';
import { useCallback } from 'react';
const testimonials = [
{ name: 'Anna Kowalska', city: 'Wrocław', rating: 5, text: 'Montaż przeprowadzony sprawnie i bez bałaganu. Ekipa przybyła punktualnie, wszystko wytłumaczyli i zostawili po sobie idealny porządek. Klimatyzator działa cicho i skutecznie.' },
{ name: 'Marek Wiśniewski', city: 'Legnica', rating: 5, text: 'Polecam z całego serca. Fachowa obsługa, dobra cena i bardzo miły kontakt. Klimatyzator dobrali idealnie do mojego salonu 40 m². Już pierwszego lata oszczędności widoczne na rachunkach.' },
{ name: 'Katarzyna Nowak', city: 'Wrocław', rating: 5, text: 'Bardzo profesjonalna firma. Zamówiłam klimatyzację do sypialni i biura — dwie jednostki zamontowane w jeden dzień. Instalacja ukryta w brudzie, wygląda świetnie.' },
{ name: 'Piotr Zając', city: 'Wałbrzych', rating: 5, text: 'Szybka wycena, konkretne podejście bez owijania w bawełnę. Klimatyzator Gree działa już drugi sezon bez żadnych problemów. Serwis też na wysokim poziomie.' },
{ name: 'Monika Dąbrowska', city: 'Jelenia Góra', rating: 5, text: 'Świetna obsługa od pierwszego kontaktu do odbioru prac. Montażyści zadbali o każdy detal, przeprowadzenie rur estetyczne. Klimatyzacja grzeje też zimą — duże zaskoczenie!' },
{ name: 'Tomasz Lewandowski', city: 'Wrocław', rating: 5, text: 'Bardzo dobra firma. Mam multisplit na 3 pokoje — wszystko działa bez zarzutu. Cena montażu zgodna z wyceną, bez ukrytych kosztów. Zdecydowanie wrócę przy kolejnej inwestycji.' },
{ name: 'Agnieszka Wróbel', city: 'Oleśnica', rating: 5, text: 'Polecam każdemu. Szybko, czysto i profesjonalnie. Klimatyzator marki Rotenso — cichy jak myszka, a chłodzi rewelacyjnie. Wreszcie sypialnia w której można spać latem.' },
{ name: 'Krzysztof Jabłoński', city: 'Lubin', rating: 5, text: 'Solidna robota. Montaż kasetonowy w biurze 60 m² — ekipa wiedziała co robi, nie tracili czasu. Urządzenie działa cicho, pracownicy zadowoleni. Na pewno polecę znajomym.' },
{ name: 'Ewa Kamińska', city: 'Wrocław', rating: 5, text: 'Doskonały kontakt i profesjonalizm na każdym etapie. Doradca pomógł wybrać odpowiednią moc, montaż odbył się następnego dnia. Klimatyzacja w domu to teraz standard.' },
{ name: 'Radosław Szymański', city: 'Świdnica', rating: 5, text: 'Bardzo sprawna realizacja. Zamówiłem w środę, w piątek już miałem zamontowaną klimatyzację. Jakość wykonania na wysokim poziomie, wszystko dokładnie wyjaśnione.' },
{ name: 'Marta Kowalczyk', city: 'Oława', rating: 5, text: 'Super firma! Klimatyzator Samsung dobrana idealnie do naszego mieszkania. Montażyści schludni, uprzejmi i szybcy. Instalacja w korytku wygląda bardzo estetycznie.' },
{ name: 'Grzegorz Woźniak', city: 'Wrocław', rating: 5, text: 'Drugi raz korzystam z usług ThermCool i znowu jestem w pełni zadowolony. Tym razem pompa ciepła — ekipa poradziła sobie z trudnym miejscem montażu bez problemu.' },
{ name: 'Joanna Michalska', city: 'Trzebnica', rating: 5, text: 'Bardzo dobra obsługa i uczciwe ceny. Klimatyzacja Daikin pracuje bezbłędnie od ponad roku. Przy okazji montażu doradca wyjaśnił jak dbać o urządzenie żeby służyło jak najdłużej.' },
{ name: 'Bartosz Krawczyk', city: 'Głogów', rating: 5, text: 'Polecam! Montaż multisplit do domu jednorodzinnego — 4 jednostki wewnętrzne zamontowane w dwa dni. Praca czysta, instalacja ukryta w brudzie. Efekt końcowy powyżej oczekiwań.' },
{ name: 'Sylwia Pawlak', city: 'Środa Śląska', rating: 5, text: 'Klimatyzator Haier dobrany pod kątem rozmiarów pokoju i budżetu. Bardzo pomocny doradca, bez zbędnego wciskania droższych modeli. Montaż szybki i bez bałaganu. Polecam!' },
];
export function Testimonials() {
const [emblaRef, emblaApi] = useEmblaCarousel(
{ loop: true, align: 'start', slidesToScroll: 1 },
[Autoplay({ delay: 4000, stopOnInteraction: true })]
);
const scrollPrev = useCallback(() => emblaApi?.scrollPrev(), [emblaApi]);
const scrollNext = useCallback(() => emblaApi?.scrollNext(), [emblaApi]);
return (
<section className="py-16 md:py-24 bg-slate-50 border-t border-b border-border">
<section className="py-16 bg-slate-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<SectionHeading
eyebrow="Opinie Klientów"
title="Co mówią o nas nasi klienci?"
subtitle="Zadowolenie klientów to nasz najwyższy cel. Zobacz opinie osób, które zaufały naszym montażystom i serwisantom."
align="center"
/>
<div className="flex items-center justify-between mb-10">
<div>
<h2 className="text-2xl md:text-3xl font-extrabold text-primary font-heading">
Co mówią nasi klienci
</h2>
<p className="text-text-muted mt-1">Opinie klientów</p>
</div>
<div className="flex gap-2">
<button onClick={scrollPrev}
className="size-10 rounded-xl border border-border bg-white flex items-center justify-center text-primary hover:border-accent hover:text-accent transition-colors">
<ChevronLeft className="size-5" />
</button>
<button onClick={scrollNext}
className="size-10 rounded-xl border border-border bg-white flex items-center justify-center text-primary hover:border-accent hover:text-accent transition-colors">
<ChevronRight className="size-5" />
</button>
</div>
</div>
{/* Testimonials Grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{testimonials.map((test, idx) => (
<div
key={idx}
className="relative p-8 rounded-xl bg-white border border-border hover:shadow-md transition-shadow flex flex-col justify-between"
>
{/* Quote icon background decoration */}
<Quote className="absolute top-6 right-6 size-12 text-slate-100 -z-0" />
<div className="relative z-10 flex flex-col gap-4">
{/* Stars */}
<div className="flex gap-1 mb-4">
{Array.from({ length: test.rating }).map((_, iIdx) => (
<Star key={iIdx} className="size-4 fill-yellow-400 text-yellow-400" />
<div className="overflow-hidden" ref={emblaRef}>
<div className="flex gap-5">
{testimonials.map((t, i) => (
<div key={i} className="flex-none w-[320px] md:w-[360px] bg-white rounded-2xl border border-border p-6 flex flex-col gap-3">
<div className="flex gap-0.5">
{Array.from({ length: t.rating }).map((_, j) => (
<Star key={j} className="size-4 fill-yellow-400 text-yellow-400" />
))}
</div>
{/* Review Text */}
<p className="text-sm md:text-base text-primary italic leading-relaxed">
&ldquo;{test.text}&rdquo;
</p>
<p className="text-sm text-text-muted leading-relaxed flex-1">"{t.text}"</p>
<div className="pt-3 border-t border-border">
<p className="text-sm font-bold text-primary">{t.name}</p>
<p className="text-xs text-text-muted">{t.city}</p>
</div>
</div>
{/* Reviewer Details */}
<div className="relative z-10 border-t border-slate-100 mt-6 pt-4 flex justify-between items-center text-xs">
<span className="font-bold text-primary font-heading">
{test.name}
</span>
{test.source && (
<span className="px-2.5 py-1 rounded bg-slate-100 text-text-muted font-medium">
{test.source}
</span>
)}
</div>
</div>
))}
))}
</div>
</div>
</div>
</section>