Files
thermcool/components/awaria/AwariaClient.tsx
T

440 lines
20 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useState } from 'react';
import { AlertTriangle, Wrench, Phone, Check, Clock, Zap } from 'lucide-react';
import Link from 'next/link';
import { companyDetails } from '@/content/company';
import { submitAwariaForm } from '@/app/zgloszenie-awarii/actions';
// Kody błędów popularnych marek
const ERROR_CODES: Record<string, Record<string, string>> = {
'Gree': {
'E1': 'Błąd czujnika temperatury powietrza wewnętrznego',
'E2': 'Błąd czujnika temperatury wymiennika wewnętrznego',
'E3': 'Błąd czujnika temperatury zewnętrznej',
'E4': 'Wysokie ciśnienie — możliwy problem ze sprężarką lub skraplaczem',
'E5': 'Niskie ciśnienie — możliwy brak czynnika chłodniczego',
'E6': 'Błąd komunikacji między jednostkami',
'F1': 'Awaria czujnika temperatury — wymagana wymiana czujnika',
'H6': 'Blokada silnika wentylatora wewnętrznego',
},
'LG': {
'CH01': 'Błąd czujnika temperatury powietrza wewnętrznego',
'CH02': 'Błąd czujnika wymiennika wewnętrznego',
'CH03': 'Błąd czujnika wymiennika zewnętrznego',
'CH05': 'Błąd komunikacji — sprawdź okablowanie',
'CH07': 'Awaria wentylatora zewnętrznego',
'CH10': 'Błąd czujnika prądu sprężarki',
'CH38': 'Błąd modułu WiFi',
'C1': 'Problem z drenażem — sprawdź odpływ skroplin',
},
'Daikin': {
'A1': 'Awaria PCB — płyta główna jednostki wewnętrznej',
'A5': 'Przegrzanie — wymagane czyszczenie lub sprawdzenie czynnika',
'C4': 'Błąd czujnika wymiennika wewnętrznego',
'E1': 'Awaria PCB jednostki zewnętrznej',
'E3': 'Wyłącznik wysokiego ciśnienia',
'E4': 'Wyłącznik niskiego ciśnienia',
'F3': 'Niskie ciśnienie oleju sprężarki',
'U4': 'Błąd komunikacji wewnętrzna-zewnętrzna',
},
'Samsung': {
'E1': 'Błąd czujnika temperatury powietrza',
'E2': 'Błąd czujnika wymiennika ciepła',
'E3': 'Błąd silnika wentylatora',
'E4': 'Wyłącznik wysokiego ciśnienia',
'E6': 'Błąd komunikacji',
'F1': 'Awaria czujnika — wymagana diagnostyka',
'C4': 'Błąd czujnika temperatury zewnętrznej',
},
'Haier': {
'E1': 'Błąd czujnika temperatury wewnętrznej',
'E2': 'Błąd czujnika wymiennika wewnętrznego',
'E3': 'Błąd czujnika temperatury zewnętrznej',
'E4': 'Wyłącznik wysokiego ciśnienia',
'E5': 'Wyłącznik niskiego ciśnienia — sprawdź poziom czynnika',
'E6': 'Błąd komunikacji',
'F1': 'Awaria czujnika — wymiana',
},
'Rotenso': {
'E1': 'Błąd czujnika temperatury wewnętrznej',
'E2': 'Błąd wymiennika wewnętrznego',
'E3': 'Błąd czujnika zewnętrznego',
'E5': 'Niskie ciśnienie — sprawdź czynnik chłodniczy',
'E6': 'Błąd komunikacji między jednostkami',
'P1': 'Ochrona przed zamrożeniem',
'P4': 'Wyłącznik wysokiego ciśnienia',
},
'Mitsubishi Electric': {
'E1': 'Błąd czujnika wewnętrznego',
'E2': 'Błąd czujnika wymiennika',
'E4': 'Błąd czujnika zewnętrznego',
'E6': 'Problem z wentylatorem zewnętrznym',
'P1': 'Błąd czujnika powietrza zewnętrznego',
'P4': 'Wyłącznik wysokiego ciśnienia',
'P8': 'Przegrzanie sprężarki',
'U1': 'Błąd napięcia zasilania',
},
'Toshiba': {
'E01': 'Błąd czujnika temperatury wewnętrznej',
'E02': 'Błąd czujnika wymiennika wewnętrznego',
'E03': 'Błąd czujnika zewnętrznego',
'E04': 'Wyłącznik wysokiego ciśnienia',
'E07': 'Błąd komunikacji',
'E08': 'Awaria wentylatora zewnętrznego',
},
};
const BRANDS = ['Gree', 'Rotenso', 'Haier', 'LG', 'Samsung', 'Daikin', 'Mitsubishi Electric', 'Toshiba', 'Aux', 'Fuji Electric', 'Inna marka'];
const PROBLEM_TYPES = [
'Nie chłodzi / nie grzeje',
'Wyświetla kod błędu',
'Głośna praca / wibracje',
'Wyciek wody',
'Brzydki zapach',
'Nie włącza się',
'Pilot nie działa',
'Zamarza wymiennik',
'Inne',
];
export function AwariaClient() {
const [step, setStep] = useState<'form' | 'success'>('form');
const [isUrgent, setIsUrgent] = useState(false);
const [brand, setBrand] = useState('');
const [model, setModel] = useState('');
const [errorCode, setErrorCode] = useState('');
const [problemType, setProblemType] = useState('');
const [description, setDescription] = useState('');
const [lastService, setLastService] = useState('');
const [warranty, setWarranty] = useState('');
const [name, setName] = useState('');
const [phone, setPhone] = useState('');
const [city, setCity] = useState('');
const [preferredDate, setPreferredDate] = useState('');
const [preferredTime, setPreferredTime] = useState('');
const [consent, setConsent] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
// Podpowiedź kodu błędu
const errorHint = brand && errorCode && ERROR_CODES[brand]?.[errorCode.toUpperCase()];
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!consent) {
alert('Proszę zaznaczyć zgodę na przetwarzanie danych.');
return;
}
setIsSubmitting(true);
await submitAwariaForm({
isUrgent,
brand,
model,
errorCode,
problemType,
description,
warranty,
lastService,
name,
phone,
city,
preferredDate,
preferredTime,
});
setIsSubmitting(false);
setStep('success');
};
if (step === 'success') {
return (
<main className="bg-slate-50 min-h-screen flex items-center justify-center py-16">
<div className="max-w-md mx-auto px-4 text-center">
<div className="size-20 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-6">
<Check className="size-10 text-green-600" />
</div>
<h1 className="text-2xl font-extrabold text-primary font-heading mb-3">
Zgłoszenie przyjęte!
</h1>
<p className="text-text-muted mb-2">
{isUrgent
? 'Twoje zgłoszenie oznaczono jako PILNE — skontaktujemy się z Tobą w ciągu 2 godzin.'
: 'Skontaktujemy się z Tobą w ciągu 24 godzin, aby ustalić termin wizyty serwisanta.'}
</p>
<p className="text-sm text-text-muted mb-8">
Jeśli sprawa jest pilna — zadzwoń bezpośrednio:
</p>
<a href={`tel:${companyDetails.phoneRaw}`}>
<button className="flex items-center gap-2 px-6 py-3 bg-accent text-white rounded-xl font-bold mx-auto hover:bg-accent/90 transition-colors mb-4">
<Phone className="size-4" /> {companyDetails.phone}
</button>
</a>
<Link href="/">
<button className="text-sm text-text-muted hover:text-accent transition-colors">
Wróć na stronę główną
</button>
</Link>
</div>
</main>
);
}
return (
<main className="bg-slate-50 min-h-screen py-12">
<div className="max-w-2xl mx-auto px-4 sm:px-6">
{/* Header */}
<div className="text-center mb-8">
<div className="size-14 bg-red-100 rounded-2xl flex items-center justify-center mx-auto mb-4">
<Wrench className="size-7 text-red-600" />
</div>
<h1 className="text-3xl font-extrabold text-primary font-heading mb-2">
Zgłoś awarię / serwis
</h1>
<p className="text-text-muted">Wypełnij formularz — oddzwonimy i ustalimy termin wizyty</p>
</div>
{/* Pilne zgłoszenie toggle */}
<div
onClick={() => setIsUrgent(!isUrgent)}
className={`flex items-center gap-4 p-4 rounded-2xl border-2 cursor-pointer mb-6 transition-all ${
isUrgent ? 'border-red-400 bg-red-50' : 'border-border bg-white hover:border-red-300'
}`}
>
<div className={`size-10 rounded-xl flex items-center justify-center shrink-0 ${isUrgent ? 'bg-red-500' : 'bg-slate-100'}`}>
<AlertTriangle className={`size-5 ${isUrgent ? 'text-white' : 'text-text-muted'}`} />
</div>
<div className="flex-1">
<p className={`font-bold text-sm ${isUrgent ? 'text-red-700' : 'text-primary'}`}>
{isUrgent ? '🚨 ZGŁOSZENIE PILNE — kontakt w ciągu 2 godzin' : 'Oznacz jako PILNE'}
</p>
<p className="text-xs text-text-muted">Np. upał, brak chłodzenia, awaria krytyczna</p>
</div>
<div className={`size-6 rounded-full border-2 flex items-center justify-center shrink-0 ${isUrgent ? 'border-red-500 bg-red-500' : 'border-slate-300'}`}>
{isUrgent && <Check className="size-3.5 text-white" />}
</div>
</div>
<form onSubmit={handleSubmit} className="flex flex-col gap-5">
{/* Sekcja 1: Urządzenie */}
<div className="bg-white rounded-2xl border border-border p-6">
<h2 className="text-base font-bold text-primary font-heading mb-4 flex items-center gap-2">
<Zap className="size-4 text-accent" /> Urządzenie
</h2>
<div className="flex flex-col gap-4">
<div>
<label className="text-xs font-medium text-primary mb-1 block">Marka *</label>
<select
required value={brand} onChange={e => { setBrand(e.target.value); setErrorCode(''); }}
className="w-full h-[42px] rounded-xl px-3 py-2.5 text-sm border border-border focus:border-accent outline-none bg-white"
>
<option value="">Wybierz markę...</option>
{BRANDS.map(b => <option key={b} value={b}>{b}</option>)}
</select>
</div>
<div>
<label className="text-xs font-medium text-primary mb-1 block">Model (opcjonalnie)</label>
<input
value={model} onChange={e => setModel(e.target.value)}
className="w-full h-[42px] rounded-xl px-3 py-2.5 text-sm border border-border focus:border-accent outline-none"
placeholder="Np. Gree Pular, LG Artcool 3,5 kW..."
/>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label className="text-xs font-medium text-primary mb-1 block">Na gwarancji?</label>
<select
value={warranty} onChange={e => setWarranty(e.target.value)}
className="w-full h-[42px] rounded-xl px-3 py-2.5 text-sm border border-border focus:border-accent outline-none bg-white"
>
<option value="">Nie wiem</option>
<option value="tak">Tak</option>
<option value="nie">Nie</option>
</select>
</div>
<div>
<label className="text-xs font-medium text-primary mb-1 block">Ostatni serwis</label>
<select
value={lastService} onChange={e => setLastService(e.target.value)}
className="w-full h-[42px] rounded-xl px-3 py-2.5 text-sm border border-border focus:border-accent outline-none bg-white"
>
<option value="">Nie pamiętam</option>
<option value="<1rok">Mniej niż rok temu</option>
<option value="1-2lata">1-2 lata temu</option>
<option value="2-5lat">2-5 lat temu</option>
<option value=">5lat">Ponad 5 lat temu</option>
<option value="nigdy">Nigdy nie był serwisowany</option>
</select>
</div>
</div>
</div>
</div>
{/* Sekcja 2: Problem */}
<div className="bg-white rounded-2xl border border-border p-6">
<h2 className="text-base font-bold text-primary font-heading mb-4 flex items-center gap-2">
<AlertTriangle className="size-4 text-accent" /> Opis problemu
</h2>
<div className="flex flex-col gap-4">
<div>
<label className="text-xs font-medium text-primary mb-2 block">Rodzaj problemu *</label>
<div className="flex flex-wrap gap-2">
{PROBLEM_TYPES.map(p => (
<button
key={p} type="button"
onClick={() => setProblemType(p)}
className={`px-3 py-1.5 rounded-xl text-xs font-semibold border-2 transition-all ${
problemType === p ? 'border-accent bg-accent text-white' : 'border-border bg-white text-primary hover:border-accent'
}`}
>
{p}
</button>
))}
</div>
</div>
{/* Kod błędu z podpowiedzią */}
<div>
<label className="text-xs font-medium text-primary mb-1 block">
Kod błędu z wyświetlacza (opcjonalnie)
</label>
<input
value={errorCode}
onChange={e => setErrorCode(e.target.value)}
className="w-full h-[42px] rounded-xl px-3 py-2.5 text-sm border border-border focus:border-accent outline-none"
placeholder="Np. E1, F3, CH05..."
/>
{errorHint && (
<div className="mt-2 p-3 bg-blue-50 border border-blue-200 rounded-xl text-xs text-blue-700">
<span className="font-bold">Kod {errorCode.toUpperCase()}:</span> {errorHint}
</div>
)}
{errorCode && !errorHint && brand && ERROR_CODES[brand] && (
<p className="mt-1 text-xs text-text-muted">Kodu nie rozpoznano — serwisant zdiagnozuje na miejscu.</p>
)}
</div>
<div>
<label className="text-xs font-medium text-primary mb-1 block">Szczegółowy opis</label>
<textarea
value={description} onChange={e => setDescription(e.target.value)}
rows={3}
className="w-full rounded-xl px-3 py-2.5 text-sm border border-border focus:border-accent outline-none resize-none"
placeholder="Opisz kiedy się pojawił problem, jak często występuje, czy jest jakiś charakterystyczny dźwięk..."
/>
</div>
</div>
</div>
{/* Sekcja 3: Termin */}
<div className="bg-white rounded-2xl border border-border p-6">
<h2 className="text-base font-bold text-primary font-heading mb-4 flex items-center gap-2">
<Clock className="size-4 text-accent" /> Preferowany termin
</h2>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs font-medium text-primary mb-1 block">Data</label>
<input
type="date" value={preferredDate} onChange={e => setPreferredDate(e.target.value)}
min={new Date().toISOString().split('T')[0]}
className="w-full h-[42px] rounded-xl px-3 py-2.5 text-sm border border-border focus:border-accent outline-none"
/>
</div>
<div>
<label className="text-xs font-medium text-primary mb-1 block">Godzina</label>
<select
value={preferredTime} onChange={e => setPreferredTime(e.target.value)}
className="w-full h-[42px] rounded-xl px-3 py-2.5 text-sm border border-border focus:border-accent outline-none bg-white"
>
<option value="">Dowolna</option>
<option value="8:00-10:00">8:00 – 10:00</option>
<option value="10:00-12:00">10:00 – 12:00</option>
<option value="12:00-14:00">12:00 – 14:00</option>
<option value="14:00-16:00">14:00 – 16:00</option>
<option value="16:00-18:00">16:00 – 18:00</option>
</select>
</div>
</div>
</div>
{/* Sekcja 4: Dane kontaktowe */}
<div className="bg-white rounded-2xl border border-border p-6">
<h2 className="text-base font-bold text-primary font-heading mb-4 flex items-center gap-2">
<Phone className="size-4 text-accent" /> Dane kontaktowe
</h2>
<div className="flex flex-col gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label className="text-xs font-medium text-primary mb-1 block">Imię i nazwisko *</label>
<input
required value={name} onChange={e => setName(e.target.value)}
className="w-full h-[42px] rounded-xl px-3 py-2.5 text-sm border border-border focus:border-accent outline-none"
placeholder="Jan Kowalski"
/>
</div>
<div>
<label className="text-xs font-medium text-primary mb-1 block">Telefon *</label>
<input
required type="tel" value={phone} onChange={e => setPhone(e.target.value)}
className="w-full h-[42px] rounded-xl px-3 py-2.5 text-sm border border-border focus:border-accent outline-none"
placeholder="+48 600 000 000"
/>
</div>
</div>
<div>
<label className="text-xs font-medium text-primary mb-1 block">Miasto *</label>
<select
required value={city} onChange={e => setCity(e.target.value)}
className="w-full h-[42px] rounded-xl px-3 py-2.5 text-sm border border-border focus:border-accent outline-none bg-white"
>
<option value="">Wybierz miasto...</option>
{['Wrocław', 'Legnica', 'Wałbrzych', 'Jelenia Góra', 'Lubin', 'Głogów', 'Świdnica', 'Dzierżoniów', 'Oleśnica', 'Oława', 'Trzebnica', 'Środa Śląska', 'Siechnice', 'Inne'].map(c => (
<option key={c} value={c}>{c}</option>
))}
</select>
</div>
</div>
</div>
{/* Zgoda RODO */}
<div
className="flex items-start gap-3 cursor-pointer"
onClick={() => setConsent(!consent)}
>
<div className={`size-5 rounded border-2 shrink-0 mt-0.5 flex items-center justify-center transition-colors ${
consent ? 'bg-accent border-accent' : 'bg-white border-slate-300'
}`}>
{consent && <Check className="size-3 text-white" />}
</div>
<span className="text-xs text-text-muted leading-relaxed">
Wyrażam zgodę na przetwarzanie danych osobowych podanych w formularzu
w celu obsługi zgłoszenia serwisowego przez firmę ThermCool. *
</span>
</div>
{/* Submit */}
<button
type="submit"
disabled={isSubmitting || !brand || !problemType || !name || !phone || !city || !consent}
className={`flex items-center justify-center gap-2 py-4 px-6 rounded-2xl font-bold text-base transition-all ${
isUrgent
? 'bg-red-500 hover:bg-red-600 text-white'
: 'bg-accent hover:bg-accent/90 text-white'
} disabled:opacity-50 disabled:cursor-not-allowed`}
>
{isSubmitting ? 'Wysyłanie...' : isUrgent ? '🚨 Wyślij PILNE zgłoszenie' : 'Wyślij zgłoszenie serwisowe'}
</button>
<p className="text-xs text-text-muted text-center">
{isUrgent ? 'Odpowiadamy na pilne zgłoszenia w ciągu 2 godzin w godzinach pracy.' : 'Skontaktujemy się w ciągu 24 godzin.'}
{' '}Możesz też zadzwonić bezpośrednio: <a href={`tel:${companyDetails.phoneRaw}`} className="text-accent font-semibold">{companyDetails.phone}</a>
</p>
</form>
</div>
</main>
);
}