style: improve cookie banner UI contrast and add reject all button

This commit is contained in:
Radosław Smoliński
2026-06-22 00:11:04 +02:00
parent 31be20da40
commit 25a0734a80
+176 -58
View File
@@ -1,75 +1,193 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState } from "react";
import Link from "next/link"; import Link from "next/link";
import { Cookie, X } from "lucide-react"; import { Cookie, X, Check, Settings2, Info } from "lucide-react";
import { useConsentStore } from "@/lib/consent-store";
export function CookieBanner() { export function CookieBanner() {
const [showBanner, setShowBanner] = useState(false); const {
isInitialized,
isOpen,
analytics,
marketing,
setConsent,
acceptAll,
rejectOptional,
closeBanner
} = useConsentStore();
useEffect(() => { const [showSettings, setShowSettings] = useState(false);
// Check if the user has already accepted cookies const [localAnalytics, setLocalAnalytics] = useState(true);
const consent = localStorage.getItem("airproject_cookie_consent"); const [localMarketing, setLocalMarketing] = useState(true);
if (!consent) {
setShowBanner(true);
}
}, []);
const acceptCookies = () => { if (!isInitialized || !isOpen) return null;
localStorage.setItem("airproject_cookie_consent", "true");
setShowBanner(false); const handleSaveSettings = () => {
setConsent(localAnalytics, localMarketing);
setShowSettings(false);
}; };
if (!showBanner) return null; const handleOpenSettings = () => {
const hasConsent = localStorage.getItem('airproject_cookie_consent_v2');
setLocalAnalytics(hasConsent ? analytics : true);
setLocalMarketing(hasConsent ? marketing : true);
setShowSettings(true);
};
return ( return (
<div className="fixed bottom-0 left-0 right-0 z-50 p-4 md:p-6 pb-safe"> <div className="fixed bottom-0 left-0 right-0 z-50 p-4 md:p-6 pb-safe flex flex-col items-center pointer-events-none">
<div className="max-w-4xl mx-auto bg-slate-900 border border-slate-700 shadow-2xl rounded-2xl p-4 md:p-6 flex flex-col md:flex-row items-start md:items-center justify-between gap-4 md:gap-6 animate-in slide-in-from-bottom-10 fade-in duration-500"> <div className="relative pointer-events-auto max-w-5xl w-full bg-slate-900 border border-slate-700 shadow-2xl rounded-2xl overflow-hidden animate-in slide-in-from-bottom-10 fade-in duration-500">
<div className="flex items-start gap-4">
<div className="bg-primary/20 p-2 md:p-3 rounded-full shrink-0 mt-1 md:mt-0 text-primary"> {/* WIDOK GŁÓWNY BANERA */}
<Cookie className="w-5 h-5 md:w-6 md:h-6" /> {!showSettings && (
<div className="p-5 md:p-8">
<div className="flex flex-col md:flex-row items-start gap-6">
<div className="bg-slate-800 p-3 rounded-full shrink-0 text-white hidden md:block border border-slate-700 shadow-sm">
<Cookie className="w-8 h-8" />
</div>
<div className="flex-grow">
<h3 className="text-white font-bold text-lg md:text-xl mb-2 flex items-center gap-2">
<Cookie className="w-5 h-5 md:hidden text-white" />
Szanujemy Twoją prywatność
</h3>
<p className="text-slate-300 text-sm md:text-base leading-relaxed mb-4">
Używamy plików cookies i podobnych technologii, by zapewnić prawidłowe działanie serwisu, personalizować treści oraz analizować ruch.
Możesz zaakceptować wszystkie ciasteczka, odrzucić opcjonalne lub dostosować zgody w ustawieniach.
Szczegóły znajdziesz w naszej{" "}
<Link href="/polityka-prywatnosci" className="text-accent hover:text-white underline underline-offset-2">
Polityce Prywatności
</Link>.
</p>
<div className="flex flex-col sm:flex-row flex-wrap items-center gap-3 mt-6">
<button
onClick={acceptAll}
className="btn btn-primary text-white flex-grow sm:flex-grow-0 border-none shadow-md"
>
<Check className="w-4 h-4 mr-1 text-white" /> Akceptuj wszystkie
</button>
<button
onClick={rejectOptional}
className="btn btn-outline border-slate-600 text-slate-200 hover:bg-slate-800 hover:text-white hover:border-slate-500 flex-grow sm:flex-grow-0"
>
<X className="w-4 h-4 mr-1 text-slate-300" /> Tylko niezbędne
</button>
<div className="flex-grow hidden sm:block"></div>
<button
onClick={handleOpenSettings}
className="btn btn-ghost text-slate-300 hover:text-white hover:bg-slate-800 flex-grow sm:flex-grow-0 group"
>
<Settings2 className="w-4 h-4 mr-1 text-slate-300 group-hover:text-white" /> Ustawienia
</button>
</div>
</div>
</div>
{/* Przycisk zamknięcia wymusza "Tylko niezbędne" dla bezpieczeństwa */}
<button
onClick={rejectOptional}
className="absolute top-4 right-4 text-slate-500 hover:text-white p-1"
aria-label="Zamknij"
>
<X className="w-5 h-5" />
</button>
</div> </div>
<div> )}
<h3 className="text-white font-semibold text-sm md:text-base mb-1">
Dbamy o Twoją prywatność {/* WIDOK ZARZĄDZANIA (MODAL) */}
</h3> {showSettings && (
<p className="text-slate-400 text-xs md:text-sm leading-relaxed max-w-2xl"> <div className="flex flex-col h-full max-h-[85vh]">
Nasz serwis używa plików cookies (ciasteczek) w celach statystycznych (m.in. Google Analytics, Google Tag Manager) oraz prawidłowego działania strony. <div className="p-5 border-b border-slate-700 flex justify-between items-center bg-slate-800/50">
Możesz określić warunki przechowywania cookies w swojej przeglądarce. Dowiedz się więcej w naszej{" "} <h3 className="text-white font-bold text-lg flex items-center gap-2">
<Link <Settings2 className="w-5 h-5 text-slate-300" />
href="/polityka-prywatnosci" Ustawienia plików cookies
className="text-primary hover:text-white transition-colors underline underline-offset-2" </h3>
<button
onClick={() => setShowSettings(false)}
className="text-slate-400 hover:text-white"
> >
Polityce Prywatności <X className="w-5 h-5" />
</Link>. </button>
</p> </div>
<div className="p-5 overflow-y-auto space-y-6">
{/* Niezbędne */}
<div className="flex gap-4 items-start bg-slate-800/30 p-4 rounded-xl border border-slate-700/50">
<div className="pt-1">
<input type="checkbox" className="toggle toggle-primary" checked disabled />
</div>
<div>
<h4 className="text-white font-semibold mb-1 flex items-center gap-2">
Niezbędne <span className="text-[10px] uppercase bg-slate-700 px-2 py-0.5 rounded text-slate-300">Zawsze aktywne</span>
</h4>
<p className="text-slate-400 text-sm">
Te pliki cookies są wymagane do podstawowego działania strony (np. nawigacja, sesja koszyka, zapisywanie tych preferencji). Nie można ich wyłączyć.
</p>
</div>
</div>
{/* Analityczne */}
<div className="flex gap-4 items-start p-2">
<div className="pt-1">
<input
type="checkbox"
className="toggle toggle-primary bg-slate-600 border-slate-500 [--tglbg:theme(colors.slate.200)] hover:bg-slate-500"
checked={localAnalytics}
onChange={(e) => setLocalAnalytics(e.target.checked)}
/>
</div>
<div>
<h4 className="text-white font-semibold mb-1">Analityczne (Google Analytics 4)</h4>
<p className="text-slate-400 text-sm">
Pozwalają nam badać statystyki ruchu na stronie, rozumieć jak Użytkownicy z niej korzystają i na tej podstawie optymalizować jej działanie.
</p>
</div>
</div>
{/* Marketingowe */}
<div className="flex gap-4 items-start p-2">
<div className="pt-1">
<input
type="checkbox"
className="toggle toggle-primary bg-slate-600 border-slate-500 [--tglbg:theme(colors.slate.200)] hover:bg-slate-500"
checked={localMarketing}
onChange={(e) => setLocalMarketing(e.target.checked)}
/>
</div>
<div>
<h4 className="text-white font-semibold mb-1">Marketingowe (Google Tag Manager)</h4>
<p className="text-slate-400 text-sm">
Pozwalają nam dopasowywać treści reklamowe do Twoich zainteresowań w sieci (np. w systemach Google Ads, Facebook Ads) oraz mierzyć skuteczność kampanii.
</p>
</div>
</div>
</div>
<div className="p-5 border-t border-slate-700 bg-slate-800/50 flex flex-col-reverse sm:flex-row justify-end gap-3 items-center">
<button
onClick={rejectOptional}
className="btn btn-ghost text-slate-400 hover:text-white w-full sm:w-auto"
>
Odrzuć wszystkie
</button>
<button
onClick={acceptAll}
className="btn btn-outline border-slate-600 text-white hover:bg-slate-700 w-full sm:w-auto"
>
Akceptuj wszystkie
</button>
<button
onClick={handleSaveSettings}
className="btn btn-primary text-white w-full sm:w-auto"
>
Zapisz ustawienia
</button>
</div>
</div> </div>
</div> )}
<div className="flex items-center gap-3 w-full md:w-auto shrink-0 justify-end md:justify-start">
<button
onClick={() => setShowBanner(false)}
className="md:hidden btn btn-ghost btn-circle text-slate-400 hover:text-white shrink-0"
>
<X className="w-5 h-5" />
</button>
<button
onClick={acceptCookies}
className="btn btn-primary text-white w-full md:w-auto shadow-md shadow-primary/20"
>
Rozumiem i Akceptuję
</button>
</div>
{/* Zamknięcie na desktop */}
<button
onClick={() => setShowBanner(false)}
className="hidden md:flex absolute top-4 right-4 text-slate-500 hover:text-white transition-colors p-1"
aria-label="Zamknij"
>
<X className="w-4 h-4" />
</button>
</div> </div>
</div> </div>
); );