From e98a903e286d726aab8f6693e674bd9f44c6b6ea Mon Sep 17 00:00:00 2001 From: MichalC <103170279+wSBMichalee@users.noreply.github.com> Date: Tue, 25 Aug 2026 23:51:18 +0200 Subject: [PATCH] changes --- app/blog/page.tsx | 15 +-- app/klimatyzacja/page.tsx | 17 +-- app/kontakt/page.tsx | 11 +- app/montaz-klimatyzacji/page.tsx | 10 +- app/o-nas/page.tsx | 13 +-- app/page.tsx | 30 +++-- app/realizacje/page.tsx | 11 +- app/sklep/page.tsx | 13 +-- components/forms/ContactForm.tsx | 27 +++-- components/kalkulator/KalkulatorClient.tsx | 25 +++-- components/layout/PageHeader.tsx | 51 +++++++++ components/sections/CalculatorTeaser.tsx | 60 ++++++++++ components/sections/CtaBanner.tsx | 20 +--- components/sections/HeroSection.tsx | 122 ++++++++++++--------- components/sections/ServicesGrid.tsx | 82 +++++++++++--- components/shop/ProductCard.tsx | 27 +++-- components/shop/ProductPageClient.tsx | 55 ++++++---- components/ui/Button.tsx | 19 ++-- remove_pump.js | 33 ------ 19 files changed, 381 insertions(+), 260 deletions(-) create mode 100644 components/layout/PageHeader.tsx create mode 100644 components/sections/CalculatorTeaser.tsx delete mode 100644 remove_pump.js diff --git a/app/blog/page.tsx b/app/blog/page.tsx index ea0d324..2d7796a 100644 --- a/app/blog/page.tsx +++ b/app/blog/page.tsx @@ -1,5 +1,5 @@ import { Metadata } from 'next'; -import { HeroSection } from '@/components/sections/HeroSection'; +import { PageHeader } from '@/components/layout/PageHeader'; import { PostsGrid } from '@/components/sections/PostsGrid'; import { CtaBanner } from '@/components/sections/CtaBanner'; import { getPosts, getFeaturedPost } from '@/content/posts'; @@ -23,15 +23,10 @@ export default function BlogPage() { return (
-
diff --git a/app/klimatyzacja/page.tsx b/app/klimatyzacja/page.tsx index e366564..fc6e46c 100644 --- a/app/klimatyzacja/page.tsx +++ b/app/klimatyzacja/page.tsx @@ -1,6 +1,6 @@ import { Metadata } from 'next'; import { JsonLd } from '@/components/ui/JsonLd'; -import { HeroSection } from '@/components/sections/HeroSection'; +import { PageHeader } from '@/components/layout/PageHeader'; import { InfoHighlights } from '@/components/sections/InfoHighlights'; import { AcTypesSection } from '@/components/sections/AcTypesSection'; import { ProcessSteps } from '@/components/sections/ProcessSteps'; @@ -74,21 +74,10 @@ export default function KlimatyzacjaPage() { <>
- - diff --git a/app/montaz-klimatyzacji/page.tsx b/app/montaz-klimatyzacji/page.tsx index 65b113a..cebc6c8 100644 --- a/app/montaz-klimatyzacji/page.tsx +++ b/app/montaz-klimatyzacji/page.tsx @@ -204,12 +204,10 @@ export default function MontazKlimatyzacjiPage() { ))} -
- - - Zapytaj o bezpłatną wycenę - - +
+ + Darmowa wycena +
diff --git a/app/o-nas/page.tsx b/app/o-nas/page.tsx index 8bc5036..41649b7 100644 --- a/app/o-nas/page.tsx +++ b/app/o-nas/page.tsx @@ -1,5 +1,5 @@ import { Metadata } from 'next'; -import { HeroSection } from '@/components/sections/HeroSection'; +import { PageHeader } from '@/components/layout/PageHeader'; import { AboutStory } from '@/components/sections/AboutStory'; import { ValuesGrid } from '@/components/sections/ValuesGrid'; import { CtaBanner } from '@/components/sections/CtaBanner'; @@ -16,15 +16,10 @@ export const metadata: Metadata = { export default function ONasPage() { return (
- diff --git a/app/page.tsx b/app/page.tsx index c5236f4..7017571 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,6 +1,6 @@ import { HeroSection } from "@/components/sections/HeroSection"; +import { CalculatorTeaser } from "@/components/sections/CalculatorTeaser"; import { ServicesGrid } from "@/components/sections/ServicesGrid"; -import { WhyUs } from "@/components/sections/WhyUs"; import { FeaturedProducts } from "@/components/sections/FeaturedProducts"; import { BlogPreview } from "@/components/sections/BlogPreview"; import { CtaBanner } from "@/components/sections/CtaBanner"; @@ -130,16 +130,10 @@ export default function Home() { - + + {/* BrandsBar will go here in Group B */} - - {/* ProjectsSection will go here in Group B */} - {/* (Ukryte na czas zbierania autentycznych opinii) */} - - - -
@@ -241,14 +235,12 @@ export default function Home() {

Nasi certyfikowani technicy są dostępni od poniedziałku do soboty. Zarezerwuj termin już dziś, aby uniknąć kolejek w sezonie.

- - Umów serwis + + Umów darmową wycenę + + + Zobacz pełny cennik - - -
@@ -257,6 +249,12 @@ export default function Home() {
+ + + {/* ProjectsSection will go here in Group B */} + {/* (Ukryte na czas zbierania autentycznych opinii) */} + + ); diff --git a/app/realizacje/page.tsx b/app/realizacje/page.tsx index d2232f2..438e1a0 100644 --- a/app/realizacje/page.tsx +++ b/app/realizacje/page.tsx @@ -1,5 +1,5 @@ import { Metadata } from 'next'; -import { HeroSection } from '@/components/sections/HeroSection'; +import { PageHeader } from '@/components/layout/PageHeader'; import { ProjectsGrid } from '@/components/sections/ProjectsGrid'; import { StatsBar } from '@/components/sections/StatsBar'; import { CtaBanner } from '@/components/sections/CtaBanner'; @@ -19,15 +19,10 @@ export const metadata: Metadata = { export default function RealizacjePage() { return (
- diff --git a/app/sklep/page.tsx b/app/sklep/page.tsx index d339d9f..1b6a112 100644 --- a/app/sklep/page.tsx +++ b/app/sklep/page.tsx @@ -1,7 +1,7 @@ import { Metadata } from 'next'; import { getProducts, getBrands } from '@/content/products'; import { ProductsGrid } from '@/components/shop/ProductsGrid'; -import { HeroSection } from '@/components/sections/HeroSection'; +import { PageHeader } from '@/components/layout/PageHeader'; export const metadata: Metadata = { title: 'Sklep z klimatyzatorami', @@ -17,15 +17,10 @@ export default function ShopPage() { return (
-
diff --git a/components/forms/ContactForm.tsx b/components/forms/ContactForm.tsx index b40e306..81e2d15 100644 --- a/components/forms/ContactForm.tsx +++ b/components/forms/ContactForm.tsx @@ -4,9 +4,10 @@ import React, { useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; -import { Loader2, CheckCircle2, AlertCircle, Check } from 'lucide-react'; +import { Loader2, CheckCircle2, AlertCircle, Check, Send } from 'lucide-react'; import { submitContactForm } from '@/app/kontakt/actions'; import { getRecaptchaToken } from '@/lib/getRecaptchaToken'; +import { Button } from "@/components/ui/Button"; const contactFormSchema = z.object({ name: z.string().min(2, "Imię musi mieć min. 2 znaki"), @@ -88,12 +89,13 @@ export function ContactForm() {

Wiadomość wysłana!

{serverState.message}

- + ); } @@ -282,20 +284,21 @@ export function ContactForm() { - + ); } diff --git a/components/kalkulator/KalkulatorClient.tsx b/components/kalkulator/KalkulatorClient.tsx index b640ccd..1fe5a2a 100644 --- a/components/kalkulator/KalkulatorClient.tsx +++ b/components/kalkulator/KalkulatorClient.tsx @@ -6,6 +6,7 @@ import Link from 'next/link'; import { ArrowRight, ArrowLeft, Check, Home, Zap, ShoppingCart, Phone } from 'lucide-react'; import { Product } from '@/content/products'; import { companyDetails } from '@/content/company'; +import { Button, buttonClasses } from '@/components/ui/Button'; interface Props { @@ -574,9 +575,9 @@ export function KalkulatorClient({ products }: Props) {

- + @@ -589,19 +590,25 @@ export function KalkulatorClient({ products }: Props) { )}
{step > 0 ? ( - + ) :
} {step < STEPS.length - 1 ? ( - + ) : ( - + Przejdź do sklepu diff --git a/components/layout/PageHeader.tsx b/components/layout/PageHeader.tsx new file mode 100644 index 0000000..32fae82 --- /dev/null +++ b/components/layout/PageHeader.tsx @@ -0,0 +1,51 @@ +import Link from "next/link"; +import { ChevronRight } from "lucide-react"; +import { Outfit } from "next/font/google"; + +const outfit = Outfit({ subsets: ["latin"], weight: ["500", "700", "800"] }); + +export interface PageHeaderProps { + title: React.ReactNode; + subtitle?: string; + breadcrumbs?: { label: string; href: string }[]; +} + +export function PageHeader({ title, subtitle, breadcrumbs }: PageHeaderProps) { + return ( +
+
+ + {/* Breadcrumbs */} + {breadcrumbs && breadcrumbs.length > 0 && ( + + )} + +
+

+ {title} +

+ {subtitle && ( +

+ {subtitle} +

+ )} +
+ +
+
+ ); +} diff --git a/components/sections/CalculatorTeaser.tsx b/components/sections/CalculatorTeaser.tsx new file mode 100644 index 0000000..0b1e82e --- /dev/null +++ b/components/sections/CalculatorTeaser.tsx @@ -0,0 +1,60 @@ +import Link from "next/link"; +import { ArrowRight, Home } from "lucide-react"; +import { buttonClasses } from "@/components/ui/Button"; + +export function CalculatorTeaser() { + return ( +
+
+
+ +
+

+ Nie wiesz, jakiej mocy potrzebujesz? +

+

+ Skorzystaj z naszego inteligentnego kalkulatora. Wystarczy podać metraż pomieszczenia, a my wskażemy optymalną moc klimatyzatora i przybliżony koszt instalacji. +

+ +
+ + Uruchom kalkulator + + +
+
+ +
+
+
+
+
+

Powierzchnia

+

35 m²

+
+ +
+ +
+
+
+
+
+ 10 m² + 100 m² +
+
+ +
+

Sugerowana moc

+

3.5 kW

+
+
+
+
+ +
+
+
+ ); +} diff --git a/components/sections/CtaBanner.tsx b/components/sections/CtaBanner.tsx index 175bab4..9b21957 100644 --- a/components/sections/CtaBanner.tsx +++ b/components/sections/CtaBanner.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { Phone, ArrowRight } from "lucide-react"; import { companyDetails } from "@/content/company"; import { cn } from "@/lib/utils"; +import { buttonClasses } from "@/components/ui/Button"; interface CtaBannerProps { title?: string; @@ -56,26 +57,15 @@ export function CtaBanner({

diff --git a/components/sections/HeroSection.tsx b/components/sections/HeroSection.tsx index fecaf70..3bac1c2 100644 --- a/components/sections/HeroSection.tsx +++ b/components/sections/HeroSection.tsx @@ -1,7 +1,8 @@ import Link from "next/link"; import Image from "next/image"; -import { ArrowRight, Star } from "lucide-react"; -import { buttonClasses } from "@/components/ui/Button"; +import { Outfit } from "next/font/google"; + +const outfit = Outfit({ subsets: ["latin"], weight: ["500", "700", "800"] }); export interface HeroSectionProps { title?: React.ReactNode; @@ -10,73 +11,88 @@ export interface HeroSectionProps { ctaLink?: string; secondaryCtaText?: string; secondaryCtaLink?: string; - backgroundImage?: string; - features?: string[]; } export function HeroSection({ title = ( <> - Klimatyzacja
+ Klimatyzacja
we Wrocławiu ), - subtitle = "Montaż, serwis i naprawa — przyjeżdżamy do Ciebie.", + subtitle = "Montaż, serwis i naprawa — przyjeżdżamy do Ciebie. Zapewniamy najwyższą jakość instalacji oraz pełne wsparcie pogwarancyjne.", ctaText = "Darmowa wycena", ctaLink = "/kontakt", secondaryCtaText = "Zobacz ofertę", - secondaryCtaLink = "/sklep", - backgroundImage = "/images/hero/home_page_hero.webp", - features + secondaryCtaLink = "/sklep" }: HeroSectionProps = {}) { return ( -
- {/* TODO ZDJĘCIE HERO: realne zdjęcie firmy/montażu, format poziomy min. 1920×1080, plik public/hero.jpg */} - Montaż klimatyzacji i pomp ciepła -
- -
-
-

- {title} -

-

- {subtitle} -

- {features && features.length > 0 && ( -
    - {features.map((feature, idx) => ( -
  • - - {feature} -
  • - ))} -
+
+ + {/* Lewa kolumna: Typografia i CTA */} +
+ +

+ {title} +

+ +

+ {subtitle} +

+ +
+ {ctaText && ctaLink && ( + + {ctaText} + + )} + {secondaryCtaText && secondaryCtaLink && ( + + {secondaryCtaText} + )} -
- {ctaText && ctaLink && ( - - {ctaText} - - - )} - {secondaryCtaText && secondaryCtaLink && ( - - {secondaryCtaText} - - )} -
- -
+ + {/* Trust Signals: Minimalistyczne dane typograficzne */} +
+
+

15+

+

Lat doświadczenia

+
+
+
+

5

+

Lat gwarancji

+
+
+
+

100%

+

Pokrycia w regionie

+
+
+
+ + {/* Prawa kolumna: Jedno wielkie, surowe zdjęcie */} +
+ Profesjonalny montaż klimatyzacji + {/* Subtelny overlay dla kontrastu jeśli zajdzie potrzeba (opcjonalnie) */} +
+
+
); } diff --git a/components/sections/ServicesGrid.tsx b/components/sections/ServicesGrid.tsx index 8f096ff..fb503db 100644 --- a/components/sections/ServicesGrid.tsx +++ b/components/sections/ServicesGrid.tsx @@ -1,6 +1,12 @@ +"use client"; + +import { useState } from "react"; import { SectionHeading } from "../common/SectionHeading"; -import { IconCard } from "../common/IconCard"; import { services as defaultServices } from "@/content/services"; +import Link from "next/link"; +import Image from "next/image"; +import { buttonClasses } from "@/components/ui/Button"; +import { ArrowRight } from "lucide-react"; interface ServicesGridProps { services?: { @@ -16,12 +22,14 @@ interface ServicesGridProps { export function ServicesGrid({ services = defaultServices, - title = "Kompleksowe usługi dla Twojego domu", - subtitle = "Zapewniamy profesjonalne wsparcie w zakresie instalacji sanitarnych, grzewczych, chłodniczych oraz prac wykończeniowych.", - eyebrow = "Pełna Oferta" + title = "Rozwiązania dla Twojego domu i biznesu", + subtitle = "Wybierz kategorię usług, aby poznać szczegóły. Oferujemy kompleksowe doradztwo, montaż oraz serwis.", + eyebrow = "Nasze Usługi" }: ServicesGridProps) { + const [activeTab, setActiveTab] = useState(0); + return ( -
+
-
- {services.map((service, idx) => ( - - ))} +
+ {/* Zakładki */} +
+ {services.map((service, idx) => ( + + ))} +
+ + {/* Zawartość aktywnej zakładki */} +
+ {services.map((service, idx) => { + if (idx !== activeTab) return null; + + return ( +
+ + {/* Zastąpiono sztuczną ikonę w kółku realnym zdjęciem */} +
+ {service.title} +
+ +
+

+ {service.title} +

+

+ {service.description} +

+ + {service.href && ( + + Więcej szczegółów + + )} +
+
+ ); + })} +
diff --git a/components/shop/ProductCard.tsx b/components/shop/ProductCard.tsx index a568b88..124e84e 100644 --- a/components/shop/ProductCard.tsx +++ b/components/shop/ProductCard.tsx @@ -6,6 +6,8 @@ import { ShoppingCart, GitCompare, Phone } from 'lucide-react'; import { Product } from '@/content/products'; import { useCartStore } from '@/lib/cart-store'; import { useCompareStore } from '@/lib/compare-store'; +import { formatCurrency } from "@/lib/utils"; +import { Button, buttonClasses } from "@/components/ui/Button"; import { useState } from 'react'; const brandLogos: Record = { @@ -144,11 +146,10 @@ export function ProductCard({ product }: ProductCardProps) {
e.stopPropagation()} > - + Zapytaj o wycenę ) : product.price !== null ? ( @@ -162,18 +163,20 @@ export function ProductCard({ product }: ProductCardProps) { {added ? ( e.stopPropagation()} > Zobacz koszyk ) : ( - + )} ) : ( @@ -182,10 +185,12 @@ export function ProductCard({ product }: ProductCardProps) { Zapytaj o cenę
- e.stopPropagation()}> - + e.stopPropagation()} + > + Zapytaj )} diff --git a/components/shop/ProductPageClient.tsx b/components/shop/ProductPageClient.tsx index bdeea32..09c1941 100644 --- a/components/shop/ProductPageClient.tsx +++ b/components/shop/ProductPageClient.tsx @@ -5,9 +5,12 @@ import Image from 'next/image'; import Link from 'next/link'; import { Phone, ShoppingCart, GitCompare, Check, - Thermometer, Volume2, Zap, Wind, Star, Info, Eye, X, type LucideIcon + Thermometer, Volume2, Zap, Wind, Star, Info, Eye, X, Mail, type LucideIcon } from 'lucide-react'; import { Product } from '@/content/products'; +import { formatCurrency } from "@/lib/utils"; +import { companyDetails } from "@/content/company"; +import { Button, buttonClasses } from "@/components/ui/Button"; import { useCartStore } from '@/lib/cart-store'; import { useCompareStore } from '@/lib/compare-store'; import { getProductVariants } from '@/content/product-variants'; @@ -277,41 +280,47 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa <>
- {currentProduct.price.toLocaleString('pl-PL')} zł + {formatCurrency(currentProduct.price)}

cena urządzenia brutto (23% VAT)

Montaż wyceniamy indywidualnie — zadzwoń lub napisz

-
+
{cartState === 'idle' && ( - + )} {cartState === 'added' && ( - + )} {cartState === 'cart' && ( - - - Zobacz koszyk - - + + Zobacz koszyk + )} - - - Zapytaj o montaż - - + + Zapytaj o montaż +
@@ -483,7 +492,7 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
- +
diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx index 4c731b1..3f5697b 100644 --- a/components/ui/Button.tsx +++ b/components/ui/Button.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { cn } from '@/lib/utils'; -export type ButtonVariant = 'primary' | 'outline' | 'error' | 'outline-light'; +export type ButtonVariant = 'primary' | 'outline' | 'error' | 'outline-light' | 'success'; export type ButtonSize = 'sm' | 'md' | 'lg'; export interface ButtonClassesProps { @@ -11,19 +11,20 @@ export interface ButtonClassesProps { } export function buttonClasses({ variant = 'primary', size = 'md', className }: ButtonClassesProps = {}) { - const baseClasses = 'inline-flex items-center justify-center gap-2 whitespace-nowrap transition-colors'; + const baseClasses = 'inline-flex items-center justify-center gap-2 whitespace-nowrap transition-colors font-bold uppercase tracking-wider rounded-md'; const variantClasses = { - primary: 'btn btn-primary text-white font-bold', - outline: 'btn bg-white/10 text-white border border-white/20 hover:bg-white/20 font-bold', - 'outline-light': 'btn bg-white text-primary border border-border hover:bg-slate-50 font-bold', - error: 'btn btn-error text-white font-bold', + primary: 'bg-primary text-white hover:bg-primary/90 border-2 border-primary', + outline: 'bg-transparent text-primary border-2 border-primary hover:bg-primary/5', + 'outline-light': 'bg-transparent text-white border-2 border-white hover:bg-white/10', + error: 'bg-red-600 text-white hover:bg-red-700 border-2 border-red-600', + success: 'bg-green-600 text-white hover:bg-green-700 border-2 border-green-600', }; const sizeClasses = { - sm: 'btn-sm text-sm', - md: '', // default DaisyUI btn size - lg: 'btn-lg text-lg', + sm: 'px-6 py-2.5 text-xs', + md: 'px-8 py-3.5 text-sm', + lg: 'px-10 py-5 text-base', }; return cn( diff --git a/remove_pump.js b/remove_pump.js deleted file mode 100644 index 342f7f5..0000000 --- a/remove_pump.js +++ /dev/null @@ -1,33 +0,0 @@ -const fs = require('fs'); -let p = 'components/kalkulator/KalkulatorClient.tsx'; -let c = fs.readFileSync(p, 'utf8'); - -c = c.replace(/const STEPS = \['Typ', 'Pomieszczenie', 'Instalacja', 'Budżet', 'Dobór'\] as const;/g, "const STEPS = ['Pomieszczenie', 'Instalacja', 'Budżet', 'Dobór'] as const;"); - -c = c.replace(/const \[deviceType, setDeviceType\] = useSessionState<'klimatyzacja' \| 'pompa'>\('calc_deviceType', 'klimatyzacja'\);\n/g, ""); -c = c.replace(/const \[pumpFormSent, setPumpFormSent\] = useState\(false\);\n/g, ""); -c = c.replace(/const \[pumpForm, setPumpForm\] = useState\(\{ name: '', phone: '', city: '', message: '' \}\);\n/g, ""); - -c = c.replace(/if \(step < 4 \|\| deviceType === 'pompa'\) return \[\];/g, "if (step < 3) return [];"); - -c = c.replace(/step === 4 && deviceType !== 'pompa'/g, "step === 3"); - -// Remove handlePumpSubmit -c = c.replace(/ const handlePumpSubmit = async \(\) => \{[\s\S]*? \};\n/g, ""); - -// Replace step === 0 (Typ urządzenia) entirely -c = c.replace(/ \{\/\* Krok 0: Typ urządzenia \*\/\}[\s\S]*? \{\/\* Krok 1: Pomieszczenie \*\/\}/g, " {/* Krok 0: Pomieszczenie */}"); - -// Shift step indices -c = c.replace(/step === 1 && \(/g, "step === 0 && ("); -c = c.replace(/step === 2 && \(/g, "step === 1 && ("); -c = c.replace(/step === 3 && \(/g, "step === 2 && ("); -c = c.replace(/step === 4 && \(/g, "step === 3 && ("); -c = c.replace(/step === 4 \? 'bg-accent/g, "step === 3 ? 'bg-accent"); -c = c.replace(/\{step === 4 && \(/g, "{step === 3 && ("); - -// Remove deviceType from results useMemo dependencies -c = c.replace(/, deviceType/g, ""); - -fs.writeFileSync(p, c); -console.log("Done");