feat: refactor calculator room logic, update pricing, add dynamic prepositions
This commit is contained in:
@@ -71,7 +71,7 @@ export default async function MiastoPage({ params }: Props) {
|
||||
{/* HERO */}
|
||||
<HeroSection
|
||||
eyebrow={`Klimatyzacja ${city.name} · Dolny Śląsk · F-Gaz`}
|
||||
title={`Montaż klimatyzacji w ${city.nameDopelniacz}`}
|
||||
title={`Montaż klimatyzacji ${city.przyimek} ${city.nameDopelniacz}`}
|
||||
subtitle={city.heroSubtitle}
|
||||
ctaText="Zamów bezpłatną wycenę"
|
||||
ctaLink="/kontakt"
|
||||
@@ -95,7 +95,7 @@ export default async function MiastoPage({ params }: Props) {
|
||||
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}
|
||||
Profesjonalny montaż klimatyzacji {city.przyimek} {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>
|
||||
@@ -106,7 +106,7 @@ export default async function MiastoPage({ params }: Props) {
|
||||
<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>
|
||||
<p className="text-text-muted text-sm mt-1">Standardowy montaż split {city.przyimek} {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">
|
||||
@@ -144,7 +144,7 @@ export default async function MiastoPage({ params }: Props) {
|
||||
<ProcessSteps
|
||||
steps={acProcessSteps}
|
||||
eyebrow="Jak Działamy"
|
||||
title={`Montaż klimatyzacji w ${city.nameDopelniacz} — krok po kroku`}
|
||||
title={`Montaż klimatyzacji ${city.przyimek} ${city.nameDopelniacz} — krok po kroku`}
|
||||
subtitle="Od pierwszego kontaktu do przekazania gotowej instalacji. Każdy etap pod kontrolą."
|
||||
/>
|
||||
|
||||
@@ -161,7 +161,7 @@ export default async function MiastoPage({ params }: Props) {
|
||||
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}
|
||||
Pompa ciepła {city.przyimek} {city.nameDopelniacz}
|
||||
</h2>
|
||||
<p className="text-white/80 leading-relaxed mb-6">{city.heatPumpTeaser}</p>
|
||||
<Link href="/pompy-ciepla">
|
||||
@@ -243,7 +243,7 @@ export default async function MiastoPage({ params }: Props) {
|
||||
|
||||
{/* KOŃCOWE CTA */}
|
||||
<CtaBanner
|
||||
title={`Potrzebujesz klimatyzacji w ${city.nameDopelniacz}?`}
|
||||
title={`Potrzebujesz klimatyzacji ${city.przyimek} ${city.nameDopelniacz}?`}
|
||||
subtitle="Odpowiemy w ciągu godziny i umówimy bezpłatny audyt w dogodnym terminie."
|
||||
ctaText="Zadzwoń lub napisz"
|
||||
ctaLink="/kontakt"
|
||||
|
||||
@@ -38,7 +38,7 @@ function estimateInstall(pipes: string, rooms: number): { min: number; max: numb
|
||||
return { min: base + pipeExtra, max: base + pipeExtra + 600 };
|
||||
}
|
||||
|
||||
const STEPS = ['Typ', 'Pomieszczenie', 'Konfiguracja', 'Instalacja', 'Budżet'] as const;
|
||||
const STEPS = ['Typ', 'Pomieszczenie', 'Instalacja', 'Budżet'] as const;
|
||||
|
||||
export function KalkulatorClient({ products }: Props) {
|
||||
const [deviceType, setDeviceType] = useState<'klimatyzacja' | 'pompa'>('klimatyzacja');
|
||||
@@ -46,21 +46,42 @@ export function KalkulatorClient({ products }: Props) {
|
||||
const [pumpForm, setPumpForm] = useState({ name: '', phone: '', city: '', message: '' });
|
||||
const [step, setStep] = useState(0);
|
||||
const [building, setBuilding] = useState('apartment');
|
||||
const [m2, setM2] = useState(25);
|
||||
const [pomieszczenia, setPomieszczenia] = useState<{ nazwa: string; m2: number }[]>([
|
||||
{ nazwa: 'Pokój 1', m2: 20 }
|
||||
]);
|
||||
const [floor, setFloor] = useState('middle'); // ground | middle | top
|
||||
const [sun, setSun] = useState('south'); // north | east | south | west
|
||||
|
||||
const [systemType, setSystemType] = useState<'auto' | 'scienne' | 'multisplit'>('auto');
|
||||
const [rooms, setRooms] = useState(1);
|
||||
const addRoom = () => {
|
||||
if (pomieszczenia.length < 5) {
|
||||
setPomieszczenia(prev => [...prev, { nazwa: `Pokój ${prev.length + 1}`, m2: 15 }]);
|
||||
}
|
||||
};
|
||||
|
||||
const removeRoom = (i: number) => {
|
||||
if (pomieszczenia.length > 1) {
|
||||
setPomieszczenia(prev => prev.filter((_, idx) => idx !== i));
|
||||
}
|
||||
};
|
||||
|
||||
const updateRoom = (i: number, m2: number) => {
|
||||
setPomieszczenia(prev => prev.map((r, idx) => idx === i ? { ...r, m2 } : r));
|
||||
};
|
||||
const [pipes, setPipes] = useState('medium'); // short(<3m) | medium(3-6m) | long(6-10m) | verylong(>10m)
|
||||
const [budget, setBudget] = useState(8000);
|
||||
const [showResults, setShowResults] = useState(false);
|
||||
const [preferredBrand, setPreferredBrand] = useState('');
|
||||
|
||||
const requiredPower = useMemo(() => calculatePower(m2, floor, sun, building), [m2, floor, sun, building]);
|
||||
const totalM2 = pomieszczenia.reduce((sum, p) => sum + p.m2, 0);
|
||||
const rooms = pomieszczenia.length;
|
||||
const requiredPower = useMemo(() =>
|
||||
pomieszczenia.reduce((sum, p) => sum + calculatePower(p.m2, floor, sun, building), 0),
|
||||
[pomieszczenia, floor, sun, building]
|
||||
);
|
||||
const requiredPowerRounded = Math.round(requiredPower * 10) / 10;
|
||||
const installCost = useMemo(() => estimateInstall(pipes, rooms), [pipes, rooms]);
|
||||
|
||||
const category = systemType === 'auto' ? (rooms === 1 ? 'scienne' : 'multisplit') : systemType;
|
||||
const category = rooms === 1 ? 'scienne' : 'multisplit';
|
||||
|
||||
const results = useMemo(() => {
|
||||
if (!showResults || deviceType === 'pompa') return [];
|
||||
@@ -151,8 +172,8 @@ export function KalkulatorClient({ products }: Props) {
|
||||
</h1>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
||||
{[
|
||||
{ label: 'Wymagana moc', value: `${requiredPower} kW` },
|
||||
{ label: 'Powierzchnia', value: `${m2} m²` },
|
||||
{ label: 'Wymagana moc', value: `${requiredPowerRounded} kW` },
|
||||
{ label: 'Powierzchnia', value: `${totalM2} m²` },
|
||||
{ label: 'Typ instalacji', value: category === 'scienne' ? 'Split (1 pokój)' : `Multisplit (${rooms} pokoje)` },
|
||||
{ label: 'Szac. montaż', value: `${installCost.min.toLocaleString('pl-PL')}–${installCost.max.toLocaleString('pl-PL')} zł` },
|
||||
].map((s, i) => (
|
||||
@@ -410,35 +431,80 @@ export function KalkulatorClient({ products }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-text-muted">Powierzchnia</span>
|
||||
<span className="text-xs font-bold text-primary bg-accent/10 px-2 py-0.5 rounded-md">{m2} m²</span>
|
||||
</div>
|
||||
<div className="relative h-5 flex items-center">
|
||||
<div className="absolute w-full h-1.5 bg-slate-200 rounded-full" />
|
||||
<div
|
||||
className="absolute h-1.5 bg-accent rounded-full"
|
||||
style={{ width: `${((m2 - 10) / 90) * 100}%` }}
|
||||
/>
|
||||
<input
|
||||
type="range" min={10} max={100} step={5} value={m2}
|
||||
onChange={e => setM2(Number(e.target.value))}
|
||||
className="absolute w-full h-1.5 opacity-0 cursor-pointer z-10"
|
||||
/>
|
||||
<div
|
||||
className="absolute size-4 bg-white border-2 border-accent rounded-full shadow-md pointer-events-none"
|
||||
style={{ left: `calc(${((m2 - 10) / 90) * 100}% - 8px)` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between text-xs text-text-muted">
|
||||
<span>10 m²</span>
|
||||
<span>100 m²</span>
|
||||
</div>
|
||||
<div className="mt-3 p-3 bg-blue-50 rounded-xl text-sm text-blue-700 font-medium">
|
||||
Wymagana moc: ok. <strong>{requiredPower} kW</strong>
|
||||
<span className="text-xs font-normal ml-2">(dla {m2} m²)</span>
|
||||
</div>
|
||||
{/* Lista pomieszczeń */}
|
||||
<div className="flex flex-col gap-3">
|
||||
{pomieszczenia.map((p, i) => (
|
||||
<div key={i} className="bg-slate-50 rounded-xl border border-border p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-sm font-semibold text-primary">{p.nazwa}</span>
|
||||
{pomieszczenia.length > 1 && (
|
||||
<button
|
||||
onClick={() => removeRoom(i)}
|
||||
className="text-xs text-red-400 hover:text-red-600 transition-colors"
|
||||
>
|
||||
Usuń
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-text-muted">Powierzchnia</span>
|
||||
<span className="text-xs font-bold text-primary bg-accent/10 px-2 py-0.5 rounded-md">{p.m2} m²</span>
|
||||
</div>
|
||||
<div className="relative h-5 flex items-center">
|
||||
<div className="absolute w-full h-1.5 bg-slate-200 rounded-full" />
|
||||
<div
|
||||
className="absolute h-1.5 bg-accent rounded-full"
|
||||
style={{ width: `${((p.m2 - 5) / 95) * 100}%` }}
|
||||
/>
|
||||
<input
|
||||
type="range" min={5} max={100} step={5} value={p.m2}
|
||||
onChange={e => updateRoom(i, Number(e.target.value))}
|
||||
className="absolute w-full h-1.5 opacity-0 cursor-pointer z-10"
|
||||
/>
|
||||
<div
|
||||
className="absolute size-4 bg-white border-2 border-accent rounded-full shadow-md pointer-events-none"
|
||||
style={{ left: `calc(${((p.m2 - 5) / 95) * 100}% - 8px)` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between text-xs text-text-muted">
|
||||
<span>5 m²</span>
|
||||
<span>100 m²</span>
|
||||
</div>
|
||||
{(() => {
|
||||
const mocPokoju = Math.round(calculatePower(p.m2, floor, sun, building) * 10) / 10;
|
||||
const dostepneMoce = [2.5, 3.5, 5.0, 7.0];
|
||||
const zalecana = dostepneMoce.reduce((prev, curr) =>
|
||||
Math.abs(curr - mocPokoju) < Math.abs(prev - mocPokoju) ? curr : prev
|
||||
);
|
||||
return (
|
||||
<div className="mt-2 flex items-center justify-between bg-white border border-border rounded-lg px-3 py-2">
|
||||
<span className="text-xs text-text-muted">Zalecana moc jednostki</span>
|
||||
<span className="text-sm font-bold text-accent">{zalecana} kW</span>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{pomieszczenia.length < 5 && (
|
||||
<button
|
||||
onClick={addRoom}
|
||||
className="flex items-center gap-2 text-sm text-accent font-semibold hover:text-accent/80 transition-colors border border-dashed border-accent/40 rounded-xl px-4 py-3 w-full justify-center"
|
||||
>
|
||||
+ Dodaj kolejne pomieszczenie
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="p-3 bg-accent/10 rounded-xl">
|
||||
<p className="text-sm text-accent font-semibold">
|
||||
Łączna wymagana moc: ok. <strong>{requiredPowerRounded} kW</strong>
|
||||
<span className="text-xs font-normal text-text-muted ml-2">
|
||||
({pomieszczenia.length === 1 ? 'klimatyzator split' : `multisplit ${pomieszczenia.length}+1`}, łącznie {totalM2} m²)
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -513,64 +579,8 @@ export function KalkulatorClient({ products }: Props) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Krok 2: Konfiguracja */}
|
||||
{/* Krok 2: Instalacja */}
|
||||
{step === 2 && (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="size-10 rounded-xl bg-purple-50 flex items-center justify-center">
|
||||
<Thermometer className="size-5 text-purple-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-bold text-primary font-heading">Konfiguracja</h2>
|
||||
<p className="text-xs text-text-muted">Ilość pomieszczeń i typ instalacji</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-semibold text-primary mb-3 block">Wybierz typ systemu</label>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-2">
|
||||
{[
|
||||
{ value: 'auto', label: 'Dobierz automatycznie' },
|
||||
{ value: 'scienne', label: 'Split (1 agregat = 1 pokój)' },
|
||||
{ value: 'multisplit', label: 'Multisplit (1 agregat = wiele)' },
|
||||
].map(o => (
|
||||
<button key={o.value} onClick={() => setSystemType(o.value as 'auto'|'scienne'|'multisplit')}
|
||||
className={`py-2 px-3 rounded-xl text-sm font-semibold border-2 transition-all ${systemType === o.value ? 'border-accent bg-accent text-white' : 'border-border bg-white text-primary hover:border-accent'}`}>
|
||||
{o.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-semibold text-primary mb-3 block">Ile pomieszczeń chcesz chłodzić?</label>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{[
|
||||
{ value: 1, label: '1 pomieszczenie', sub: 'Pojedynczy pokój', icon: '🏠' },
|
||||
{ value: 2, label: '2 pomieszczenia', sub: 'Dwa pokoje', icon: '🏡' },
|
||||
{ value: 3, label: '3 pomieszczenia', sub: 'Trzy pokoje', icon: '🏘️' },
|
||||
{ value: 4, label: '4-5 pomieszczeń', sub: 'Wiele pokoi', icon: '🏗️' },
|
||||
].map(o => (
|
||||
<button key={o.value} onClick={() => setRooms(o.value)}
|
||||
className={`p-4 rounded-xl border-2 text-left transition-all ${rooms === o.value ? 'border-accent bg-accent/5' : 'border-border hover:border-accent'}`}>
|
||||
<span className="text-2xl mb-2 block">{o.icon}</span>
|
||||
<p className="text-sm font-bold text-primary">{o.label}</p>
|
||||
<p className="text-xs text-text-muted">{o.sub}</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-slate-50 rounded-xl text-xs text-text-muted">
|
||||
{category === 'scienne'
|
||||
? '✅ Wybrano: Klimatyzator split — jedna jednostka wewnętrzna i jedna zewnętrzna. Dedykowane dla jednego pokoju.'
|
||||
: `✅ Wybrano: System multisplit — jedna jednostka zewnętrzna obsłuży ${rooms} jednostki wewnętrzne. Lepsza estetyka na zewnątrz budynku.`}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Krok 3: Instalacja */}
|
||||
{step === 3 && (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="size-10 rounded-xl bg-orange-50 flex items-center justify-center">
|
||||
@@ -628,8 +638,8 @@ export function KalkulatorClient({ products }: Props) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Krok 4: Budżet */}
|
||||
{step === 4 && (
|
||||
{/* Krok 3: Budżet */}
|
||||
{step === 3 && (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="size-10 rounded-xl bg-green-50 flex items-center justify-center">
|
||||
@@ -684,7 +694,7 @@ export function KalkulatorClient({ products }: Props) {
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-blue-50 rounded-xl text-xs text-blue-700">
|
||||
💡 Podsumowanie: szukamy klimatyzatora <strong>{category === 'scienne' ? 'split ściennego' : 'multisplit'}</strong> o mocy ok. <strong>{requiredPower} kW</strong> w cenie do <strong>{(budget * 0.75).toLocaleString('pl-PL')} zł</strong>
|
||||
💡 Podsumowanie: szukamy klimatyzatora <strong>{category === 'scienne' ? 'split ściennego' : 'multisplit'}</strong> o mocy ok. <strong>{requiredPowerRounded} kW</strong> w cenie do <strong>{(budget * 0.75).toLocaleString('pl-PL')} zł</strong>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
import Link from "next/link";
|
||||
import { Wrench, Thermometer, Wind, Zap, Calendar, Phone, Check } from "lucide-react";
|
||||
import { companyDetails } from "@/content/company";
|
||||
|
||||
export function HomeServices() {
|
||||
return (
|
||||
<section className="bg-white py-16 px-4">
|
||||
<div className="bg-slate-50 max-w-7xl mx-auto rounded-[2.5rem] p-6 sm:p-10 lg:p-14">
|
||||
<div className="max-w-7xl mx-auto flex flex-col gap-8">
|
||||
<div className="mb-2">
|
||||
<div className="inline-flex items-center gap-1.5 text-[10px] font-bold text-accent uppercase tracking-widest mb-6 bg-accent/5 border border-accent/20 px-3 py-1.5 rounded-full">
|
||||
<Check className="size-3.5" />
|
||||
Cennik usług
|
||||
</div>
|
||||
<h2 className="text-3xl sm:text-5xl font-extrabold text-primary font-heading leading-tight mb-4">
|
||||
Montaż, serwis i czyszczenie klimatyzacji –<br />już od <span className="underline decoration-accent underline-offset-4">150 zł</span>
|
||||
</h2>
|
||||
<p className="text-text-muted text-sm sm:text-base max-w-2xl leading-relaxed">
|
||||
Profesjonalna obsługa, certyfikowani specjaliści i szybki czas reakcji na terenie całego Wrocławia. Zadbaj o czyste powietrze i wydajność swoich urządzeń.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Cards Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
|
||||
{/* Card 1: Montaż klimatyzacji */}
|
||||
<div className="bg-white p-8 flex flex-col rounded-2xl shadow-sm border border-border">
|
||||
<div className="flex flex-col gap-6 mb-12">
|
||||
<div className="size-12 bg-slate-100 flex items-center justify-center shrink-0 border border-slate-200 rounded-lg">
|
||||
<Wrench className="size-5 text-accent" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-primary font-heading mb-3">Montaż klimatyzacji</h3>
|
||||
<p className="text-sm text-text-muted leading-relaxed">Profesjonalny montaż urządzeń split i multisplit. Zapewniamy estetykę wykonania i pełne uruchomienie.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-border pt-5 mt-auto">
|
||||
<p className="text-[10px] font-bold uppercase tracking-widest text-text-muted mb-1">Cena usługi</p>
|
||||
<p className="text-xl font-extrabold text-primary">od 1400 zł</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card 2: Czyszczenie */}
|
||||
<div className="bg-white p-8 flex flex-col rounded-2xl shadow-sm border border-border">
|
||||
<div className="flex flex-col gap-6 mb-12">
|
||||
<div className="size-12 bg-slate-100 flex items-center justify-center shrink-0 border border-slate-200 rounded-lg">
|
||||
<Thermometer className="size-5 text-accent" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-primary font-heading mb-3">Czyszczenie i odgrzybianie</h3>
|
||||
<p className="text-sm text-text-muted leading-relaxed">Kompleksowe czyszczenie, dezynfekcja chemiczna, odgrzybianie parowe, czyszczenie filtrów.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-border pt-5 mt-auto">
|
||||
<p className="text-[10px] font-bold uppercase tracking-widest text-text-muted mb-1">Cena usługi</p>
|
||||
<p className="text-xl font-extrabold text-primary">od 250 zł</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card 3: Przegląd techniczny */}
|
||||
<div className="bg-white p-8 flex flex-col rounded-2xl shadow-sm border border-border">
|
||||
<div className="flex flex-col gap-6 mb-12">
|
||||
<div className="size-12 bg-slate-100 flex items-center justify-center shrink-0 border border-slate-200 rounded-lg">
|
||||
<Wind className="size-5 text-accent" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-primary font-heading mb-3">Przegląd techniczny</h3>
|
||||
<p className="text-sm text-text-muted leading-relaxed">Sprawdzenie parametrów pracy, kontrola szczelności układu, pomiar ciśnień roboczych.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-border pt-5 mt-auto">
|
||||
<p className="text-[10px] font-bold uppercase tracking-widest text-text-muted mb-1">Cena usługi</p>
|
||||
<p className="text-xl font-extrabold text-primary">od 150 zł</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card 4: Uzupełnienie czynnika */}
|
||||
<div className="bg-white p-8 flex flex-col rounded-2xl shadow-sm border border-border">
|
||||
<div className="flex flex-col gap-6 mb-12">
|
||||
<div className="size-12 bg-slate-100 flex items-center justify-center shrink-0 border border-slate-200 rounded-lg">
|
||||
<Zap className="size-5 text-accent" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-primary font-heading mb-3">Uzupełnienie czynnika</h3>
|
||||
<p className="text-sm text-text-muted leading-relaxed">Lokalizacja nieszczelności, próba ciśnieniowa azotem, dobicie czynnika chłodniczego.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-border pt-5 mt-auto">
|
||||
<p className="text-[10px] font-bold uppercase tracking-widest text-text-muted mb-1">Cena usługi</p>
|
||||
<p className="text-xl font-extrabold text-primary">od 180 zł</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card 5: Naprawa usterek */}
|
||||
<div className="bg-white p-8 flex flex-col rounded-2xl shadow-sm border border-border">
|
||||
<div className="flex flex-col gap-6 mb-12">
|
||||
<div className="size-12 bg-slate-100 flex items-center justify-center shrink-0 border border-slate-200 rounded-lg">
|
||||
<Wrench className="size-5 text-accent" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-primary font-heading mb-3">Naprawa usterek</h3>
|
||||
<p className="text-sm text-text-muted leading-relaxed">Diagnoza awarii, wymiana uszkodzonych podzespołów elektroniki, sprężarek, wentylatorów.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-border pt-5 mt-auto">
|
||||
<p className="text-[10px] font-bold uppercase tracking-widest text-text-muted mb-1">Cena usługi</p>
|
||||
<p className="text-sm font-extrabold text-primary">Wycena indywidualna</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card 6: Serwis sezonowy (Dark card) */}
|
||||
<div className="bg-[#1a2332] p-8 flex flex-col border border-[#1a2332] shadow-sm rounded-2xl">
|
||||
<div className="flex flex-col gap-6 mb-12">
|
||||
<div className="size-12 bg-slate-800 flex items-center justify-center shrink-0 border border-slate-700 rounded-lg">
|
||||
<Calendar className="size-5 text-accent" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-white font-heading mb-3">Serwis sezonowy / Abonament</h3>
|
||||
<p className="text-sm text-white/60 leading-relaxed">Regularna opieka nad instalacją przed sezonem letnim i zimowym. Priorytetowy czas reakcji w przypadku awarii.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-white/10 pt-5 mt-auto">
|
||||
<p className="text-[10px] font-bold uppercase tracking-widest text-white/60 mb-1">Cena usługi</p>
|
||||
<p className="text-xl font-extrabold text-white">od 199 zł / rok</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card 6: Gotowy na świeże powietrze? (Full width) */}
|
||||
<div className="bg-white md:col-span-3 p-8 md:p-10 flex flex-col lg:flex-row items-start lg:items-center justify-between gap-8 border-l-[6px] border-accent rounded-2xl shadow-sm">
|
||||
<div className="max-w-2xl">
|
||||
<h3 className="text-2xl font-bold text-primary font-heading mb-3">Gotowy na świeże powietrze?</h3>
|
||||
<p className="text-sm text-text-muted leading-relaxed">Nasi certyfikowani technicy są dostępni od poniedziałku do soboty. Zarezerwuj termin już dziś, aby uniknąć kolejek w sezonie.</p>
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-4 shrink-0 w-full lg:w-auto">
|
||||
<Link href="/kontakt" className="w-full sm:w-auto">
|
||||
<button className="w-full sm:w-auto flex justify-center items-center gap-2 px-6 py-3.5 bg-accent text-white font-bold text-sm hover:bg-accent/90 transition-colors rounded-xl">
|
||||
Umów serwis <Calendar className="size-4" />
|
||||
</button>
|
||||
</Link>
|
||||
<a href={`tel:${companyDetails.phoneRaw}`} className="w-full sm:w-auto">
|
||||
<button className="w-full sm:w-auto flex justify-center items-center gap-2 px-6 py-3.5 bg-white text-primary border border-border font-bold text-sm hover:bg-slate-50 transition-colors rounded-xl">
|
||||
<Phone className="size-4" /> Zadzwoń do nas
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
+19
-1
@@ -1,4 +1,5 @@
|
||||
export interface CityData {
|
||||
przyimek: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
nameDopelniacz: string;
|
||||
@@ -20,6 +21,7 @@ export interface CityData {
|
||||
|
||||
export const dolnySlaskCities: CityData[] = [
|
||||
{
|
||||
przyimek: "we",
|
||||
slug: "wroclaw",
|
||||
name: "Wrocław",
|
||||
nameDopelniacz: "Wrocławiu",
|
||||
@@ -29,7 +31,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
distanceFromHQ: "siedziba firmy",
|
||||
lat: 51.1079,
|
||||
lng: 17.0385,
|
||||
heroImage: "https://images.unsplash.com/photo-1585155784229-aff921ccfa22?w=1600&q=80",
|
||||
heroImage: "/images/cities/wroclaw.webp",
|
||||
seoTitle: "Klimatyzacja Wrocław — Montaż i Serwis | ThermCool",
|
||||
seoDescription: "Montaż klimatyzacji we Wrocławiu od specjalistów z uprawnieniami F-Gaz. Bezpłatny audyt, split i multisplit, serwis gwarancyjny. Zadzwoń po wycenę!",
|
||||
heroSubtitle: "Profesjonalny montaż klimatyzacji we Wrocławiu — split, multisplit, kasetonowe. Bezpłatny audyt, estetyczna instalacja, pełna gwarancja.",
|
||||
@@ -39,6 +41,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Planujesz ogrzewanie pompy ciepła we Wrocławiu? Pomagamy w doborze, montażu i uzyskaniu dofinansowania z programu Czyste Powietrze. Realizujemy instalacje powietrze-woda na terenie całego Wrocławia i powiatu wrocławskiego.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "legnica",
|
||||
name: "Legnica",
|
||||
nameDopelniacz: "Legnicy",
|
||||
@@ -58,6 +61,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Interesuje Cię pompa ciepła w Legnicy? Dobieramy urządzenia dopasowane do zapotrzebowania budynku, pomagamy uzyskać dofinansowanie z Czystego Powietrza i realizujemy kompletną instalację powietrze-woda.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "walbrzych",
|
||||
name: "Wałbrzych",
|
||||
nameDopelniacz: "Wałbrzychu",
|
||||
@@ -77,6 +81,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Wałbrzychu to inwestycja szczególnie opłacalna — wyższe położenie i chłodniejszy klimat sprawiają że urządzenia pracują wydajniej. Pomożemy w doborze, montażu i uzyskaniu dotacji z Czystego Powietrza.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "jelenia-gora",
|
||||
name: "Jelenia Góra",
|
||||
nameDopelniacz: "Jeleniej Górze",
|
||||
@@ -96,6 +101,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Jeleniej Górze — region górski to idealne warunki dla nowoczesnych pomp ciepła, które pracują wydajnie nawet w niskich temperaturach. Realizujemy instalacje z pełną pomocą w uzyskaniu dofinansowania.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "lubin",
|
||||
name: "Lubin",
|
||||
nameDopelniacz: "Lubinie",
|
||||
@@ -115,6 +121,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Lubinie — właściciele domów jednorodzinnych w rejonie Lubina coraz chętniej wymieniają kotły gazowe i węglowe na pompy ciepła. Pomagamy w całym procesie — od audytu przez montaż po dotację.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "glogow",
|
||||
name: "Głogów",
|
||||
nameDopelniacz: "Głogowie",
|
||||
@@ -134,6 +141,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Głogowie i powiecie głogowskim — pomagamy właścicielom domów jednorodzinnych przejść z ogrzewania węglowego na ekologiczne i tańsze w eksploatacji pompy ciepła powietrze-woda.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "swidnica",
|
||||
name: "Świdnica",
|
||||
nameDopelniacz: "Świdnicy",
|
||||
@@ -153,6 +161,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Świdnicy i powiecie świdnickim — realizujemy instalacje powietrze-woda z pełnym wsparciem w procesie aplikacji o dofinansowanie z programu Czyste Powietrze.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "dzierzoniow",
|
||||
name: "Dzierżoniów",
|
||||
nameDopelniacz: "Dzierżoniowie",
|
||||
@@ -172,6 +181,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Dzierżoniowie — dobieramy urządzenia dostosowane do wielkości i izolacji budynku. Pomagamy w uzyskaniu dotacji z Czystego Powietrza, co znacząco obniża koszt inwestycji.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "bielawa",
|
||||
name: "Bielawa",
|
||||
nameDopelniacz: "Bielawie",
|
||||
@@ -191,6 +201,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Bielawie — dla właścicieli domów jednorodzinnych to szansa na uniezależnienie się od rosnących cen gazu i węgla. Realizujemy instalacje z pomocą w dotacjach z Czystego Powietrza.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "olesnica",
|
||||
name: "Oleśnica",
|
||||
nameDopelniacz: "Oleśnicy",
|
||||
@@ -210,6 +221,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Oleśnicy — bliskość Wrocławia i dobra dostępność naszego serwisu sprawiają że obsługa instalacji jest wyjątkowo komfortowa. Realizujemy instalacje powietrze-woda z pełnym pakietem dofinansowania.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "olawa",
|
||||
name: "Oława",
|
||||
nameDopelniacz: "Oławie",
|
||||
@@ -229,6 +241,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Oławie — realizujemy instalacje dla domów jednorodzinnych w Oławie i całym powiecie oławskim. Krótki dojazd to też sprawny serwis przez cały okres użytkowania urządzenia.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "strzelin",
|
||||
name: "Strzelin",
|
||||
nameDopelniacz: "Strzelinie",
|
||||
@@ -248,6 +261,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Strzelinie — powiat strzeliński to rejon gdzie wiele domów nadal ogrzewa się węglem. Pompa ciepła to ekologiczna i ekonomiczna alternatywa, a dofinansowanie z Czystego Powietrza czyni ją realnie dostępną.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "sroda-slaska",
|
||||
name: "Środa Śląska",
|
||||
nameDopelniacz: "Środzie Śląskiej",
|
||||
@@ -267,6 +281,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Środzie Śląskiej — przy nowych domach jednorodzinnych w tej okolicy pompa ciepła jest często wybierana jako podstawowe źródło ogrzewania. Pomagamy w całym procesie inwestycji.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "trzebnica",
|
||||
name: "Trzebnica",
|
||||
nameDopelniacz: "Trzebnicy",
|
||||
@@ -286,6 +301,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Trzebnicy — obsługujemy zarówno prywatne domy jak i większe obiekty. Bliskość Wrocławia to szybki serwis i krótkie czasy reakcji przez cały okres użytkowania pompy.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "siechnice",
|
||||
name: "Siechnice",
|
||||
nameDopelniacz: "Siechnicach",
|
||||
@@ -305,6 +321,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Pompa ciepła w Siechnicach — przy nowych domach w tej gminie pompa ciepła jest standardowym wyborem. Realizujemy instalacje powietrze-woda i gruntowe, doradzamy w wyborze optymalnego rozwiązania dla danej działki.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "opole",
|
||||
name: "Opole",
|
||||
nameDopelniacz: "Opolu",
|
||||
@@ -324,6 +341,7 @@ export const dolnySlaskCities: CityData[] = [
|
||||
heatPumpTeaser: "Interesuje Cię pompa ciepła w Opolu? Realizujemy instalacje powietrze-woda z pomocą w uzyskaniu dofinansowania z programu Czyste Powietrze. Skontaktuj się z nami — dobierzemy optymalne rozwiązanie dla Twojego budynku.",
|
||||
},
|
||||
{
|
||||
przyimek: "w",
|
||||
slug: "klodzko",
|
||||
name: "Kłodzko",
|
||||
nameDopelniacz: "Kłodzku",
|
||||
|
||||
+63
-23
@@ -10,39 +10,63 @@ export interface PricingItem {
|
||||
export const pricingData: PricingItem[] = [
|
||||
{
|
||||
id: 'montaz-split-podstawowy',
|
||||
name: 'Montaż klimatyzacji typu SPLIT (do 3.5 kW)',
|
||||
priceNet: 1200,
|
||||
priceGrossPrivate: 1296,
|
||||
priceGrossCompany: 1476,
|
||||
name: 'Montaż klimatyzatora split — podstawowy (do 4,5 kW)',
|
||||
priceNet: 1400,
|
||||
priceGrossPrivate: 1512,
|
||||
priceGrossCompany: 1722,
|
||||
category: 'split',
|
||||
},
|
||||
{
|
||||
id: 'montaz-split-rozszerzony',
|
||||
name: 'Montaż klimatyzacji typu SPLIT (powyżej 3.5 kW)',
|
||||
priceNet: 1500,
|
||||
priceGrossPrivate: 1620,
|
||||
priceGrossCompany: 1845,
|
||||
id: 'montaz-split-istniejaca',
|
||||
name: 'Montaż klimatyzatora split przy istniejącej instalacji',
|
||||
priceNet: 800,
|
||||
priceGrossPrivate: 864,
|
||||
priceGrossCompany: 984,
|
||||
category: 'split',
|
||||
},
|
||||
{
|
||||
id: 'montaz-multisplit-2',
|
||||
name: 'Montaż klimatyzacji MULTISPLIT (2 jednostki wew.)',
|
||||
priceNet: 2200,
|
||||
priceGrossPrivate: 2376,
|
||||
priceGrossCompany: 2706,
|
||||
name: 'Montaż multisplit 2+1 — podstawowy (do 6 m instalacji)',
|
||||
priceNet: 2400,
|
||||
priceGrossPrivate: 2592,
|
||||
priceGrossCompany: 2952,
|
||||
category: 'multisplit',
|
||||
},
|
||||
{
|
||||
id: 'montaz-multisplit-3',
|
||||
name: 'Montaż klimatyzacji MULTISPLIT (3 jednostki wew.)',
|
||||
priceNet: 3000,
|
||||
priceGrossPrivate: 3240,
|
||||
priceGrossCompany: 3690,
|
||||
name: 'Montaż multisplit 3+1 — podstawowy (do 9 m instalacji)',
|
||||
priceNet: 3200,
|
||||
priceGrossPrivate: 3456,
|
||||
priceGrossCompany: 3936,
|
||||
category: 'multisplit',
|
||||
},
|
||||
{
|
||||
id: 'dodatkowy-metr',
|
||||
name: 'Dodatkowy metr instalacji chłodniczej',
|
||||
id: 'montaz-multisplit-4',
|
||||
name: 'Montaż multisplit 4+1 — podstawowy (do 12 m instalacji)',
|
||||
priceNet: 3800,
|
||||
priceGrossPrivate: 4104,
|
||||
priceGrossCompany: 4674,
|
||||
category: 'multisplit',
|
||||
},
|
||||
{
|
||||
id: 'dodatkowy-metr-korytko',
|
||||
name: 'Dodatkowy metr instalacji freonowej — korytko PCV',
|
||||
priceNet: 80,
|
||||
priceGrossPrivate: 86,
|
||||
priceGrossCompany: 98,
|
||||
category: 'additional',
|
||||
},
|
||||
{
|
||||
id: 'dodatkowy-metr-bruzda',
|
||||
name: 'Dodatkowy metr instalacji freonowej — bruzda w ścianie',
|
||||
priceNet: 90,
|
||||
priceGrossPrivate: 97,
|
||||
priceGrossCompany: 111,
|
||||
category: 'additional',
|
||||
},
|
||||
{
|
||||
id: 'przewiert',
|
||||
name: 'Dopłata za przewiert otworu instalacyjnego w betonie',
|
||||
priceNet: 100,
|
||||
priceGrossPrivate: 108,
|
||||
priceGrossCompany: 123,
|
||||
@@ -50,18 +74,34 @@ export const pricingData: PricingItem[] = [
|
||||
},
|
||||
{
|
||||
id: 'kucie-bruzdy',
|
||||
name: 'Kucie bruzdy w betonie/żelbecie (mb)',
|
||||
name: 'Dopłata za kucie bruzdy instalacyjnej w żelbecie',
|
||||
priceNet: 150,
|
||||
priceGrossPrivate: 162,
|
||||
priceGrossCompany: 184.5,
|
||||
priceGrossCompany: 184,
|
||||
category: 'additional',
|
||||
},
|
||||
{
|
||||
id: 'pompa-skroplin',
|
||||
name: 'Instalacja pompy skroplin',
|
||||
priceNet: 400,
|
||||
priceGrossPrivate: 432,
|
||||
priceGrossCompany: 492,
|
||||
category: 'additional',
|
||||
},
|
||||
{
|
||||
id: 'demontaz',
|
||||
name: 'Demontaż klimatyzacji',
|
||||
priceNet: 300,
|
||||
priceGrossPrivate: 324,
|
||||
priceGrossCompany: 369,
|
||||
category: 'additional',
|
||||
},
|
||||
{
|
||||
id: 'przeglad-klimatyzacji',
|
||||
name: 'Okresowy przegląd i odgrzybianie klimatyzacji',
|
||||
name: 'Czyszczenie i odgrzybianie klimatyzatora',
|
||||
priceNet: 250,
|
||||
priceGrossPrivate: 270,
|
||||
priceGrossCompany: 307.5,
|
||||
priceGrossCompany: 307,
|
||||
category: 'additional',
|
||||
},
|
||||
];
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
Reference in New Issue
Block a user