fix: ui tweaks including button styles, texts, and missing account link

This commit is contained in:
Radosław Smoliński
2026-06-22 00:45:57 +02:00
parent 25a0734a80
commit 6bdfe18e18
10 changed files with 129 additions and 32 deletions
+7 -12
View File
@@ -4,27 +4,23 @@ import { Header } from "@/components/layout/Header";
import { TrustBar } from "@/components/layout/TrustBar"; import { TrustBar } from "@/components/layout/TrustBar";
import { Footer } from "@/components/layout/Footer"; import { Footer } from "@/components/layout/Footer";
import { CookieBanner } from "@/components/ui/CookieBanner"; import { CookieBanner } from "@/components/ui/CookieBanner";
import { GoogleAnalytics, GoogleTagManager } from "@next/third-parties/google"; import { AnalyticsWrapper } from "@/components/ui/AnalyticsWrapper";
import "./globals.css"; import "./globals.css";
const fontSans = Inter({ const fontSans = Inter({
subsets: ["latin"],
variable: "--font-sans", variable: "--font-sans",
subsets: ["latin", "latin-ext"],
}); });
const fontHeading = Poppins({ const fontHeading = Poppins({
variable: "--font-heading", subsets: ["latin"],
weight: ["400", "500", "600", "700", "800"], weight: ["400", "500", "600", "700", "800"],
subsets: ["latin", "latin-ext"], variable: "--font-heading",
}); });
export const metadata: Metadata = { export const metadata: Metadata = {
title: { title: "ThermCool | Klimatyzacja i Pompy Ciepła",
default: "Klimatyzacja, pompy ciepła i usługi domowe", description: "Zapewniamy profesjonalny montaż klimatyzacji, pomp ciepła oraz kompleksowe usługi remontowo-wykończeniowe. Sprawdź naszą ofertę!",
template: "%s | HVAC & Usługi Domowe",
},
description: "Montaż klimatyzacji, pompy ciepła, remonty i hydraulika. Dobór, montaż i serwis. Darmowa konsultacja.",
metadataBase: new URL("http://localhost:3000"),
}; };
export default function RootLayout({ export default function RootLayout({
@@ -37,14 +33,13 @@ export default function RootLayout({
lang="pl" lang="pl"
className={`${fontSans.variable} ${fontHeading.variable} h-full antialiased`} className={`${fontSans.variable} ${fontHeading.variable} h-full antialiased`}
> >
<GoogleTagManager gtmId="GTM-MBMRCD2N" />
<body className="min-h-full flex flex-col bg-background text-foreground"> <body className="min-h-full flex flex-col bg-background text-foreground">
<Header /> <Header />
<main className="flex-grow">{children}</main> <main className="flex-grow">{children}</main>
<TrustBar /> <TrustBar />
<Footer /> <Footer />
<CookieBanner /> <CookieBanner />
<GoogleAnalytics gaId="G-1NGTSR4TWD" /> <AnalyticsWrapper />
</body> </body>
</html> </html>
); );
+1 -2
View File
@@ -176,11 +176,10 @@ export function ContactForm() {
{errors.consent && <p className="text-red-500 text-xs font-medium">{errors.consent.message}</p>} {errors.consent && <p className="text-red-500 text-xs font-medium">{errors.consent.message}</p>}
</div> </div>
{/* Button */}
<button <button
type="submit" type="submit"
disabled={isSubmitting} disabled={isSubmitting}
className="w-full bg-cta hover:bg-cta-hover text-white font-bold py-4 rounded-xl transition-colors flex items-center justify-center gap-2 disabled:opacity-70 disabled:cursor-not-allowed shadow-md" className="btn btn-primary w-full text-white font-bold py-4 h-auto rounded-xl flex items-center justify-center gap-2 shadow-md"
> >
{isSubmitting ? ( {isSubmitting ? (
<> <>
+9 -5
View File
@@ -1,6 +1,7 @@
import Link from "next/link"; import Link from "next/link";
import { Phone, Mail, MapPin, Clock, Facebook, Instagram, ShieldCheck } from "lucide-react"; import { Phone, Mail, MapPin, Clock, Facebook, Instagram, ShieldCheck } from "lucide-react";
import { companyDetails } from "@/content/company"; import { companyDetails } from "@/content/company";
import { CookieSettingsTrigger } from "@/components/ui/CookieSettingsTrigger";
export function Footer() { export function Footer() {
const currentYear = new Date().getFullYear(); const currentYear = new Date().getFullYear();
@@ -136,10 +137,10 @@ export function Footer() {
</ul> </ul>
</div> </div>
{/* Column 4: Account */} {/* Column 4: Obsługa Klienta */}
<div> <div>
<h3 className="text-base font-semibold font-heading text-white mb-6 uppercase tracking-wider"> <h3 className="text-base font-semibold font-heading text-white mb-6 uppercase tracking-wider">
Twoje Konto Obsługa Klienta
</h3> </h3>
<ul className="flex flex-col gap-3 text-sm text-slate-300"> <ul className="flex flex-col gap-3 text-sm text-slate-300">
<li> <li>
@@ -165,9 +166,12 @@ export function Footer() {
{/* Bottom Bar */} {/* Bottom Bar */}
<div className="border-t border-slate-800 bg-slate-950/40 py-6"> <div className="border-t border-slate-800 bg-slate-950/40 py-6">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col sm:flex-row justify-between items-center gap-4"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col sm:flex-row justify-between items-center gap-4">
<p className="text-xs text-slate-400 text-center sm:text-left"> <div className="flex flex-col gap-2">
© {currentYear} {companyDetails.name}. Wszelkie prawa zastrzeżone. Projekt strony i wdrożenie ThermCool. <p className="text-xs text-slate-400 text-center sm:text-left">
</p> © {currentYear} {companyDetails.name}. Wszelkie prawa zastrzeżone. Projekt strony i wdrożenie ThermCool.
</p>
<CookieSettingsTrigger />
</div>
<div className="flex items-center gap-6"> <div className="flex items-center gap-6">
{/* Social Icons */} {/* Social Icons */}
-8
View File
@@ -140,14 +140,6 @@ export function Header() {
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<GlobalSearch /> <GlobalSearch />
<Link
href="/konto"
className="p-2 text-primary hover:text-accent hover:bg-background rounded-lg transition-colors outline-none"
aria-label="Konto użytkownika"
>
<User className="size-5" />
</Link>
<Link <Link
href="/koszyk" href="/koszyk"
className="p-2 text-primary hover:text-accent hover:bg-background rounded-lg transition-colors relative outline-none" className="p-2 text-primary hover:text-accent hover:bg-background rounded-lg transition-colors relative outline-none"
+2 -2
View File
@@ -70,7 +70,7 @@ export function HeroSection({
<ul className="mt-6 grid grid-cols-2 gap-3 text-white/90 font-medium"> <ul className="mt-6 grid grid-cols-2 gap-3 text-white/90 font-medium">
{features.map((feature, idx) => ( {features.map((feature, idx) => (
<li key={idx} className="flex items-center gap-2"> <li key={idx} className="flex items-center gap-2">
<Star className="size-4 text-accent fill-accent" /> <Star className="size-4 text-yellow-400 fill-yellow-400" />
{feature} {feature}
</li> </li>
))} ))}
@@ -98,7 +98,7 @@ export function HeroSection({
{/* Trust Bar (Hero specific) */} {/* Trust Bar (Hero specific) */}
<div className="mt-8 flex items-center gap-3 text-sm font-medium text-white/80"> <div className="mt-8 flex items-center gap-3 text-sm font-medium text-white/80">
<div className="flex items-center text-cta"> <div className="flex items-center text-yellow-400">
<Star className="size-4 fill-current" /> <Star className="size-4 fill-current" />
<Star className="size-4 fill-current" /> <Star className="size-4 fill-current" />
<Star className="size-4 fill-current" /> <Star className="size-4 fill-current" />
+2 -2
View File
@@ -25,9 +25,9 @@ export function Testimonials() {
<div className="relative z-10 flex flex-col gap-4"> <div className="relative z-10 flex flex-col gap-4">
{/* Stars */} {/* Stars */}
<div className="flex gap-1 text-cta"> <div className="flex gap-1 mb-4">
{Array.from({ length: test.rating }).map((_, iIdx) => ( {Array.from({ length: test.rating }).map((_, iIdx) => (
<Star key={iIdx} className="size-4 fill-cta text-cta" /> <Star key={iIdx} className="size-4 fill-yellow-400 text-yellow-400" />
))} ))}
</div> </div>
+26
View File
@@ -0,0 +1,26 @@
"use client";
import { useEffect } from "react";
import { useConsentStore } from "@/lib/consent-store";
import { GoogleAnalytics, GoogleTagManager } from "@next/third-parties/google";
export function AnalyticsWrapper() {
const { analytics, marketing, isInitialized, initialize } = useConsentStore();
useEffect(() => {
initialize();
}, [initialize]);
if (!isInitialized) return null;
return (
<>
{/* Ładujemy GTM tylko jeśli zgoda na marketing ATAK LUB analitykę,
w zależności od rygorystyczności, zazwyczaj GTM potrzebuje analityki/marketingu */}
{(analytics || marketing) && <GoogleTagManager gtmId="GTM-MBMRCD2N" />}
{/* Ładujemy GA4 tylko jeśli użytkownik zgodził się na analitykę */}
{analytics && <GoogleAnalytics gaId="G-1NGTSR4TWD" />}
</>
);
}
+16
View File
@@ -0,0 +1,16 @@
"use client";
import { useConsentStore } from "@/lib/consent-store";
export function CookieSettingsTrigger() {
const openBanner = useConsentStore((state) => state.openBanner);
return (
<button
onClick={openBanner}
className="text-slate-400 hover:text-accent transition-colors text-xs text-center sm:text-left underline underline-offset-2"
>
Zarządzaj plikami cookies
</button>
);
}
+1 -1
View File
@@ -126,7 +126,7 @@ export const homeProcessSteps: ProcessStep[] = [
{ {
number: 3, number: 3,
title: "Terminowa realizacja", title: "Terminowa realizacja",
description: "Wchodzimy na budowę w umówionym terminie. Dbamy o porządek i przestrzeganie norm budowlanych.", description: "Rozpoczynamy prace u klienta w umówionym terminie. Zabezpieczamy przestrzeń, dbamy o porządek i najwyższą jakość wykonania.",
}, },
{ {
number: 4, number: 4,
+65
View File
@@ -0,0 +1,65 @@
import { create } from 'zustand';
interface ConsentState {
isInitialized: boolean;
isOpen: boolean;
analytics: boolean;
marketing: boolean;
initialize: () => void;
openBanner: () => void;
closeBanner: () => void;
setConsent: (analytics: boolean, marketing: boolean) => void;
acceptAll: () => void;
rejectOptional: () => void;
}
const CONSENT_KEY = 'airproject_cookie_consent_v2';
export const useConsentStore = create<ConsentState>((set) => ({
isInitialized: false,
isOpen: false,
analytics: false,
marketing: false,
initialize: () => {
if (typeof window !== 'undefined') {
const stored = localStorage.getItem(CONSENT_KEY);
if (stored) {
try {
const parsed = JSON.parse(stored);
set({
isInitialized: true,
isOpen: false,
analytics: !!parsed.analytics,
marketing: !!parsed.marketing,
});
} catch (e) {
// If parsing fails, show banner
set({ isInitialized: true, isOpen: true });
}
} else {
// No consent found, show banner
set({ isInitialized: true, isOpen: true });
}
}
},
openBanner: () => set({ isOpen: true }),
closeBanner: () => set({ isOpen: false }),
setConsent: (analytics, marketing) => {
localStorage.setItem(CONSENT_KEY, JSON.stringify({ essential: true, analytics, marketing }));
set({ analytics, marketing, isOpen: false });
},
acceptAll: () => {
localStorage.setItem(CONSENT_KEY, JSON.stringify({ essential: true, analytics: true, marketing: true }));
set({ analytics: true, marketing: true, isOpen: false });
},
rejectOptional: () => {
localStorage.setItem(CONSENT_KEY, JSON.stringify({ essential: true, analytics: false, marketing: false }));
set({ analytics: false, marketing: false, isOpen: false });
}
}));