'use client'; import { useState, useEffect, useMemo } from 'react'; import Link from 'next/link'; import { ArrowRight, ArrowLeft, Check, Home, Zap, ShoppingCart, Phone } from 'lucide-react'; import { Product } from '@/content/products'; import { companyDetails } from '@/content/company'; import { ProductCard } from '@/components/shop/ProductCard'; interface Props { products: Product[]; } // Hook do zapisywania stanu w sessionStorage function useSessionState(key: string, initialValue: T) { const [state, setState] = useState(initialValue); useEffect(() => { try { const item = window.sessionStorage.getItem(key); if (item) { const timer = setTimeout(() => setState(JSON.parse(item)), 0); return () => clearTimeout(timer); } } catch {} }, [key]); const setValue = (value: T | ((val: T) => T)) => { try { const valueToStore = value instanceof Function ? value(state) : value; setState(valueToStore); if (typeof window !== 'undefined') { window.sessionStorage.setItem(key, JSON.stringify(valueToStore)); } } catch {} }; return [state, setValue] as const; } // Oblicz wymaganą moc chłodniczą w kW function calculatePower(m2: number, floor: string, sun: string, building: string): number { let power = m2 * 0.1; // baza: 1 kW na 10 m² // Korekta typu budynku i piętra if (building === 'apartment') { if (floor === 'top') power *= 1.2; // ostatnie piętro - więcej ciepła z dachu if (floor === 'ground') power *= 0.95; } else if (building === 'house') { if (floor === 'top') power *= 1.1; // poddasze - dodatkowe nagrzewanie } // Korekta nasłonecznienia if (sun === 'south') power *= 1.15; // południe - duże nasłonecznienie if (sun === 'west') power *= 1.1; if (sun === 'north') power *= 0.9; return Math.round(power * 10) / 10; } // Szacunkowy koszt montażu function estimateInstall(pipes: string, rooms: number, installMethod: string, outdoorUnitLocation: string, hasSocket: string): { min: number; max: number } { const base = rooms === 1 ? 1400 : rooms === 2 ? 2400 : rooms === 3 ? 3200 : 3800; // Przybliżona liczba dodatkowych metrów (powyżej 3m) const extraMeters = pipes === 'short' ? 0 : pipes === 'medium' ? 3 : pipes === 'long' ? 7 : 15; const methodPrice = installMethod === 'bruzda' ? 90 : 80; // zł za metr // Zostawmy bazowe koszty rur miedzianych (np. miedź, kable) i dodajmy koszt bruzdy/korytka const pipeBaseCost = pipes === 'short' ? 0 : pipes === 'medium' ? 200 : pipes === 'long' ? 500 : 1000; // Elewacja: 400 zł (pompka) + ok. 600 zł (1h podnośnika jako bufor) const facadeCost = outdoorUnitLocation === 'facade' ? 1000 : 0; // Brak gniazdka: dodatkowy koszt ok 200 zł za dociągnięcie zasilania const socketCost = hasSocket !== 'yes' ? 200 : 0; const pipeExtra = pipeBaseCost + (extraMeters * methodPrice) + facadeCost + socketCost; return { min: base + pipeExtra, max: base + pipeExtra + 600 }; } const STEPS = ['Typ', 'Pomieszczenie', 'Instalacja', 'Budżet', 'Dobór'] as const; export function KalkulatorClient({ products }: Props) { const [showWidthWarning, setShowWidthWarning] = useState(false); const [maxWidth, setMaxWidth] = useState(80); const [deviceType, setDeviceType] = useSessionState<'klimatyzacja' | 'pompa'>('calc_deviceType', 'klimatyzacja'); const [pumpFormSent, setPumpFormSent] = useState(false); const [pumpForm, setPumpForm] = useState({ name: '', phone: '', city: '', message: '' }); const [step, setStep] = useSessionState('calc_step', 0); const [building, setBuilding] = useSessionState('calc_building', 'apartment'); const [pomieszczenia, setPomieszczenia] = useSessionState<{ nazwa: string; m2: number }[]>('calc_pomieszczenia', [ { nazwa: 'Pokój 1', m2: 20 } ]); const [floor, setFloor] = useSessionState('calc_floor', 'middle'); // ground | middle | top const [sun, setSun] = useSessionState('calc_sun', 'south'); // north | east | south | west 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] = useSessionState('calc_pipes', 'medium'); // short(<3m) | medium(3-6m) | long(6-10m) | verylong(>10m) const [installMethod, setInstallMethod] = useSessionState<'korytko' | 'bruzda'>('calc_installMethod', 'korytko'); const [outdoorUnitLocation, setOutdoorUnitLocation] = useSessionState<'balcony' | 'facade' | 'roof'>('calc_outdoorLocation', 'balcony'); const [hasSocket, setHasSocket] = useSessionState<'yes' | 'no' | 'dont_know'>('calc_hasSocket', 'yes'); const [budget, setBudget] = useSessionState('calc_budget', 8000); const [preferredBrand, setPreferredBrand] = useSessionState('calc_brand', ''); const [energyClass, setEnergyClass] = useSessionState('calc_energy', ''); const [maxNoise, setMaxNoise] = useSessionState('calc_maxNoise', null); const [maxNoiseOut, setMaxNoiseOut] = useSessionState('calc_maxNoiseOut', null); const [page, setPage] = useSessionState('calc_page', 1); 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, installMethod, outdoorUnitLocation, hasSocket), [pipes, rooms, installMethod, outdoorUnitLocation, hasSocket]); const category = rooms === 1 ? 'scienne' : 'multisplit'; const results = useMemo(() => { if (step < 4 || deviceType === 'pompa') return []; const maxPrice = budget * 0.75; const powerMin = requiredPower * 0.6; const powerMax = requiredPower * 1.8; const filtered = products .filter(p => { if (p.category !== category) return false; if (preferredBrand && p.brand !== preferredBrand) return false; if (energyClass) { const pClass = p.specs?.['Klasa energetyczna']; if (!pClass || !pClass.includes(energyClass)) return false; } if (maxNoise !== null) { const noiseStr = p.specs?.['Hałas wewnątrz (dB)']; if (!noiseStr) return false; // Hałas jest zapisany np. "19–38" - bierzemy dolną granicę const minNoise = parseInt(noiseStr.split('–')[0].trim()); if (isNaN(minNoise) || minNoise > maxNoise) return false; } if (maxNoiseOut !== null) { const noiseOutStr = p.specs?.['Hałas zewnątrz (dB)']; // Jeśli produktu nie ma tego parametru, odrzucamy, jeśli nałożono ścisły filtr if (!noiseOutStr) return false; const minNoiseOut = parseInt(noiseOutStr.split('–')[0].trim()); if (isNaN(minNoiseOut) || minNoiseOut > maxNoiseOut) return false; } if (category === 'multisplit') { if (p.price !== null && p.price > maxPrice) return false; } else { if (p.price === null || p.price > maxPrice) return false; } const moc = parseFloat(String(p.specs?.['Moc chłodnicza'] || '0').replace(',', '.').split('–')[0]); return moc >= powerMin && moc <= powerMax; }) .sort((a, b) => { // Sortuj po tym jak blisko jest moc do wymaganej const mocA = parseFloat(String(a.specs?.['Moc chłodnicza'] || '0').replace(',', '.').split('–')[0]); const mocB = parseFloat(String(b.specs?.['Moc chłodnicza'] || '0').replace(',', '.').split('–')[0]); return Math.abs(mocA - requiredPower) - Math.abs(mocB - requiredPower); }); return filtered.length > 0 ? filtered : products .filter(p => p.category === category && p.price !== null) .sort((a, b) => (a.price || 0) - (b.price || 0)); }, [step, deviceType, products, requiredPower, category, budget, preferredBrand, energyClass, maxNoise, maxNoiseOut]); const paginatedResults = useMemo(() => results.slice((page - 1) * 9, page * 9), [results, page]); const totalPages = Math.ceil(results.length / 9); const getPageNumbers = () => { const pages = []; if (totalPages <= 7) { for (let i = 1; i <= totalPages; i++) pages.push(i); } else { if (page <= 4) { pages.push(1, 2, 3, 4, 5, '...', totalPages); } else if (page >= totalPages - 3) { pages.push(1, '...', totalPages - 4, totalPages - 3, totalPages - 2, totalPages - 1, totalPages); } else { pages.push(1, '...', page - 1, page, page + 1, '...', totalPages); } } return pages; }; const handleNext = () => { setStep(s => s + 1); setPage(1); window.scrollTo({ top: 0, behavior: 'smooth' }); }; const handlePumpSubmit = async () => { if (!pumpForm.name || !pumpForm.phone || !pumpForm.city) return; // Na razie pokazujemy success — podepniemy Resend później setPumpFormSent(true); }; return (
{/* Header */}

Dobierz urządzenie

Odpowiedz na {STEPS.length} krótkich pytań — pokażemy Ci najlepsze opcje z naszej oferty

{/* Pasek postępu */}
{STEPS.map((s, i) => (
{i < step ? : i + 1}
{i < STEPS.length - 1 && (
)}
))}
{/* Karty kroków */}
{/* Krok 0: Typ urządzenia */} {step === 0 && (

Co chcesz zainstalować?

{deviceType === 'pompa' && !pumpFormSent && (

Wypełnij formularz — oddzwonimy w celu ustalenia terminu bezpłatnej i niezobowiązującej wizji lokalnej

setPumpForm(f => ({...f, name: e.target.value}))} />
setPumpForm(f => ({...f, phone: e.target.value}))} />
setPumpForm(f => ({...f, city: e.target.value}))} />