fix: linting cleanup, TS errors, mobile cookie banner and hero max-width
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
*.js
|
||||||
|
*.cjs
|
||||||
@@ -53,7 +53,7 @@ export default function DostawaPage() {
|
|||||||
|
|
||||||
<h2 id="instalatorzy">2. Dostawa podczas montażu</h2>
|
<h2 id="instalatorzy">2. Dostawa podczas montażu</h2>
|
||||||
<p>
|
<p>
|
||||||
Jeśli wybierzesz wariant zakupu "Z montażem", urządzenie nie jest wysyłane z wyprzedzeniem za pośrednictwem zewnętrznej firmy kurierskiej. Sprzęt w dniu umówionej instalacji przywozi nasza własna ekipa monterska, dzięki czemu minimalizujemy ryzyko uszkodzenia urządzenia w transporcie i zdejmujemy z Ciebie obowiązek składowania gabarytowych kartonów.
|
Jeśli wybierzesz wariant zakupu "Z montażem", urządzenie nie jest wysyłane z wyprzedzeniem za pośrednictwem zewnętrznej firmy kurierskiej. Sprzęt w dniu umówionej instalacji przywozi nasza własna ekipa monterska, dzięki czemu minimalizujemy ryzyko uszkodzenia urządzenia w transporcie i zdejmujemy z Ciebie obowiązek składowania gabarytowych kartonów.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="platnosci">3. Metody płatności</h2>
|
<h2 id="platnosci">3. Metody płatności</h2>
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export default function PolitykaPrywatnosciPage() {
|
|||||||
|
|
||||||
<h2 id="cookies" className="text-2xl font-bold mt-8 mb-4">4. Pliki Cookies i Analityka (Google Analytics 4 / Tag Manager)</h2>
|
<h2 id="cookies" className="text-2xl font-bold mt-8 mb-4">4. Pliki Cookies i Analityka (Google Analytics 4 / Tag Manager)</h2>
|
||||||
<p>
|
<p>
|
||||||
Nasz serwis wykorzystuje pliki cookies (tzw. "ciasteczka") – dane informatyczne, w szczególności pliki tekstowe, które przechowywane są w urządzeniu końcowym Użytkownika i przeznaczone są do korzystania z naszej strony internetowej.
|
Nasz serwis wykorzystuje pliki cookies (tzw. "ciasteczka") – dane informatyczne, w szczególności pliki tekstowe, które przechowywane są w urządzeniu końcowym Użytkownika i przeznaczone są do korzystania z naszej strony internetowej.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 className="text-xl font-semibold mt-6 mb-3">Google Analytics 4 (GA4) i Google Tag Manager (GTM)</h3>
|
<h3 className="text-xl font-semibold mt-6 mb-3">Google Analytics 4 (GA4) i Google Tag Manager (GTM)</h3>
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ export default async function BlogPostPage({ params }: BlogPostPageProps) {
|
|||||||
{/* Główny obraz */}
|
{/* Główny obraz */}
|
||||||
<div className="relative w-full aspect-video rounded-2xl overflow-hidden mb-12 shadow-md">
|
<div className="relative w-full aspect-video rounded-2xl overflow-hidden mb-12 shadow-md">
|
||||||
<Image
|
<Image
|
||||||
src={post.image || 'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=1600&q=80'}
|
src={post.image || '/images/hero/hero-section-image.webp'}
|
||||||
alt={post.title}
|
alt={post.title}
|
||||||
fill
|
fill
|
||||||
priority
|
priority
|
||||||
@@ -231,7 +231,7 @@ export default async function BlogPostPage({ params }: BlogPostPageProps) {
|
|||||||
<Link key={related.slug} href={`/blog/${related.slug}`} className="group block">
|
<Link key={related.slug} href={`/blog/${related.slug}`} className="group block">
|
||||||
<div className="relative aspect-[16/10] rounded-xl overflow-hidden mb-4">
|
<div className="relative aspect-[16/10] rounded-xl overflow-hidden mb-4">
|
||||||
<Image
|
<Image
|
||||||
src={related.image || 'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=800&q=80'}
|
src={related.image || '/images/hero/hero-section-image.webp'}
|
||||||
alt={related.title}
|
alt={related.title}
|
||||||
fill
|
fill
|
||||||
className="object-cover transition-transform duration-500 group-hover:scale-105"
|
className="object-cover transition-transform duration-500 group-hover:scale-105"
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ export default function BlogPage() {
|
|||||||
ctaLink="/kontakt"
|
ctaLink="/kontakt"
|
||||||
secondaryCtaText="Nasze usługi"
|
secondaryCtaText="Nasze usługi"
|
||||||
secondaryCtaLink="/uslugi-domowe"
|
secondaryCtaLink="/uslugi-domowe"
|
||||||
backgroundImage="https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=1600&q=80"
|
backgroundImage="/images/hero/hero-section-image.webp"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<section className="py-16 md:py-24 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<section className="py-16 md:py-24 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
|||||||
+2
-2
@@ -159,11 +159,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground overflow-x-hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@apply font-sans;
|
@apply font-sans overflow-x-hidden scroll-smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { Check, Phone, Wrench, ArrowRight, Home, Thermometer, Wind, Zap } from 'lucide-react';
|
import { Phone, Wrench, ArrowRight, Home, Thermometer, Wind, Zap } from 'lucide-react';
|
||||||
import { companyDetails } from '@/content/company';
|
import { companyDetails } from '@/content/company';
|
||||||
import { buttonClasses, Button } from '@/components/ui/Button';
|
import { buttonClasses, Button } from '@/components/ui/Button';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { MapPin, Clock, Phone, ArrowRight, ThermometerSun } from 'lucide-react';
|
import { Clock, Phone, ArrowRight } from 'lucide-react';
|
||||||
import { getCityBySlug, getAllCitySlugs, dolnySlaskCities } from '@/content/cities';
|
import { getCityBySlug, getAllCitySlugs, dolnySlaskCities } from '@/content/cities';
|
||||||
import { HeroSection } from '@/components/sections/HeroSection';
|
import { HeroSection } from '@/components/sections/HeroSection';
|
||||||
import { ProcessSteps } from '@/components/sections/ProcessSteps';
|
import { ProcessSteps } from '@/components/sections/ProcessSteps';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Metadata } from 'next';
|
import { Metadata } from 'next';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { MapPin, Clock, Phone, ArrowRight, ThermometerSun } from 'lucide-react';
|
import { MapPin, Clock, Phone, ArrowRight } from 'lucide-react';
|
||||||
import { getDistrictBySlug, getAllDistrictSlugs, wroclawDistricts } from '@/content/wroclaw-districts';
|
import { getDistrictBySlug, getAllDistrictSlugs, wroclawDistricts } from '@/content/wroclaw-districts';
|
||||||
import { HeroSection } from '@/components/sections/HeroSection';
|
import { HeroSection } from '@/components/sections/HeroSection';
|
||||||
import { ProcessSteps } from '@/components/sections/ProcessSteps';
|
import { ProcessSteps } from '@/components/sections/ProcessSteps';
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const contactFormSchema = z.object({
|
|||||||
consent: z.boolean().refine((val) => val === true, "Zgoda RODO jest wymagana"),
|
consent: z.boolean().refine((val) => val === true, "Zgoda RODO jest wymagana"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function submitContactForm(prevState: any, formData: FormData) {
|
export async function submitContactForm(prevState: unknown, formData: FormData) {
|
||||||
try {
|
try {
|
||||||
if (process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY && process.env.RECAPTCHA_SECRET_KEY) {
|
if (process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY && process.env.RECAPTCHA_SECRET_KEY) {
|
||||||
const token = formData.get('recaptchaToken') as string;
|
const token = formData.get('recaptchaToken') as string;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default function KontaktPage() {
|
|||||||
ctaLink={`tel:${companyDetails.phoneRaw}`}
|
ctaLink={`tel:${companyDetails.phoneRaw}`}
|
||||||
secondaryCtaText="Napisz maila"
|
secondaryCtaText="Napisz maila"
|
||||||
secondaryCtaLink={`mailto:${companyDetails.email}`}
|
secondaryCtaLink={`mailto:${companyDetails.email}`}
|
||||||
backgroundImage="https://images.unsplash.com/photo-1516387938699-a93567ec168e?w=1600&q=80"
|
backgroundImage="/images/hero/hero-section-image.webp"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ContactSection />
|
<ContactSection />
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ export default function ONasPage() {
|
|||||||
ctaLink="/kontakt"
|
ctaLink="/kontakt"
|
||||||
secondaryCtaText="Nasze realizacje"
|
secondaryCtaText="Nasze realizacje"
|
||||||
secondaryCtaLink="/realizacje"
|
secondaryCtaLink="/realizacje"
|
||||||
backgroundImage="https://images.unsplash.com/photo-1504307651254-35680f356f12?w=1600&q=80"
|
backgroundImage="/images/about/about-story.webp"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AboutStory />
|
<AboutStory />
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default function RealizacjePage() {
|
|||||||
ctaLink="/uslugi-domowe"
|
ctaLink="/uslugi-domowe"
|
||||||
secondaryCtaText="Skontaktuj się"
|
secondaryCtaText="Skontaktuj się"
|
||||||
secondaryCtaLink="/kontakt"
|
secondaryCtaLink="/kontakt"
|
||||||
backgroundImage="https://images.unsplash.com/photo-1503387762-592deb58ef4e?w=1600&q=80"
|
backgroundImage="/images/hero/hero-section-image.webp"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ProjectsGrid />
|
<ProjectsGrid />
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const orderSchema = z.object({
|
|||||||
total: z.string(),
|
total: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function submitOrderForm(prevState: any, formData: FormData) {
|
export async function submitOrderForm(prevState: unknown, formData: FormData) {
|
||||||
try {
|
try {
|
||||||
if (process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY && process.env.RECAPTCHA_SECRET_KEY) {
|
if (process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY && process.env.RECAPTCHA_SECRET_KEY) {
|
||||||
const token = formData.get('recaptchaToken') as string;
|
const token = formData.get('recaptchaToken') as string;
|
||||||
@@ -44,7 +44,7 @@ export async function submitOrderForm(prevState: any, formData: FormData) {
|
|||||||
const products = JSON.parse(data.products);
|
const products = JSON.parse(data.products);
|
||||||
const total = parseFloat(data.total);
|
const total = parseFloat(data.total);
|
||||||
|
|
||||||
const productRows = products.map((p: any) =>
|
const productRows = products.map((p: { name: string; quantity: number; price: number }) =>
|
||||||
`<tr><td style="padding:8px;border-bottom:1px solid #f1f5f9;">${p.name}</td><td style="padding:8px;border-bottom:1px solid #f1f5f9;text-align:center;">${p.quantity}</td><td style="padding:8px;border-bottom:1px solid #f1f5f9;text-align:right;">${(p.price * p.quantity).toLocaleString('pl-PL')} zł</td></tr>`
|
`<tr><td style="padding:8px;border-bottom:1px solid #f1f5f9;">${p.name}</td><td style="padding:8px;border-bottom:1px solid #f1f5f9;text-align:center;">${p.quantity}</td><td style="padding:8px;border-bottom:1px solid #f1f5f9;text-align:right;">${(p.price * p.quantity).toLocaleString('pl-PL')} zł</td></tr>`
|
||||||
).join('');
|
).join('');
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ export async function submitOrderForm(prevState: any, formData: FormData) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return { success: true, message: 'Dziękujemy! Skontaktujemy się z Tobą w ciągu 24 godzin, aby potwierdzić zamówienie i ustalić termin montażu.' };
|
return { success: true, message: 'Dziękujemy! Skontaktujemy się z Tobą w ciągu 24 godzin, aby potwierdzić zamówienie i ustalić termin montażu.' };
|
||||||
} catch (e) {
|
} catch {
|
||||||
return { success: false, message: 'Wystąpił błąd. Spróbuj ponownie lub zadzwoń do nas bezpośrednio.' };
|
return { success: false, message: 'Wystąpił błąd. Spróbuj ponownie lub zadzwoń do nas bezpośrednio.' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default function ZamowieniePage() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (state.success) clearCart();
|
if (state.success) clearCart();
|
||||||
}, [state.success]);
|
}, [state.success, clearCart]);
|
||||||
|
|
||||||
if (items.length === 0 && !state.success) {
|
if (items.length === 0 && !state.success) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export default async function MultisplitInquiryPage({ params }: Props) {
|
|||||||
const product = getProductBySlug(slug);
|
const product = getProductBySlug(slug);
|
||||||
if (!product || product.category !== 'multisplit') notFound();
|
if (!product || product.category !== 'multisplit') notFound();
|
||||||
|
|
||||||
const specIcons: Record<string, any> = {
|
const specIcons: Record<string, React.ElementType> = {
|
||||||
'Moc chłodnicza': Zap,
|
'Moc chłodnicza': Zap,
|
||||||
'Moc grzewcza': Thermometer,
|
'Moc grzewcza': Thermometer,
|
||||||
'Głośność wewnętrzna (dB)': Volume2,
|
'Głośność wewnętrzna (dB)': Volume2,
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ const multisplitFormSchema = z.object({
|
|||||||
productSku: z.string(),
|
productSku: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function submitMultisplitForm(data: any) {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export async function submitMultisplitForm(data: Record<string, any>) {
|
||||||
try {
|
try {
|
||||||
if (process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY && process.env.RECAPTCHA_SECRET_KEY) {
|
if (process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY && process.env.RECAPTCHA_SECRET_KEY) {
|
||||||
if (!data.recaptchaToken) {
|
if (!data.recaptchaToken) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable */
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
|
|||||||
+25
-22
@@ -37,13 +37,16 @@ export default function CookieBanner() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
setPrefs({
|
const timer = setTimeout(() => {
|
||||||
necessary: true,
|
setPrefs({
|
||||||
analytics: analytics,
|
necessary: true,
|
||||||
marketing: marketing,
|
analytics: analytics,
|
||||||
advertising: advertising,
|
marketing: marketing,
|
||||||
});
|
advertising: advertising,
|
||||||
setShowDetails(false);
|
});
|
||||||
|
setShowDetails(false);
|
||||||
|
}, 0);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
}
|
}
|
||||||
}, [isOpen, analytics, marketing, advertising]);
|
}, [isOpen, analytics, marketing, advertising]);
|
||||||
|
|
||||||
@@ -54,17 +57,17 @@ export default function CookieBanner() {
|
|||||||
if (!isInitialized || !isOpen) return null;
|
if (!isInitialized || !isOpen) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed bottom-0 left-0 right-0 z-[100] p-4 sm:p-6">
|
<div className="fixed bottom-0 left-0 right-0 z-[100] p-0 sm:p-6">
|
||||||
<div className="max-w-4xl mx-auto bg-white rounded-2xl shadow-2xl border border-border overflow-hidden">
|
<div className="max-w-4xl mx-auto bg-white sm:rounded-2xl shadow-[0_-4px_20px_rgba(0,0,0,0.1)] sm:shadow-2xl border-t sm:border border-border overflow-hidden pb-safe sm:pb-0">
|
||||||
{/* Główny pasek */}
|
{/* Główny pasek */}
|
||||||
<div className="p-5 sm:p-6">
|
<div className="p-4 sm:p-6">
|
||||||
<div className="flex items-start gap-3 mb-4">
|
<div className="flex items-start gap-3 mb-3 sm:mb-4">
|
||||||
<span className="text-2xl shrink-0">🍪</span>
|
<span className="hidden sm:block text-2xl shrink-0">🍪</span>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-base font-extrabold text-primary font-heading mb-1">
|
<h2 className="text-sm sm:text-base font-extrabold text-primary font-heading mb-1">
|
||||||
Ta strona używa plików cookie
|
Ta strona używa plików cookie
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-text-muted leading-relaxed">
|
<p className="text-xs sm:text-sm text-text-muted leading-relaxed">
|
||||||
Używamy plików cookie, aby zapewnić prawidłowe działanie strony, analizować ruch
|
Używamy plików cookie, aby zapewnić prawidłowe działanie strony, analizować ruch
|
||||||
i dostosowywać treści. Możesz wybrać które kategorie akceptujesz.{' '}
|
i dostosowywać treści. Możesz wybrać które kategorie akceptujesz.{' '}
|
||||||
<Link href="/polityka-prywatnosci" className="text-accent underline hover:no-underline">
|
<Link href="/polityka-prywatnosci" className="text-accent underline hover:no-underline">
|
||||||
@@ -149,32 +152,32 @@ export default function CookieBanner() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Przyciski */}
|
{/* Przyciski */}
|
||||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-2">
|
<div className="grid grid-cols-2 sm:flex sm:flex-row items-stretch sm:items-center gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={storeAcceptAll}
|
onClick={storeAcceptAll}
|
||||||
className="flex-1 sm:flex-none px-5 py-2.5 bg-accent text-white font-bold rounded-xl text-sm hover:bg-accent/90 transition-colors"
|
className="col-span-2 sm:col-span-1 flex-1 sm:flex-none px-4 py-2 sm:px-5 sm:py-2.5 bg-accent text-white font-bold rounded-xl text-xs sm:text-sm hover:bg-accent/90 transition-colors min-h-[44px]"
|
||||||
>
|
>
|
||||||
Akceptuj wszystkie
|
Akceptuj wszystkie
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={storeRejectOptional}
|
onClick={storeRejectOptional}
|
||||||
className="flex-1 sm:flex-none px-5 py-2.5 bg-slate-100 text-primary font-bold rounded-xl text-sm hover:bg-slate-200 transition-colors"
|
className="col-span-1 flex-1 sm:flex-none px-4 py-2 sm:px-5 sm:py-2.5 bg-slate-100 text-primary font-bold rounded-xl text-xs sm:text-sm hover:bg-slate-200 transition-colors min-h-[44px]"
|
||||||
>
|
>
|
||||||
Odrzuć opcjonalne
|
Odrzuć
|
||||||
</button>
|
</button>
|
||||||
{showDetails ? (
|
{showDetails ? (
|
||||||
<button
|
<button
|
||||||
onClick={saveSelected}
|
onClick={saveSelected}
|
||||||
className="flex-1 sm:flex-none px-5 py-2.5 bg-white border border-border text-primary font-bold rounded-xl text-sm hover:border-accent transition-colors"
|
className="col-span-1 flex-1 sm:flex-none px-4 py-2 sm:px-5 sm:py-2.5 bg-white border border-border text-primary font-bold rounded-xl text-xs sm:text-sm hover:border-accent transition-colors min-h-[44px]"
|
||||||
>
|
>
|
||||||
Zapisz wybór
|
Zapisz
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowDetails(true)}
|
onClick={() => setShowDetails(true)}
|
||||||
className="flex-1 sm:flex-none px-5 py-2.5 bg-white border border-border text-primary font-bold rounded-xl text-sm hover:border-accent transition-colors"
|
className="col-span-1 flex-1 sm:flex-none px-4 py-2 sm:px-5 sm:py-2.5 bg-white border border-border text-primary font-bold rounded-xl text-xs sm:text-sm hover:border-accent transition-colors min-h-[44px]"
|
||||||
>
|
>
|
||||||
Dostosuj ustawienia
|
Dostosuj
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Wrench, Zap, Info, Clock, Check, AlertTriangle, Phone, CheckCircle2 } from 'lucide-react';
|
import { Wrench, Zap, Check, AlertTriangle, Phone, CheckCircle2 } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
|
||||||
import { companyDetails } from '@/content/company';
|
import { companyDetails } from '@/content/company';
|
||||||
import { submitAwariaForm } from '@/app/zgloszenie-awarii/actions';
|
import { submitAwariaForm } from '@/app/zgloszenie-awarii/actions';
|
||||||
import { getRecaptchaToken } from '@/lib/getRecaptchaToken';
|
import { getRecaptchaToken } from '@/lib/getRecaptchaToken';
|
||||||
@@ -127,6 +126,7 @@ export function AwariaClient() {
|
|||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
|
|
||||||
const recaptchaToken = await getRecaptchaToken('submit');
|
const recaptchaToken = await getRecaptchaToken('submit');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const formDataObj: any = {
|
const formDataObj: any = {
|
||||||
isUrgent,
|
isUrgent,
|
||||||
brand,
|
brand,
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import { useState, useEffect, useMemo } from 'react';
|
import { useState, useEffect, useMemo } from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Image from 'next/image';
|
|
||||||
import { ArrowRight, ArrowLeft, Check, Thermometer, Home, Zap, ShoppingCart, Phone, RotateCcw } from 'lucide-react';
|
import { ArrowRight, ArrowLeft, Check, Home, Zap, ShoppingCart, Phone } from 'lucide-react';
|
||||||
import { Product } from '@/content/products';
|
import { Product } from '@/content/products';
|
||||||
import { companyDetails } from '@/content/company';
|
import { companyDetails } from '@/content/company';
|
||||||
import { ProductCard } from '@/components/shop/ProductCard';
|
import { ProductCard } from '@/components/shop/ProductCard';
|
||||||
@@ -20,9 +20,10 @@ function useSessionState<T>(key: string, initialValue: T) {
|
|||||||
try {
|
try {
|
||||||
const item = window.sessionStorage.getItem(key);
|
const item = window.sessionStorage.getItem(key);
|
||||||
if (item) {
|
if (item) {
|
||||||
setState(JSON.parse(item));
|
const timer = setTimeout(() => setState(JSON.parse(item)), 0);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch {}
|
||||||
}, [key]);
|
}, [key]);
|
||||||
|
|
||||||
const setValue = (value: T | ((val: T) => T)) => {
|
const setValue = (value: T | ((val: T) => T)) => {
|
||||||
@@ -32,7 +33,7 @@ function useSessionState<T>(key: string, initialValue: T) {
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch {}
|
||||||
};
|
};
|
||||||
|
|
||||||
return [state, setValue] as const;
|
return [state, setValue] as const;
|
||||||
@@ -203,35 +204,6 @@ export function KalkulatorClient({ products }: Props) {
|
|||||||
return pages;
|
return pages;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFinish = () => {
|
|
||||||
setStep(4);
|
|
||||||
setPage(1);
|
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
setStep(0);
|
|
||||||
setPage(1);
|
|
||||||
Object.keys(window.sessionStorage).forEach(key => {
|
|
||||||
if (key.startsWith('calc_')) window.sessionStorage.removeItem(key);
|
|
||||||
});
|
|
||||||
setDeviceType('klimatyzacja');
|
|
||||||
setBuilding('apartment');
|
|
||||||
setPomieszczenia([{ nazwa: 'Pokój 1', m2: 20 }]);
|
|
||||||
setFloor('middle');
|
|
||||||
setSun('south');
|
|
||||||
setPipes('medium');
|
|
||||||
setInstallMethod('korytko');
|
|
||||||
setOutdoorUnitLocation('balcony');
|
|
||||||
setHasSocket('yes');
|
|
||||||
setBudget(8000);
|
|
||||||
setPreferredBrand('');
|
|
||||||
setEnergyClass('');
|
|
||||||
setMaxNoise(null);
|
|
||||||
setMaxNoiseOut(null);
|
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
setStep(s => s + 1);
|
setStep(s => s + 1);
|
||||||
setPage(1);
|
setPage(1);
|
||||||
@@ -621,7 +593,7 @@ export function KalkulatorClient({ products }: Props) {
|
|||||||
{ value: 'facade', label: 'Elewacja / balkon francuski', sub: 'Wymaga użycia podnośnika koszowego (ok. 600 zł/h) i pompki skroplin (+ ok. 400 zł)' },
|
{ value: 'facade', label: 'Elewacja / balkon francuski', sub: 'Wymaga użycia podnośnika koszowego (ok. 600 zł/h) i pompki skroplin (+ ok. 400 zł)' },
|
||||||
{ value: 'roof', label: 'Dach budynku', sub: 'Wymaga odpowiedniego stelaża lub konstrukcji dachowej' },
|
{ value: 'roof', label: 'Dach budynku', sub: 'Wymaga odpowiedniego stelaża lub konstrukcji dachowej' },
|
||||||
].map(o => (
|
].map(o => (
|
||||||
<button key={o.value} onClick={() => setOutdoorUnitLocation(o.value as any)}
|
<button key={o.value} onClick={() => setOutdoorUnitLocation(o.value as 'balcony' | 'facade' | 'roof')}
|
||||||
className={`p-4 rounded-xl border-2 text-left transition-all ${outdoorUnitLocation === o.value ? 'border-accent bg-accent/5' : 'border-border hover:border-accent'}`}>
|
className={`p-4 rounded-xl border-2 text-left transition-all ${outdoorUnitLocation === o.value ? 'border-accent bg-accent/5' : 'border-border hover:border-accent'}`}>
|
||||||
<p className="text-sm font-bold text-primary">{o.label}</p>
|
<p className="text-sm font-bold text-primary">{o.label}</p>
|
||||||
<p className="text-xs text-text-muted">{o.sub}</p>
|
<p className="text-xs text-text-muted">{o.sub}</p>
|
||||||
@@ -643,7 +615,7 @@ export function KalkulatorClient({ products }: Props) {
|
|||||||
{ value: 'no', label: 'Nie', sub: 'Instalator musi dociągnąć prąd z wewnątrz (+ ok. 200 zł)' },
|
{ value: 'no', label: 'Nie', sub: 'Instalator musi dociągnąć prąd z wewnątrz (+ ok. 200 zł)' },
|
||||||
{ value: 'dont_know', label: 'Nie wiem / Do ustalenia', sub: 'Do ustalenia w trakcie wizji lokalnej' },
|
{ value: 'dont_know', label: 'Nie wiem / Do ustalenia', sub: 'Do ustalenia w trakcie wizji lokalnej' },
|
||||||
].map(o => (
|
].map(o => (
|
||||||
<button key={o.value} onClick={() => setHasSocket(o.value as any)}
|
<button key={o.value} onClick={() => setHasSocket(o.value as 'yes' | 'no' | 'dont_know')}
|
||||||
className={`p-4 rounded-xl border-2 text-left transition-all ${hasSocket === o.value ? 'border-accent bg-accent/5' : 'border-border hover:border-accent'}`}>
|
className={`p-4 rounded-xl border-2 text-left transition-all ${hasSocket === o.value ? 'border-accent bg-accent/5' : 'border-border hover:border-accent'}`}>
|
||||||
<p className="text-sm font-bold text-primary">{o.label}</p>
|
<p className="text-sm font-bold text-primary">{o.label}</p>
|
||||||
<p className="text-xs text-text-muted">{o.sub}</p>
|
<p className="text-xs text-text-muted">{o.sub}</p>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useRef, useEffect, useMemo } from "react";
|
import { useState, useMemo } from "react";
|
||||||
import { Search } from "lucide-react";
|
import { Search } from "lucide-react";
|
||||||
import { getProducts } from "@/content/products";
|
import { getProducts } from "@/content/products";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -85,7 +85,7 @@ export function GlobalSearch() {
|
|||||||
>
|
>
|
||||||
<div className="w-16 h-16 bg-white border border-border rounded-md overflow-hidden relative shrink-0">
|
<div className="w-16 h-16 bg-white border border-border rounded-md overflow-hidden relative shrink-0">
|
||||||
<Image
|
<Image
|
||||||
src={product.image || 'https://images.unsplash.com/photo-1621905251189-08b45d6a269e?w=800&q=80'}
|
src={product.image || '/images/about/about-story.webp'}
|
||||||
alt={product.name}
|
alt={product.name}
|
||||||
fill
|
fill
|
||||||
className="object-contain p-1 mix-blend-multiply"
|
className="object-contain p-1 mix-blend-multiply"
|
||||||
@@ -104,7 +104,7 @@ export function GlobalSearch() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="p-8 text-center text-text-muted">
|
<div className="p-8 text-center text-text-muted">
|
||||||
Brak wyników dla <span className="font-semibold">"{query}"</span>
|
Brak wyników dla <span className="font-semibold">"{query}"</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { User, ShoppingBag, Menu, Phone, Wrench } from "lucide-react";
|
import { ShoppingBag, Menu, Phone, Wrench } from "lucide-react";
|
||||||
import { NavDropdown } from "./NavDropdown";
|
import { NavDropdown } from "./NavDropdown";
|
||||||
import { GlobalSearch } from "./GlobalSearch";
|
import { GlobalSearch } from "./GlobalSearch";
|
||||||
import { MobileMenu } from "./MobileMenu";
|
import { MobileMenu } from "./MobileMenu";
|
||||||
@@ -20,7 +20,7 @@ export function Header() {
|
|||||||
const cartItems = useCartStore((state) => state.items);
|
const cartItems = useCartStore((state) => state.items);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
const t = setTimeout(() => setMounted(true), 0);
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
if (window.scrollY > 40) {
|
if (window.scrollY > 40) {
|
||||||
setIsScrolled(true);
|
setIsScrolled(true);
|
||||||
@@ -30,7 +30,10 @@ export function Header() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("scroll", handleScroll);
|
window.addEventListener("scroll", handleScroll);
|
||||||
return () => window.removeEventListener("scroll", handleScroll);
|
return () => {
|
||||||
|
window.removeEventListener("scroll", handleScroll);
|
||||||
|
clearTimeout(t);
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const cartCount = mounted
|
const cartCount = mounted
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export function AboutStory() {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="relative aspect-[4/5] rounded-3xl overflow-hidden shadow-2xl">
|
<div className="relative aspect-[4/5] rounded-3xl overflow-hidden shadow-2xl">
|
||||||
<Image
|
<Image
|
||||||
src="https://images.unsplash.com/photo-1621905251189-08b45d6a269e?w=800&q=80"
|
src="/images/about/about-story.webp"
|
||||||
alt="Zespół firmy w trakcie pracy"
|
alt="Zespół firmy w trakcie pracy"
|
||||||
fill
|
fill
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { Wind, LayoutGrid, Sparkles, ArrowRight } from 'lucide-react';
|
import { Wind, LayoutGrid, ArrowRight } from 'lucide-react';
|
||||||
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CheckCircle2, Zap, ShieldCheck, Banknote } from "lucide-react";
|
import { CheckCircle2, Zap, ShieldCheck } from "lucide-react";
|
||||||
|
|
||||||
export function AdvantagesBar() {
|
export function AdvantagesBar() {
|
||||||
const advantages = [
|
const advantages = [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { ArrowRight, Calendar, User } from "lucide-react";
|
import { ArrowRight, Calendar } from "lucide-react";
|
||||||
import { SectionHeading } from "../common/SectionHeading";
|
import { SectionHeading } from "../common/SectionHeading";
|
||||||
import { getLatestPosts } from "@/content/posts";
|
import { getLatestPosts } from "@/content/posts";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Image from 'next/image';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
const brands = [
|
const brands = [
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Image from 'next/image';
|
|
||||||
import { SectionHeading } from '../common/SectionHeading';
|
import { SectionHeading } from '../common/SectionHeading';
|
||||||
import { companyDetails } from '@/content/company';
|
import { companyDetails } from '@/content/company';
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export function FeaturedPost({ post }: FeaturedPostProps) {
|
|||||||
{/* Obraz */}
|
{/* Obraz */}
|
||||||
<div className="relative w-full md:w-3/5 aspect-video md:aspect-auto md:min-h-[400px]">
|
<div className="relative w-full md:w-3/5 aspect-video md:aspect-auto md:min-h-[400px]">
|
||||||
<Image
|
<Image
|
||||||
src={post.image || 'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=1200&q=80'}
|
src={post.image || '/images/hero/hero-section-image.webp'}
|
||||||
alt={post.title}
|
alt={post.title}
|
||||||
fill
|
fill
|
||||||
className="object-cover transition-transform duration-700 group-hover:scale-105"
|
className="object-cover transition-transform duration-700 group-hover:scale-105"
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export const FundingSection = () => {
|
|||||||
<span className="font-semibold text-gray-900">45 000 zł</span>
|
<span className="font-semibold text-gray-900">45 000 zł</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between pb-4 border-b border-gray-100 text-green-600 font-medium">
|
<div className="flex justify-between pb-4 border-b border-gray-100 text-green-600 font-medium">
|
||||||
<span>Dotacja "Czyste Powietrze"</span>
|
<span>Dotacja "Czyste Powietrze"</span>
|
||||||
<span>- 20 500 zł</span>
|
<span>- 20 500 zł</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between pb-4 border-b border-gray-100 text-green-600 font-medium">
|
<div className="flex justify-between pb-4 border-b border-gray-100 text-green-600 font-medium">
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function HeroSection({
|
|||||||
features
|
features
|
||||||
}: HeroSectionProps = {}) {
|
}: HeroSectionProps = {}) {
|
||||||
return (
|
return (
|
||||||
<section className="relative bg-primary min-h-[480px] md:min-h-[600px] flex items-center overflow-hidden">
|
<section className="relative bg-primary min-h-[480px] md:min-h-[600px] flex items-center overflow-hidden max-w-[2560px] mx-auto">
|
||||||
{/* TODO ZDJĘCIE HERO: realne zdjęcie firmy/montażu, format poziomy min. 1920×1080, plik public/hero.jpg */}
|
{/* TODO ZDJĘCIE HERO: realne zdjęcie firmy/montażu, format poziomy min. 1920×1080, plik public/hero.jpg */}
|
||||||
<Image
|
<Image
|
||||||
src={backgroundImage}
|
src={backgroundImage}
|
||||||
@@ -85,6 +85,11 @@ export function HeroSection({
|
|||||||
<ArrowRight className="size-5 group-hover:translate-x-1 transition-transform" />
|
<ArrowRight className="size-5 group-hover:translate-x-1 transition-transform" />
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
{secondaryCtaText && secondaryCtaLink && (
|
||||||
|
<Link href={secondaryCtaLink} className="w-fit sm:w-auto btn btn-outline bg-white/10 text-white border-white/20 hover:bg-white hover:text-primary font-bold px-8">
|
||||||
|
<span>{secondaryCtaText}</span>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { ThermometerSun, Home, ChevronRight, Wrench, ShieldCheck, ArrowRight } from "lucide-react";
|
import { ThermometerSun, Home, ChevronRight, ArrowRight } from "lucide-react";
|
||||||
|
|
||||||
export function PillarsSection() {
|
export function PillarsSection() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export function PostsGrid({ posts, featuredPost }: PostsGridProps) {
|
|||||||
>
|
>
|
||||||
<Link href={`/blog/${post.slug}`} className="relative aspect-[16/10] overflow-hidden">
|
<Link href={`/blog/${post.slug}`} className="relative aspect-[16/10] overflow-hidden">
|
||||||
<Image
|
<Image
|
||||||
src={post.image || 'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=800&q=80'}
|
src={post.image || '/images/hero/hero-section-image.webp'}
|
||||||
alt={post.title}
|
alt={post.title}
|
||||||
fill
|
fill
|
||||||
className="object-cover transition-transform duration-700 group-hover:scale-105"
|
className="object-cover transition-transform duration-700 group-hover:scale-105"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { pricingData, PricingItem } from '@/content/pricing';
|
import { pricingData, PricingItem } from '@/content/pricing';
|
||||||
import { Check, Info, ArrowRight, Download } from 'lucide-react';
|
import { Check, Download } from 'lucide-react';
|
||||||
|
|
||||||
export const PricingTable = () => {
|
export const PricingTable = () => {
|
||||||
const splitItems = pricingData.filter((item) => item.category === 'split');
|
const splitItems = pricingData.filter((item) => item.category === 'split');
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
|
||||||
import { projectsData, Project } from '@/content/projects';
|
import { projectsData, Project } from '@/content/projects';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { MapPin, ArrowRight } from 'lucide-react';
|
import { MapPin } from 'lucide-react';
|
||||||
|
|
||||||
type Category = 'wszystkie' | Project['category'];
|
type Category = 'wszystkie' | Project['category'];
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export function Testimonials() {
|
|||||||
<Star key={j} className="size-4 fill-yellow-400 text-yellow-400" />
|
<Star key={j} className="size-4 fill-yellow-400 text-yellow-400" />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-text-muted leading-relaxed flex-1">"{t.text}"</p>
|
<p className="text-sm text-text-muted leading-relaxed flex-1">"{t.text}"</p>
|
||||||
<div className="pt-3 border-t border-border">
|
<div className="pt-3 border-t border-border">
|
||||||
<p className="text-sm font-bold text-primary">{t.name}</p>
|
<p className="text-sm font-bold text-primary">{t.name}</p>
|
||||||
<p className="text-xs text-text-muted">{t.city}</p>
|
<p className="text-xs text-text-muted">{t.city}</p>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Check, Loader2, CheckCircle2 } from 'lucide-react';
|
import { Loader2, CheckCircle2 } from 'lucide-react';
|
||||||
import { submitMultisplitForm } from '@/app/zapytanie-multisplit/actions';
|
import { submitMultisplitForm } from '@/app/zapytanie-multisplit/actions';
|
||||||
import { getRecaptchaToken } from '@/lib/getRecaptchaToken';
|
import { getRecaptchaToken } from '@/lib/getRecaptchaToken';
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { ShoppingCart, GitCompare, Phone, Check } from 'lucide-react';
|
import { ShoppingCart, GitCompare, Phone } from 'lucide-react';
|
||||||
import { Product } from '@/content/products';
|
import { Product } from '@/content/products';
|
||||||
import { useCartStore } from '@/lib/cart-store';
|
import { useCartStore } from '@/lib/cart-store';
|
||||||
import { useCompareStore } from '@/lib/compare-store';
|
import { useCompareStore } from '@/lib/compare-store';
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ interface ProductGalleryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ProductGallery({ image, name, images }: ProductGalleryProps) {
|
export function ProductGallery({ image, name, images }: ProductGalleryProps) {
|
||||||
const [mainImage, setMainImage] = useState(image || 'https://images.unsplash.com/photo-1581094794329-c8112a89af12?w=800&q=80');
|
const [mainImage, setMainImage] = useState(image || '/images/hero/shop-section-image.webp');
|
||||||
|
|
||||||
// Tworzymy tablicę miniatur, używamy placeholdera jako drugiej by pokazać funkcjonalność
|
// Tworzymy tablicę miniatur, używamy placeholdera jako drugiej by pokazać funkcjonalność
|
||||||
const gallery = images && images.length > 0
|
const gallery = images && images.length > 0
|
||||||
? images
|
? images
|
||||||
: [image, 'https://images.unsplash.com/photo-1584622650111-993a426fbf0a?w=800&q=80'];
|
: [image, '/images/hero/shop-section-image.webp'];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { useState } from 'react';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import {
|
import {
|
||||||
ArrowLeft, Phone, ShoppingCart, GitCompare, Check,
|
Phone, ShoppingCart, GitCompare, Check,
|
||||||
Thermometer, Volume2, Zap, Wind, Star, Info, Eye, X
|
Thermometer, Volume2, Zap, Wind, Star, Info, Eye, X, type LucideIcon
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { Product } from '@/content/products';
|
import { Product } from '@/content/products';
|
||||||
import { useCartStore } from '@/lib/cart-store';
|
import { useCartStore } from '@/lib/cart-store';
|
||||||
@@ -114,7 +114,7 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
|
|||||||
currentProduct.specs['Hałas wewnątrz (dB)'] && { icon: Volume2, label: 'Hałas wewnątrz', value: `${currentProduct.specs['Hałas wewnątrz (dB)']} dB`, color: 'text-purple-600 bg-purple-50' },
|
currentProduct.specs['Hałas wewnątrz (dB)'] && { icon: Volume2, label: 'Hałas wewnątrz', value: `${currentProduct.specs['Hałas wewnątrz (dB)']} dB`, color: 'text-purple-600 bg-purple-50' },
|
||||||
currentProduct.specs['Czynnik chłodniczy'] && { icon: Wind, label: 'Czynnik', value: currentProduct.specs['Czynnik chłodniczy'], color: 'text-teal-600 bg-teal-50' },
|
currentProduct.specs['Czynnik chłodniczy'] && { icon: Wind, label: 'Czynnik', value: currentProduct.specs['Czynnik chłodniczy'], color: 'text-teal-600 bg-teal-50' },
|
||||||
currentProduct.specs['Grzanie do'] && { icon: Zap, label: 'Grzanie do', value: `-${currentProduct.specs['Grzanie do']}°C`, color: 'text-accent bg-accent-light' },
|
currentProduct.specs['Grzanie do'] && { icon: Zap, label: 'Grzanie do', value: `-${currentProduct.specs['Grzanie do']}°C`, color: 'text-accent bg-accent-light' },
|
||||||
].filter(Boolean) as { icon: any; label: string; value: string; color: string }[];
|
].filter(Boolean) as { icon: LucideIcon; label: string; value: string; color: string }[];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="bg-slate-50 min-h-screen pb-16">
|
<main className="bg-slate-50 min-h-screen pb-16">
|
||||||
|
|||||||
@@ -96,14 +96,17 @@ export function ProductsGrid({ products, brands }: ProductsGridProps) {
|
|||||||
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSearchQuery(urlSearch);
|
const timer = setTimeout(() => {
|
||||||
setSelectedBrands(urlBrand ? [urlBrand] : []);
|
setSearchQuery(urlSearch);
|
||||||
if (urlCategory !== 'all') {
|
setSelectedBrands(urlBrand ? [urlBrand] : []);
|
||||||
setCategory(urlCategory as 'scienne' | 'multisplit' | 'podlogowe' | 'jednostka-scienna');
|
if (urlCategory !== 'all') {
|
||||||
} else {
|
setCategory(urlCategory as 'scienne' | 'multisplit' | 'podlogowe' | 'jednostka-scienna');
|
||||||
setCategory('all');
|
} else {
|
||||||
}
|
setCategory('all');
|
||||||
setPage(1);
|
}
|
||||||
|
setPage(1);
|
||||||
|
}, 0);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
}, [urlSearch, urlBrand, urlCategory]);
|
}, [urlSearch, urlBrand, urlCategory]);
|
||||||
|
|
||||||
const toggleBrand = (b: string) => {
|
const toggleBrand = (b: string) => {
|
||||||
@@ -171,7 +174,7 @@ export function ProductsGrid({ products, brands }: ProductsGridProps) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}, [products, category, selectedBrands, priceMin, priceMax, selectedRooms, halasMin, halasMax, selectedColors]);
|
}, [products, category, selectedBrands, priceMin, priceMax, selectedRooms, halasMin, halasMax, selectedColors, searchQuery]);
|
||||||
|
|
||||||
const totalPages = Math.ceil(filtered.length / PER_PAGE);
|
const totalPages = Math.ceil(filtered.length / PER_PAGE);
|
||||||
const paginated = filtered.slice((page - 1) * PER_PAGE, page * PER_PAGE);
|
const paginated = filtered.slice((page - 1) * PER_PAGE, page * PER_PAGE);
|
||||||
@@ -217,7 +220,7 @@ export function ProductsGrid({ products, brands }: ProductsGridProps) {
|
|||||||
|
|
||||||
const hasFilters = category !== 'all' || selectedBrands.length > 0 || searchQuery !== '' || priceMin || priceMax || selectedRooms.length > 0 || selectedColors.length > 0 || halasMin > 15 || halasMax < 50;
|
const hasFilters = category !== 'all' || selectedBrands.length > 0 || searchQuery !== '' || priceMin || priceMax || selectedRooms.length > 0 || selectedColors.length > 0 || halasMin > 15 || halasMax < 50;
|
||||||
|
|
||||||
const Sidebar = () => (
|
const sidebarContent = (
|
||||||
<aside className="w-full">
|
<aside className="w-full">
|
||||||
{/* Klimatyzatory - typ */}
|
{/* Klimatyzatory - typ */}
|
||||||
<Accordion title="Klimatyzatory">
|
<Accordion title="Klimatyzatory">
|
||||||
@@ -452,7 +455,7 @@ export function ProductsGrid({ products, brands }: ProductsGridProps) {
|
|||||||
<X className="size-5 text-text-muted" />
|
<X className="size-5 text-text-muted" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<Sidebar />
|
{sidebarContent}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -469,7 +472,7 @@ export function ProductsGrid({ products, brands }: ProductsGridProps) {
|
|||||||
<X className="size-3" /> Wyczyść filtry
|
<X className="size-3" /> Wyczyść filtry
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<Sidebar />
|
{sidebarContent}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export const useConsentStore = create<ConsentState>((set) => ({
|
|||||||
stored = JSON.stringify(newData);
|
stored = JSON.stringify(newData);
|
||||||
localStorage.setItem(CONSENT_KEY, stored);
|
localStorage.setItem(CONSENT_KEY, stored);
|
||||||
localStorage.removeItem(OLD_CONSENT_KEY);
|
localStorage.removeItem(OLD_CONSENT_KEY);
|
||||||
} catch (e) {
|
} catch {
|
||||||
// Ignore if parse fails
|
// Ignore if parse fails
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ export const useConsentStore = create<ConsentState>((set) => ({
|
|||||||
marketing: !!parsed.marketing,
|
marketing: !!parsed.marketing,
|
||||||
advertising: !!parsed.advertising,
|
advertising: !!parsed.advertising,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch {
|
||||||
// If parsing fails, show banner
|
// If parsing fails, show banner
|
||||||
set({ isInitialized: true, isOpen: true });
|
set({ isInitialized: true, isOpen: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
grecaptcha: {
|
||||||
|
ready: (cb: () => void) => void;
|
||||||
|
execute: (siteKey: string, options: { action: string }) => Promise<string>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getRecaptchaToken(action: string = 'submit'): Promise<string | null> {
|
export function getRecaptchaToken(action: string = 'submit'): Promise<string | null> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const siteKey = process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY;
|
const siteKey = process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY;
|
||||||
if (typeof window === 'undefined' || !siteKey || !(window as any).grecaptcha) {
|
if (typeof window === 'undefined' || !siteKey || !window.grecaptcha) {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
(window as any).grecaptcha.ready(() => {
|
window.grecaptcha.ready(() => {
|
||||||
(window as any).grecaptcha
|
window.grecaptcha
|
||||||
.execute(siteKey, { action })
|
.execute(siteKey, { action })
|
||||||
.then((token: string) => resolve(token))
|
.then((token: string) => resolve(token))
|
||||||
.catch(() => resolve(null));
|
.catch(() => resolve(null));
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 232 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 232 KiB |
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable */
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const filePaths = [
|
||||||
|
'components/sections/PostsGrid.tsx',
|
||||||
|
'components/sections/FeaturedPost.tsx',
|
||||||
|
'components/sections/AboutStory.tsx',
|
||||||
|
'components/layout/GlobalSearch.tsx',
|
||||||
|
'components/shop/ProductGallery.tsx',
|
||||||
|
'app/kontakt/page.tsx',
|
||||||
|
'app/realizacje/page.tsx',
|
||||||
|
'app/blog/page.tsx',
|
||||||
|
'app/blog/[slug]/page.tsx',
|
||||||
|
'app/o-nas/page.tsx'
|
||||||
|
];
|
||||||
|
|
||||||
|
filePaths.forEach(filePath => {
|
||||||
|
const absolutePath = path.join(process.cwd(), filePath);
|
||||||
|
if (!fs.existsSync(absolutePath)) return;
|
||||||
|
|
||||||
|
let content = fs.readFileSync(absolutePath, 'utf8');
|
||||||
|
|
||||||
|
// about story
|
||||||
|
content = content.replace(/https:\/\/images\.unsplash\.com\/photo-1621905251189-08b45d6a269e[^"']*/g, '/images/about/about-story.webp');
|
||||||
|
|
||||||
|
// product gallery and search
|
||||||
|
content = content.replace(/https:\/\/images\.unsplash\.com\/photo-1581094794329-c8112a89af12[^"']*/g, '/images/hero/shop-section-image.webp');
|
||||||
|
content = content.replace(/https:\/\/images\.unsplash\.com\/photo-1584622650111-993a426fbf0a[^"']*/g, '/images/hero/shop-section-image.webp');
|
||||||
|
|
||||||
|
// blog post placeholders
|
||||||
|
content = content.replace(/https:\/\/images\.unsplash\.com\/photo-1516321318423-f06f85e504b3[^"']*/g, '/images/hero/hero-section-image.webp');
|
||||||
|
|
||||||
|
// backgrounds
|
||||||
|
content = content.replace(/https:\/\/images\.unsplash\.com\/photo-1516387938699-a93567ec168e[^"']*/g, '/images/hero/hero-section-image.webp');
|
||||||
|
content = content.replace(/https:\/\/images\.unsplash\.com\/photo-1503387762-592deb58ef4e[^"']*/g, '/images/hero/hero-section-image.webp');
|
||||||
|
content = content.replace(/https:\/\/images\.unsplash\.com\/photo-1504307651254-35680f356f12[^"']*/g, '/images/about/about-story.webp');
|
||||||
|
|
||||||
|
fs.writeFileSync(absolutePath, content, 'utf8');
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Replaced unsplash links');
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable */
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
Reference in New Issue
Block a user