fix: ui tweaks including button styles, texts, and missing account link
This commit is contained in:
+7
-12
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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 ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -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 */}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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" />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
}
|
||||||
|
}));
|
||||||
Reference in New Issue
Block a user