feat: filtr jednostek multisplit, ostatnio oglądane, sklep pompy ciepła, czyszczenie komentarzy, checkbox zgody

- Filtr 'Ilość jednostek wewn.' w sklepie (1–9 jednostek, checkboxy, liczniki)
- Komponent RecentlyViewedProducts + Zustand store (recently-viewed-store.ts)
- Pompy ciepła dodane do sklepu i nawigacji (Header + MobileMenu)
- Logotypy marek zaktualizowane (Daikin, Panasonic + odświeżone istniejące)
- Checkbox zgody RODO w /zamowienie powiększony (w-5 h-5)
- Usunięcie ~65 zbędnych komentarzy WHAT z 12 plików (build: 0 errors)
- Plik analyze_products.ts (pomocniczy skrypt diagnostyczny)
This commit is contained in:
MichalC
2026-08-02 20:31:55 +02:00
parent 092cff1899
commit c9e4283789
37 changed files with 331 additions and 123 deletions
+9 -21
View File
@@ -11,6 +11,9 @@ import { Product } from '@/content/products';
import { useCartStore } from '@/lib/cart-store';
import { useCompareStore } from '@/lib/compare-store';
import { getProductVariants } from '@/content/product-variants';
import { useRecentlyViewedStore } from '@/lib/recently-viewed-store';
import { RecentlyViewedProducts } from './RecentlyViewedProducts';
import { useEffect } from 'react';
const brandLogos: Record<string, string> = {
'Gree': '/brands/gree.webp',
@@ -34,7 +37,6 @@ interface Props {
companyPhoneRaw: string;
}
// Szacowany SEER na podstawie klasy energetycznej
function estimateSEER(klasa: string): number {
if (!klasa) return 5.0;
const k = klasa.toUpperCase();
@@ -45,7 +47,6 @@ function estimateSEER(klasa: string): number {
return 5.0;
}
// Parsuj moc — bierz wartość nominalną (środkową lub pierwszą)
function parseMoc(val: string): number {
if (!val) return 0;
const nums = val.replace(',', '.').match(/[\d.]+/g);
@@ -81,6 +82,11 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
const { addItem: addCompare, removeItem: removeCompare, isAdded } = useCompareStore();
const inCompare = isAdded(currentProduct.slug);
const addRecentlyViewed = useRecentlyViewedStore((s) => s.addItem);
useEffect(() => {
addRecentlyViewed(currentProduct);
}, [currentProduct, addRecentlyViewed]);
const handleAddToCart = () => {
if (!currentProduct.price) return;
addToCart({
@@ -99,14 +105,12 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
else addCompare({ slug: currentProduct.slug, name: currentProduct.name, brand: currentProduct.brand, price: currentProduct.price, image: currentProduct.image, specs: currentProduct.specs });
};
// Kalkulator
const mocChlodnicza = parseMoc(currentProduct.specs['Moc chłodnicza'] || '');
const pobórMocy = parseMoc(currentProduct.specs['Pobór mocy chłodzenie (kW)'] || '') || (mocChlodnicza / estimateSEER(currentProduct.specs['Klasa energetyczna'] || ''));
const cenaKwh = 0.75;
const kosztGodzina = pobórMocy * cenaKwh;
const kosztMiesiac = kosztGodzina * godziny * 30;
// Kluczowe parametry do ikon
const params = [
currentProduct.specs['Klasa energetyczna'] && { icon: Star, label: 'Klasa energetyczna', value: currentProduct.specs['Klasa energetyczna'], color: 'text-green-600 bg-green-50' },
currentProduct.specs['Moc chłodnicza'] && { icon: Thermometer, label: 'Moc chłodnicza', value: `${currentProduct.specs['Moc chłodnicza']} kW`, color: 'text-blue-600 bg-blue-50' },
@@ -120,7 +124,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
<main className="bg-slate-50 min-h-screen pb-16">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
{/* Breadcrumb */}
<nav className="flex items-center gap-1.5 text-xs text-text-muted mb-6">
<Link href="/" className="hover:text-accent transition-colors">Strona główna</Link>
<span>/</span>
@@ -133,7 +136,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 xl:gap-12">
{/* LEWA: Zdjęcie */}
<div>
<div className="relative aspect-[4/3] lg:aspect-auto lg:h-[420px] lg:max-h-[420px] bg-white rounded-2xl border border-border overflow-hidden">
<Image
@@ -144,7 +146,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
priority
unoptimized={currentProduct.image.startsWith('http')}
/>
{/* Badges */}
{currentProduct.specs['Klasa energetyczna'] && (
<div className="absolute top-4 left-4 px-2.5 py-1 bg-green-500 text-white text-xs font-bold rounded-lg shadow">
{currentProduct.specs['Klasa energetyczna'].split('/')[0]}
@@ -158,9 +159,7 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
</div>
</div>
{/* PRAWA: Info */}
<div className="flex flex-col gap-3">
{/* Nagłówek */}
<div>
<div className="mb-2 flex items-center gap-3 flex-wrap">
{brandLogos[currentProduct.brand] ? (
@@ -195,7 +194,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
<p className="text-xs text-text-muted mt-1.5 mb-4">SKU: {currentProduct.sku}</p>
</div>
{/* Warianty mocy */}
{variants && variants.moce.length > 0 && (
<div className="mb-3">
<p className="text-xs font-semibold text-text-muted uppercase tracking-wider mb-2">
@@ -235,7 +233,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
</div>
)}
{/* Warianty wykończenia */}
{variants && variants.wykończenia.length > 0 && (
<div className="mb-3">
<p className="text-xs font-semibold text-text-muted uppercase tracking-wider mb-2">
@@ -265,7 +262,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
</div>
)}
{/* Ikony parametrów */}
{params.length > 0 && (
<div className="flex flex-wrap gap-2">
{params.map((p, i) => {
@@ -283,7 +279,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
</div>
)}
{/* Cena i CTA */}
<div className="bg-white rounded-2xl border border-border p-4">
{currentProduct.price !== null ? (
<>
@@ -347,7 +342,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
)}
</div>
{/* Profesjonalna porada */}
<a
href={`tel:${companyPhoneRaw}`}
className="flex items-center justify-between px-5 py-2.5 bg-primary rounded-xl text-white hover:bg-primary/90 transition-colors"
@@ -358,7 +352,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
</span>
</a>
{/* Kalkulator kosztów energii */}
{mocChlodnicza > 0 && (
<div className="bg-white rounded-2xl border border-border p-4">
<div className="flex items-center gap-2 mb-4">
@@ -408,7 +401,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
</div>
</div>
{/* TABS */}
<div className="mt-10">
<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 => (
@@ -426,7 +418,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
))}
</div>
{/* Tab: Opis */}
{activeTab === 'Opis' && (
<div className="max-w-3xl">
{currentProduct.shortDescription ? (
@@ -437,7 +428,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
</div>
)}
{/* Tab: Specyfikacja */}
{activeTab === 'Specyfikacja' && (
<div className="max-w-2xl">
{Object.keys(currentProduct.specs).length > 0 ? (
@@ -459,7 +449,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
</div>
)}
{/* Tab: Montaż */}
{activeTab === 'Montaż' && (
<div className="max-w-2xl space-y-4">
<div className="bg-white rounded-xl border border-border p-5">
@@ -483,7 +472,6 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
</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">
@@ -512,7 +500,7 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
</div>
)}
<RecentlyViewedProducts currentSlug={currentProduct.slug} />
</div>
</div>