feat(calculator, city-page): add width warning option and optimize stats layout on mobile
This commit is contained in:
@@ -165,9 +165,8 @@ export default async function MiastoPage({ params }: Props) {
|
|||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||||
{[
|
{[
|
||||||
{ label: 'Dofinansowanie', value: 'do 135 000 zł', sub: 'z programu Czyste Powietrze' },
|
|
||||||
{ label: 'Oszczędność', value: 'do 60%', sub: 'niższe koszty ogrzewania' },
|
{ label: 'Oszczędność', value: 'do 60%', sub: 'niższe koszty ogrzewania' },
|
||||||
{ label: 'Sprawność', value: 'COP 4–5', sub: 'na każdy 1 kW energii' },
|
{ label: 'Sprawność', value: 'COP 4–5', sub: 'na każdy 1 kW energii' },
|
||||||
{ label: 'Gwarancja', value: '5 lat', sub: 'na urządzenie i montaż' },
|
{ label: 'Gwarancja', value: '5 lat', sub: 'na urządzenie i montaż' },
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ function estimateInstall(pipes: string, rooms: number, installMethod: string, ou
|
|||||||
const STEPS = ['Typ', 'Pomieszczenie', 'Instalacja', 'Budżet', 'Dobór'] as const;
|
const STEPS = ['Typ', 'Pomieszczenie', 'Instalacja', 'Budżet', 'Dobór'] as const;
|
||||||
|
|
||||||
export function KalkulatorClient({ products }: Props) {
|
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 [deviceType, setDeviceType] = useSessionState<'klimatyzacja' | 'pompa'>('calc_deviceType', 'klimatyzacja');
|
||||||
const [pumpFormSent, setPumpFormSent] = useState(false);
|
const [pumpFormSent, setPumpFormSent] = useState(false);
|
||||||
const [pumpForm, setPumpForm] = useState({ name: '', phone: '', city: '', message: '' });
|
const [pumpForm, setPumpForm] = useState({ name: '', phone: '', city: '', message: '' });
|
||||||
@@ -254,16 +256,20 @@ export function KalkulatorClient({ products }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pasek postępu */}
|
{/* Pasek postępu */}
|
||||||
<div className="flex items-center gap-2 mb-8">
|
<div className="flex items-center mb-8">
|
||||||
{STEPS.map((s, i) => (
|
{STEPS.map((s, i) => (
|
||||||
<div key={s} className="flex-1 flex items-center gap-2">
|
<div key={s} className="flex items-start flex-1 last:flex-none">
|
||||||
<div className={`size-7 rounded-full flex items-center justify-center text-xs font-bold shrink-0 transition-all ${
|
<div className="flex flex-col items-center gap-1">
|
||||||
i < step ? 'bg-green-500 text-white' : i === step ? 'bg-accent text-white' : 'bg-slate-200 text-text-muted'
|
<div className={`size-7 rounded-full flex items-center justify-center text-xs font-bold shrink-0 transition-all ${
|
||||||
}`}>
|
i < step ? 'bg-green-500 text-white' : i === step ? 'bg-accent text-white' : 'bg-slate-200 text-text-muted'
|
||||||
{i < step ? <Check className="size-3.5" /> : i + 1}
|
}`}>
|
||||||
|
{i < step ? <Check className="size-3.5" /> : i + 1}
|
||||||
|
</div>
|
||||||
|
<span className={`text-xs font-medium hidden sm:block whitespace-nowrap ${i === step ? 'text-accent' : 'text-text-muted'}`}>{s}</span>
|
||||||
</div>
|
</div>
|
||||||
<span className={`text-xs font-medium hidden sm:block ${i === step ? 'text-accent' : 'text-text-muted'}`}>{s}</span>
|
{i < STEPS.length - 1 && (
|
||||||
{i < STEPS.length - 1 && <div className={`flex-1 h-0.5 ${i < step ? 'bg-green-500' : 'bg-slate-200'}`} />}
|
<div className={`flex-1 h-0.5 mx-2 mt-[13px] ${i < step ? 'bg-green-500' : 'bg-slate-200'}`} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -436,6 +442,63 @@ export function KalkulatorClient({ products }: Props) {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Ograniczenie szerokości */}
|
||||||
|
<div className="bg-slate-50 border border-border rounded-xl p-4">
|
||||||
|
<div className="flex items-start justify-between gap-3 mb-2">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-semibold text-primary">Ograniczenie szerokości jednostki?</p>
|
||||||
|
<p className="text-xs text-text-muted mt-0.5">Np. wąska ściana, wnęka, szafa</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowWidthWarning(prev => !prev)}
|
||||||
|
className={`shrink-0 text-xs font-bold px-3 py-1.5 rounded-lg transition-colors ${
|
||||||
|
showWidthWarning ? 'bg-amber-100 text-amber-700' : 'bg-slate-200 text-slate-600 hover:bg-slate-300'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{showWidthWarning ? 'Tak, mam ograniczenie' : 'Nie dotyczy'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{showWidthWarning && (
|
||||||
|
<div className="mt-3">
|
||||||
|
<div className="flex items-center justify-between mb-1.5">
|
||||||
|
<span className="text-xs text-text-muted">Maksymalna szerokość jednostki</span>
|
||||||
|
<span className="text-xs font-bold text-amber-700 bg-amber-100 px-2 py-0.5 rounded-md">{maxWidth} cm</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-amber-400 rounded-full"
|
||||||
|
style={{ width: `${((maxWidth - 40) / 80) * 100}%` }}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="range" min={40} max={120} step={5} value={maxWidth}
|
||||||
|
onChange={e => setMaxWidth(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-amber-400 rounded-full shadow-md pointer-events-none"
|
||||||
|
style={{ left: `calc(${((maxWidth - 40) / 80) * 100}% - 8px)` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between text-xs text-text-muted mt-1">
|
||||||
|
<span>40 cm</span>
|
||||||
|
<span>120 cm</span>
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 flex items-start gap-2 p-3 bg-amber-50 border border-amber-200 rounded-xl">
|
||||||
|
<span className="text-amber-500 text-base shrink-0">⚠️</span>
|
||||||
|
<p className="text-xs text-amber-800 leading-relaxed">
|
||||||
|
Standardowe jednostki split mają <strong>70–110 cm szerokości</strong>.
|
||||||
|
{maxWidth < 70 && ' Przy tak małej przestrzeni wybór jest bardzo ograniczony — '}
|
||||||
|
{maxWidth >= 70 && maxWidth < 85 && ' Przy tej szerokości dostępne są kompaktowe modele — '}
|
||||||
|
{maxWidth >= 85 && ' Większość modeli powinna się zmieścić — '}
|
||||||
|
skonsultuj wymiary konkretnego modelu przed zakupem.
|
||||||
|
<a href="/kontakt" className="ml-1 font-bold text-amber-700 underline">Zapytaj nas →</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="p-3 bg-accent/10 rounded-xl">
|
<div className="p-3 bg-accent/10 rounded-xl">
|
||||||
<p className="text-sm text-accent font-semibold">
|
<p className="text-sm text-accent font-semibold">
|
||||||
Łączna wymagana moc: ok. <strong>{requiredPowerRounded} kW</strong>
|
Łączna wymagana moc: ok. <strong>{requiredPowerRounded} kW</strong>
|
||||||
|
|||||||
Reference in New Issue
Block a user