feat: enhance UI components (replace sliders with daisyUI range, hide tabs scrollbar, add product reviews tab, adjust banner positioning)

This commit is contained in:
AI Assistant
2026-07-19 12:51:00 +02:00
parent d64266243e
commit 2060990ab9
3 changed files with 47 additions and 56 deletions
+2 -24
View File
@@ -395,22 +395,11 @@ export function KalkulatorClient({ products }: Props) {
<span className="text-xs text-text-muted">Powierzchnia</span> <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> <span className="text-xs font-bold text-primary bg-accent/10 px-2 py-0.5 rounded-md">{p.m2} m²</span>
</div> </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 <input
type="range" min={5} max={100} step={5} value={p.m2} type="range" min={5} max={100} step={5} value={p.m2}
onChange={e => updateRoom(i, Number(e.target.value))} onChange={e => updateRoom(i, Number(e.target.value))}
className="absolute w-full h-1.5 opacity-0 cursor-pointer z-10" className="range range-xs range-accent w-full"
/> />
<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"> <div className="flex justify-between text-xs text-text-muted">
<span>5 m²</span> <span>5 m²</span>
<span>100 m²</span> <span>100 m²</span>
@@ -712,22 +701,11 @@ export function KalkulatorClient({ products }: Props) {
<span className="text-xs text-text-muted">Budżet</span> <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> <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> </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: `${((budget - 3000) / 22000) * 100}%` }}
/>
<input <input
type="range" min={3000} max={25000} step={500} value={budget} type="range" min={3000} max={25000} step={500} value={budget}
onChange={e => setBudget(Number(e.target.value))} onChange={e => setBudget(Number(e.target.value))}
className="absolute w-full h-1.5 opacity-0 cursor-pointer z-10" className="range range-xs range-accent w-full"
/> />
<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"> <div className="flex justify-between text-xs text-text-muted">
<span>3 000 zł</span> <span>3 000 zł</span>
<span>25 000 zł</span> <span>25 000 zł</span>
+39 -4
View File
@@ -54,7 +54,7 @@ function parseMoc(val: string): number {
return floats[Math.floor(floats.length / 2)]; return floats[Math.floor(floats.length / 2)];
} }
const TABS = ['Opis', 'Specyfikacja', 'Montaż'] as const; const TABS = ['Opis', 'Specyfikacja', 'Montaż', 'Opinie'] as const;
type Tab = typeof TABS[number]; type Tab = typeof TABS[number];
function seededViews(slug: string): number { function seededViews(slug: string): number {
@@ -378,7 +378,7 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
max={24} max={24}
value={godziny} value={godziny}
onChange={e => setGodziny(Number(e.target.value))} onChange={e => setGodziny(Number(e.target.value))}
className="flex-1 accent-accent" className="range range-xs range-accent flex-1"
/> />
<span className="text-sm font-bold text-primary w-8 text-right">{godziny}h</span> <span className="text-sm font-bold text-primary w-8 text-right">{godziny}h</span>
</div> </div>
@@ -408,7 +408,7 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
{/* TABS */} {/* TABS */}
<div className="mt-10"> <div className="mt-10">
<div className="flex gap-1 border-b border-border mb-6 overflow-x-auto"> <div className="flex gap-1 border-b border-border mb-6 overflow-x-auto [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]">
{TABS.map(tab => ( {TABS.map(tab => (
<button <button
key={tab} key={tab}
@@ -481,12 +481,47 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
</div> </div>
)} )}
{/* Tab: Opinie */}
{activeTab === 'Opinie' && (
<div className="max-w-3xl space-y-8">
<div className="bg-slate-50 p-6 rounded-2xl border border-border">
<h3 className="font-bold text-primary font-heading mb-4">Dodaj opinię</h3>
<form className="space-y-4" onSubmit={(e) => { e.preventDefault(); alert('Opinia została wysłana i oczekuje na moderację.'); e.currentTarget.reset(); }}>
<div className="flex gap-1 mb-2">
{[1,2,3,4,5].map(star => (
<Star key={star} className="size-6 text-amber-400 cursor-pointer fill-amber-400 hover:scale-110 transition-transform" />
))}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="space-y-1.5">
<label className="text-sm font-semibold text-primary">Imię</label>
<input type="text" required className="w-full px-4 py-2.5 rounded-xl border border-border bg-white text-sm focus:outline-none focus:ring-2 focus:ring-accent/50" placeholder="Twoje imię" />
</div>
<div className="space-y-1.5">
<label className="text-sm font-semibold text-primary">Email</label>
<input type="email" required className="w-full px-4 py-2.5 rounded-xl border border-border bg-white text-sm focus:outline-none focus:ring-2 focus:ring-accent/50" placeholder="twó[email protected]" />
</div>
</div>
<div className="space-y-1.5">
<label className="text-sm font-semibold text-primary">Treść opinii</label>
<textarea required rows={4} className="w-full px-4 py-2.5 rounded-xl border border-border bg-white text-sm focus:outline-none focus:ring-2 focus:ring-accent/50" placeholder="Podziel się swoimi wrażeniami..."></textarea>
</div>
<button type="submit" className="btn btn-primary font-bold text-white w-full sm:w-auto">Wyślij opinię</button>
</form>
</div>
<div className="space-y-4">
<h3 className="font-bold text-primary font-heading">Opinie klientów (0)</h3>
<p className="text-sm text-text-muted">Bądź pierwszą osobą, która oceni ten produkt!</p>
</div>
</div>
)}
</div> </div>
</div> </div>
{showViewsBanner && ( {showViewsBanner && (
<div className="fixed bottom-6 left-6 z-50 flex items-center gap-3 bg-white border border-amber-200 shadow-lg rounded-xl px-4 py-3 max-w-xs animate-in slide-in-from-left-4 duration-300"> <div className="fixed bottom-24 left-6 z-50 flex items-center gap-3 bg-white border border-amber-200 shadow-lg rounded-xl px-4 py-3 max-w-xs animate-in slide-in-from-left-4 duration-300">
<Eye className="size-4 text-amber-500 shrink-0" /> <Eye className="size-4 text-amber-500 shrink-0" />
<p className="text-sm text-gray-800"> <p className="text-sm text-gray-800">
W ostatnich 30 dniach interesuje się tym produktem <span className="font-bold">{viewsCount}</span> osób. W ostatnich 30 dniach interesuje się tym produktem <span className="font-bold">{viewsCount}</span> osób.
+2 -24
View File
@@ -348,24 +348,13 @@ export function ProductsGrid({ products, brands }: ProductsGridProps) {
<span className="text-xs text-text-muted">od</span> <span className="text-xs text-text-muted">od</span>
<span className="text-xs font-bold text-primary bg-accent/10 px-2 py-0.5 rounded-md">{halasMin} dB</span> <span className="text-xs font-bold text-primary bg-accent/10 px-2 py-0.5 rounded-md">{halasMin} dB</span>
</div> </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={{ left: `${((halasMin - 15) / 35) * 100}%`, right: `${100 - ((halasMax - 15) / 35) * 100}%` }}
/>
<input <input
type="range" type="range"
min={15} max={50} min={15} max={50}
value={halasMin} value={halasMin}
onChange={e => { setHalasMin(Math.min(Number(e.target.value), halasMax - 1)); setPage(1); }} onChange={e => { setHalasMin(Math.min(Number(e.target.value), halasMax - 1)); setPage(1); }}
className="absolute w-full h-1.5 opacity-0 cursor-pointer z-10" className="range range-xs range-accent w-full"
/> />
<div
className="absolute size-4 bg-white border-2 border-accent rounded-full shadow-md pointer-events-none"
style={{ left: `calc(${((halasMin - 15) / 35) * 100}% - 8px)` }}
/>
</div>
</div> </div>
{/* do */} {/* do */}
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
@@ -373,24 +362,13 @@ export function ProductsGrid({ products, brands }: ProductsGridProps) {
<span className="text-xs text-text-muted">do</span> <span className="text-xs text-text-muted">do</span>
<span className="text-xs font-bold text-primary bg-accent/10 px-2 py-0.5 rounded-md">{halasMax} dB</span> <span className="text-xs font-bold text-primary bg-accent/10 px-2 py-0.5 rounded-md">{halasMax} dB</span>
</div> </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={{ left: `${((halasMin - 15) / 35) * 100}%`, right: `${100 - ((halasMax - 15) / 35) * 100}%` }}
/>
<input <input
type="range" type="range"
min={15} max={50} min={15} max={50}
value={halasMax} value={halasMax}
onChange={e => { setHalasMax(Math.max(Number(e.target.value), halasMin + 1)); setPage(1); }} onChange={e => { setHalasMax(Math.max(Number(e.target.value), halasMin + 1)); setPage(1); }}
className="absolute w-full h-1.5 opacity-0 cursor-pointer z-10" className="range range-xs range-accent w-full"
/> />
<div
className="absolute size-4 bg-white border-2 border-accent rounded-full shadow-md pointer-events-none"
style={{ left: `calc(${((halasMax - 15) / 35) * 100}% - 8px)` }}
/>
</div>
</div> </div>
</div> </div>
</Accordion> </Accordion>