Uporzadkowanie nawigacji i formularzy: ukrycie O nas, ulepszenie kalkulatora i uproszczenie zgloszenia awarii
This commit is contained in:
+1
-1
@@ -14,7 +14,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
||||
|
||||
'/realizacje',
|
||||
'/blog',
|
||||
'/o-nas',
|
||||
// '/o-nas',
|
||||
'/kontakt',
|
||||
'/sklep',
|
||||
'/koszyk',
|
||||
|
||||
@@ -111,8 +111,6 @@ export function AwariaClient() {
|
||||
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);
|
||||
|
||||
@@ -139,8 +137,8 @@ export function AwariaClient() {
|
||||
name,
|
||||
phone,
|
||||
city,
|
||||
preferredDate,
|
||||
preferredTime,
|
||||
preferredDate: '',
|
||||
preferredTime: '',
|
||||
});
|
||||
|
||||
setIsSubmitting(false);
|
||||
@@ -329,36 +327,7 @@ export function AwariaClient() {
|
||||
</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">
|
||||
|
||||
@@ -21,6 +21,7 @@ type ContactFormValues = z.infer<typeof contactFormSchema>;
|
||||
export function ContactForm() {
|
||||
const [serverState, setServerState] = useState<{ success?: boolean; message?: string } | null>(null);
|
||||
const [consent, setConsent] = useState(false);
|
||||
const [files, setFiles] = useState<File[]>([]);
|
||||
|
||||
const {
|
||||
register,
|
||||
@@ -199,6 +200,39 @@ export function ContactForm() {
|
||||
{errors.message && <p className="text-red-500 text-xs font-medium">{errors.message.message}</p>}
|
||||
</div>
|
||||
|
||||
{/* Załączniki */}
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<label className="text-sm font-medium text-primary">
|
||||
Załącz zdjęcia <span className="text-text-muted font-normal">(opcjonalnie)</span>
|
||||
</label>
|
||||
<label className="flex flex-col items-center justify-center gap-2 border-2 border-dashed border-border rounded-xl p-6 cursor-pointer hover:border-accent hover:bg-accent/5 transition-colors">
|
||||
<input
|
||||
type="file"
|
||||
multiple
|
||||
accept="image/*,.pdf"
|
||||
className="sr-only"
|
||||
onChange={(e) => setFiles(Array.from(e.target.files || []))}
|
||||
/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="size-8 text-text-muted" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 00-2 2z" />
|
||||
</svg>
|
||||
<span className="text-sm text-text-muted text-center">
|
||||
Kliknij aby wybrać zdjęcia lub przeciągnij tutaj<br />
|
||||
<span className="text-xs">JPG, PNG, WEBP, PDF — maks. 10 MB każdy</span>
|
||||
</span>
|
||||
{files.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 mt-2">
|
||||
{files.map((f, i) => (
|
||||
<span key={i} className="text-xs bg-accent/10 text-accent px-2 py-1 rounded-md font-medium">
|
||||
{f.name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</label>
|
||||
<p className="text-xs text-text-muted">Zdjęcia miejsca montażu znacznie przyspieszają wycenę.</p>
|
||||
</div>
|
||||
|
||||
{/* RODO */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-start gap-3 cursor-pointer" onClick={() => setConsent(!consent)}>
|
||||
|
||||
@@ -33,7 +33,7 @@ function calculatePower(m2: number, floor: string, sun: string, building: string
|
||||
|
||||
// Szacunkowy koszt montażu
|
||||
function estimateInstall(pipes: string, rooms: number): { min: number; max: number } {
|
||||
const base = rooms === 1 ? 1200 : rooms === 2 ? 2800 : rooms === 3 ? 4200 : 5500;
|
||||
const base = rooms === 1 ? 1400 : rooms === 2 ? 2400 : rooms === 3 ? 3200 : 3800;
|
||||
const pipeExtra = pipes === 'short' ? 0 : pipes === 'medium' ? 300 : pipes === 'long' ? 700 : 1200;
|
||||
return { min: base + pipeExtra, max: base + pipeExtra + 600 };
|
||||
}
|
||||
@@ -332,7 +332,7 @@ export function KalkulatorClient({ products }: Props) {
|
||||
</div>
|
||||
{deviceType === 'pompa' && !pumpFormSent && (
|
||||
<div className="mt-4 p-5 bg-white border border-border rounded-2xl flex flex-col gap-4">
|
||||
<p className="text-sm font-bold text-primary">Wypełnij formularz — oddzwonimy z bezpłatną wyceną</p>
|
||||
<p className="text-sm font-bold text-primary">Wypełnij formularz — oddzwonimy w celu ustalenia terminu bezpłatnej i niezobowiązującej wizji lokalnej</p>
|
||||
<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>
|
||||
@@ -382,6 +382,9 @@ export function KalkulatorClient({ products }: Props) {
|
||||
>
|
||||
Wyślij zapytanie o pompę ciepła
|
||||
</button>
|
||||
<p className="text-xs text-text-muted text-center mt-2">
|
||||
Wizja lokalna jest bezpłatna i niezobowiązująca. Oddzwaniamy w ciągu 24 godzin.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -407,17 +410,30 @@ export function KalkulatorClient({ products }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-semibold text-primary mb-3 block">
|
||||
Powierzchnia pomieszczenia: <span className="text-accent">{m2} m²</span>
|
||||
</label>
|
||||
<input
|
||||
type="range" min={10} max={100} step={5} value={m2}
|
||||
onChange={e => setM2(Number(e.target.value))}
|
||||
className="w-full accent-accent"
|
||||
/>
|
||||
<div className="flex justify-between text-xs text-text-muted mt-1">
|
||||
<span>10 m²</span><span>100 m²</span>
|
||||
<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>
|
||||
@@ -585,6 +601,30 @@ export function KalkulatorClient({ products }: Props) {
|
||||
Szacowany koszt montażu dla Twojej konfiguracji:
|
||||
<strong className="ml-1">{installCost.min.toLocaleString('pl-PL')}–{installCost.max.toLocaleString('pl-PL')} zł</strong>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 p-4 bg-blue-50 border border-blue-200 rounded-xl">
|
||||
<p className="text-sm font-semibold text-blue-900 mb-1">📐 Chcesz dokładną wycenę?</p>
|
||||
<p className="text-xs text-blue-800 leading-relaxed">
|
||||
Skontaktuj się z nami — wycena jest <strong>bezpłatna i niezobowiązująca</strong>.
|
||||
Aby przyspieszyć wycenę, wyślij nam zdjęcia:
|
||||
</p>
|
||||
<ul className="mt-2 flex flex-col gap-1">
|
||||
{[
|
||||
'Miejsca gdzie ma wisieć jednostka wewnętrzna',
|
||||
'Ściany przez które przejdzie instalacja',
|
||||
'Miejsca gdzie stanie jednostka zewnętrzna',
|
||||
'Skrzynki elektrycznej (bezpieczniki)',
|
||||
].map((item, i) => (
|
||||
<li key={i} className="flex items-start gap-2 text-xs text-blue-800">
|
||||
<span className="text-blue-500 shrink-0">→</span>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<a href="/kontakt" className="mt-3 inline-flex items-center gap-1.5 text-xs font-bold text-white bg-accent px-3 py-1.5 rounded-lg hover:bg-accent/90 transition-colors">
|
||||
Wyślij zdjęcia i zapytaj o wycenę →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -601,18 +641,30 @@ export function KalkulatorClient({ products }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<label className="text-sm font-semibold text-primary">Budżet całkowity</label>
|
||||
<span className="text-lg font-extrabold text-accent font-heading">{budget.toLocaleString('pl-PL')} zł</span>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-text-muted">Budżet</span>
|
||||
<span className="text-xs font-bold text-primary bg-accent/10 px-2 py-0.5 rounded-md">{budget.toLocaleString('pl-PL')} zł</span>
|
||||
</div>
|
||||
<input
|
||||
type="range" min={3000} max={25000} step={500} value={budget}
|
||||
onChange={e => setBudget(Number(e.target.value))}
|
||||
className="w-full accent-accent"
|
||||
/>
|
||||
<div className="flex justify-between text-xs text-text-muted mt-1">
|
||||
<span>3 000 zł</span><span>25 000 zł</span>
|
||||
<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: `${((budget - 3000) / 22000) * 100}%` }}
|
||||
/>
|
||||
<input
|
||||
type="range" min={3000} max={25000} step={500} value={budget}
|
||||
onChange={e => setBudget(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(${((budget - 3000) / 22000) * 100}% - 8px)` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between text-xs text-text-muted">
|
||||
<span>3 000 zł</span>
|
||||
<span>25 000 zł</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -85,11 +85,11 @@ export function Footer() {
|
||||
Informacje
|
||||
</h3>
|
||||
<ul className="flex flex-col gap-3 text-sm text-slate-300">
|
||||
<li>
|
||||
{/* <li>
|
||||
<Link href="/o-nas" className="hover:text-accent transition-colors">
|
||||
O nas
|
||||
</Link>
|
||||
</li>
|
||||
</li> */}
|
||||
<li>
|
||||
<Link href="/realizacje" className="hover:text-accent transition-colors">
|
||||
Realizacje (Portfolio)
|
||||
|
||||
@@ -224,12 +224,12 @@ export function Header() {
|
||||
>
|
||||
Blog
|
||||
</Link>
|
||||
<Link
|
||||
{/* <Link
|
||||
href="/o-nas"
|
||||
className="text-sm font-medium text-primary hover:text-accent transition-colors"
|
||||
>
|
||||
O nas
|
||||
</Link>
|
||||
</Link> */}
|
||||
<Link
|
||||
href="/kontakt"
|
||||
className="text-sm font-medium text-primary hover:text-accent transition-colors"
|
||||
|
||||
@@ -205,7 +205,7 @@ export function MobileMenu({ isOpen, onClose }: MobileMenuProps) {
|
||||
</div>
|
||||
|
||||
{/* 6. O Nas */}
|
||||
<div className="border-b border-border py-3">
|
||||
{/* <div className="border-b border-border py-3">
|
||||
<Link
|
||||
href="/o-nas"
|
||||
onClick={onClose}
|
||||
@@ -213,7 +213,7 @@ export function MobileMenu({ isOpen, onClose }: MobileMenuProps) {
|
||||
>
|
||||
O nas
|
||||
</Link>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* 7. Kontakt */}
|
||||
<div className="border-b border-border py-3">
|
||||
|
||||
@@ -100,29 +100,6 @@ export const posts: Post[] = [
|
||||
<p>Pamiętaj jednak, że pompa ciepła nie sprawdzi się w nieocieplonych, starych domach z tradycyjnymi grzejnikami. Potrzebujesz rzetelnego audytu? Skontaktuj się z nami, a doradzimy najlepsze rozwiązanie dla Twojego domu!</p>
|
||||
`
|
||||
},
|
||||
{
|
||||
slug: 'dofinansowanie-czyste-powietrze-2026',
|
||||
title: 'Czyste Powietrze 2026 — ile możesz dostać na pompę ciepła?',
|
||||
category: 'pompy-ciepla',
|
||||
excerpt: 'Program Czyste Powietrze w 2026 roku oferuje rekordowe dopłaty. Dowiedz się, kto może skorzystać, jakie są progi dochodowe i jak złożyć wniosek.',
|
||||
date: '2026-02-02T10:00:00Z',
|
||||
author: 'Tomasz Kowalski',
|
||||
image: '',
|
||||
featured: true,
|
||||
content: `
|
||||
<h2>Rekordowe dopłaty w programie Czyste Powietrze 2026</h2>
|
||||
<p>Program Czyste Powietrze na rok 2026 przeszedł kilka ważnych modyfikacji, które ułatwiają inwestycję w odnawialne źródła energii. Jeśli planujesz wymianę "kopciucha" na nowoczesną pompę ciepła, to idealny moment na działanie.</p>
|
||||
<h3>Progi dofinansowania</h3>
|
||||
<p>Kwota, jaką możesz otrzymać, zależy od Twoich dochodów:</p>
|
||||
<ul>
|
||||
<li><strong>Poziom podstawowy:</strong> dla osób z rocznym dochodem do 135 000 zł.</li>
|
||||
<li><strong>Poziom podwyższony:</strong> wyższa dotacja dla osób z niższym dochodem na członka rodziny.</li>
|
||||
<li><strong>Poziom najwyższy:</strong> nawet do 100% kosztów (do 135 000 zł) dla najuboższych.</li>
|
||||
</ul>
|
||||
<h3>Jak uniknąć błędów?</h3>
|
||||
<p>Najczęstszym błędem jest zakup urządzenia, które nie figuruje na liście ZUM (Zielonych Urządzeń i Materiałów). Przed opłaceniem faktury zawsze sprawdzaj certyfikaty. Nasi specjaliści pomagają nie tylko w doborze i montażu urządzeń, ale również w kompleksowym przygotowaniu wniosków o dotację. Chcesz wiedzieć więcej? Odezwij się do nas!</p>
|
||||
`
|
||||
},
|
||||
{
|
||||
slug: 'jak-dobrac-moc-klimatyzatora',
|
||||
title: 'Jak dobrać moc klimatyzatora? Wzory i kalkulator dla każdego pomieszczenia',
|
||||
@@ -382,28 +359,6 @@ export const posts: Post[] = [
|
||||
<p>Dzięki inwerterom, klimatyzacja jest obecnie bardzo tania w eksploatacji. Sprawdź naszą ofertę inwerterowych systemów z certyfikatem A+++.</p>
|
||||
`
|
||||
},
|
||||
{
|
||||
slug: 'pompy-ciepla-dolny-slask-dofinansowanie',
|
||||
title: 'Pompy ciepła na Dolnym Śląsku — dofinansowanie i realizacje',
|
||||
category: 'pompy-ciepla',
|
||||
excerpt: 'Sprawdź, jak działają pompy ciepła na terenie Dolnego Śląska, jakie dofinansowania lokalne możesz uzyskać i jak dbamy o profesjonalny montaż.',
|
||||
date: '2026-04-25T10:00:00Z',
|
||||
author: 'Tomasz Kowalski',
|
||||
image: '',
|
||||
content: `
|
||||
<h2>Dolny Śląsk to idealny region dla pomp ciepła</h2>
|
||||
<p>Dzięki stosunkowo łagodnym zimom na nizinach Dolnego Śląska (Wrocław, Legnica, Lubin), powietrzne pompy ciepła osiągają u nas fenomenalne wyniki wydajnościowe (SCOP) przez cały sezon grzewczy. Nawet w rejonach przedgórskich (Wałbrzych, Jelenia Góra) nowoczesne instalacje świetnie radzą sobie bez wspomagania grzałkami.</p>
|
||||
<h3>Wsparcie finansowe dla mieszkańców</h3>
|
||||
<p>Poza ogólnopolskim programem "Czyste Powietrze", wiele dolnośląskich gmin i miast (m.in. Wrocław z programem KAWKA Plus) oferuje dodatkowe dotacje na likwidację pieców węglowych. Połączenie lokalnych funduszy z rządowymi potrafi sfinansować nawet do 80-90% całej inwestycji.</p>
|
||||
<h3>Zaufaj lokalnym ekspertom</h3>
|
||||
<ul>
|
||||
<li>Znamy specyfikę dolnośląskiego budownictwa.</li>
|
||||
<li>Zapewniamy szybki serwis gwarancyjny (nasza baza to okolice Wrocławia/Legnicy).</li>
|
||||
<li>Pomagamy z biurokracją we wrocławskim i lokalnych urzędach.</li>
|
||||
</ul>
|
||||
<p>Chcesz dołączyć do grona ekologicznych domów w naszym województwie? Zostaw kontakt, a przeanalizujemy Twoje możliwości dotacyjne!</p>
|
||||
`
|
||||
},
|
||||
{
|
||||
slug: 'klimatyzacja-legnica-montaz',
|
||||
title: 'Klimatyzacja Legnica — montaż split i multisplit w mieście i okolicach',
|
||||
|
||||
Reference in New Issue
Block a user