216 lines
7.2 KiB
TypeScript
216 lines
7.2 KiB
TypeScript
import { useState, useEffect } from "react";
|
|||
|
|
import { Button } from "@/components/ui/button";
|
||
|
|
import { Card, CardContent } from "@/components/ui/card";
|
||
|
|
import { Switch } from "@/components/ui/switch";
|
||
|
|
import { Label } from "@/components/ui/label";
|
||
|
|
import { Cookie, X, Settings, Shield } from "lucide-react";
|
||
|
|
import { Link } from "react-router-dom";
|
||
|
|
import {
|
||
|
|
Dialog,
|
||
|
|
DialogContent,
|
||
|
|
DialogDescription,
|
||
|
|
DialogHeader,
|
||
|
|
DialogTitle,
|
||
|
|
} from "@/components/ui/dialog";
|
||
|
|
|
||
|
|
interface CookiePreferences {
|
||
|
|
necessary: boolean;
|
||
|
|
analytics: boolean;
|
||
|
|
marketing: boolean;
|
||
|
|
}
|
||
|
|
|
||
|
|
const COOKIE_CONSENT_KEY = "cookie-consent";
|
||
|
|
const COOKIE_PREFERENCES_KEY = "cookie-preferences";
|
||
|
|
|
||
|
|
const CookieConsent = () => {
|
||
|
|
const [showBanner, setShowBanner] = useState(false);
|
||
|
|
const [showSettings, setShowSettings] = useState(false);
|
||
|
|
const [preferences, setPreferences] = useState<CookiePreferences>({
|
||
|
|
necessary: true, // Always required
|
||
|
|
analytics: false,
|
||
|
|
marketing: false,
|
||
|
|
});
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const consent = localStorage.getItem(COOKIE_CONSENT_KEY);
|
||
|
|
if (!consent) {
|
||
|
|
// Small delay to avoid flash on page load
|
||
|
|
const timer = setTimeout(() => setShowBanner(true), 1000);
|
||
|
|
return () => clearTimeout(timer);
|
||
|
|
} else {
|
||
|
|
const savedPreferences = localStorage.getItem(COOKIE_PREFERENCES_KEY);
|
||
|
|
if (savedPreferences) {
|
||
|
|
setPreferences(JSON.parse(savedPreferences));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const handleAcceptAll = () => {
|
||
|
|
const allAccepted: CookiePreferences = {
|
||
|
|
necessary: true,
|
||
|
|
analytics: true,
|
||
|
|
marketing: true,
|
||
|
|
};
|
||
|
|
saveConsent(allAccepted);
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleRejectAll = () => {
|
||
|
|
const onlyNecessary: CookiePreferences = {
|
||
|
|
necessary: true,
|
||
|
|
analytics: false,
|
||
|
|
marketing: false,
|
||
|
|
};
|
||
|
|
saveConsent(onlyNecessary);
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleSavePreferences = () => {
|
||
|
|
saveConsent(preferences);
|
||
|
|
setShowSettings(false);
|
||
|
|
};
|
||
|
|
|
||
|
|
const saveConsent = (prefs: CookiePreferences) => {
|
||
|
|
localStorage.setItem(COOKIE_CONSENT_KEY, "true");
|
||
|
|
localStorage.setItem(COOKIE_PREFERENCES_KEY, JSON.stringify(prefs));
|
||
|
|
setPreferences(prefs);
|
||
|
|
setShowBanner(false);
|
||
|
|
|
||
|
|
// Dispatch custom event for analytics scripts to listen to
|
||
|
|
window.dispatchEvent(
|
||
|
|
new CustomEvent("cookie-consent-updated", { detail: prefs })
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
if (!showBanner) return null;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<>
|
||
|
|
{/* Cookie Banner */}
|
||
|
|
<div className="fixed bottom-0 left-0 right-0 z-50 p-4 animate-fade-in">
|
||
|
|
<Card className="max-w-4xl mx-auto glass-card border-border/50 shadow-2xl">
|
||
|
|
<CardContent className="p-6">
|
||
|
|
<div className="flex flex-col md:flex-row items-start md:items-center gap-4">
|
||
|
|
<div className="flex items-start gap-3 flex-1">
|
||
|
|
<div className="p-2 rounded-lg bg-primary/10">
|
||
|
|
<Cookie className="w-6 h-6 text-primary" />
|
||
|
|
</div>
|
||
|
|
<div className="space-y-1">
|
||
|
|
<h3 className="font-semibold text-foreground">
|
||
|
|
We value your privacy
|
||
|
|
</h3>
|
||
|
|
<p className="text-sm text-muted-foreground">
|
||
|
|
We use cookies to enhance your browsing experience, serve
|
||
|
|
personalized content, and analyze our traffic. By clicking
|
||
|
|
"Accept All", you consent to our use of cookies.{" "}
|
||
|
|
<Link
|
||
|
|
to="/privacy"
|
||
|
|
className="text-primary hover:underline"
|
||
|
|
>
|
||
|
|
Read our Privacy Policy
|
||
|
|
</Link>
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div className="flex flex-wrap items-center gap-2 w-full md:w-auto">
|
||
|
|
<Button
|
||
|
|
variant="outline"
|
||
|
|
size="sm"
|
||
|
|
onClick={() => setShowSettings(true)}
|
||
|
|
className="gap-2"
|
||
|
|
>
|
||
|
|
<Settings className="w-4 h-4" />
|
||
|
|
Customize
|
||
|
|
</Button>
|
||
|
|
<Button variant="outline" size="sm" onClick={handleRejectAll}>
|
||
|
|
Reject All
|
||
|
|
</Button>
|
||
|
|
<Button size="sm" onClick={handleAcceptAll}>
|
||
|
|
Accept All
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</CardContent>
|
||
|
|
</Card>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Cookie Settings Dialog */}
|
||
|
|
<Dialog open={showSettings} onOpenChange={setShowSettings}>
|
||
|
|
<DialogContent className="sm:max-w-lg">
|
||
|
|
<DialogHeader>
|
||
|
|
<DialogTitle className="flex items-center gap-2">
|
||
|
|
<Shield className="w-5 h-5 text-primary" />
|
||
|
|
Cookie Preferences
|
||
|
|
</DialogTitle>
|
||
|
|
<DialogDescription>
|
||
|
|
Manage your cookie preferences. You can enable or disable
|
||
|
|
different types of cookies below.
|
||
|
|
</DialogDescription>
|
||
|
|
</DialogHeader>
|
||
|
|
|
||
|
|
<div className="space-y-6 py-4">
|
||
|
|
{/* Necessary Cookies */}
|
||
|
|
<div className="flex items-start justify-between gap-4">
|
||
|
|
<div className="space-y-1">
|
||
|
|
<Label className="text-base font-medium">
|
||
|
|
Necessary Cookies
|
||
|
|
</Label>
|
||
|
|
<p className="text-sm text-muted-foreground">
|
||
|
|
Essential for the website to function properly. Cannot be
|
||
|
|
disabled.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
<Switch checked={true} disabled />
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Analytics Cookies */}
|
||
|
|
<div className="flex items-start justify-between gap-4">
|
||
|
|
<div className="space-y-1">
|
||
|
|
<Label className="text-base font-medium">
|
||
|
|
Analytics Cookies
|
||
|
|
</Label>
|
||
|
|
<p className="text-sm text-muted-foreground">
|
||
|
|
Help us understand how visitors interact with our website to
|
||
|
|
improve user experience.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
<Switch
|
||
|
|
checked={preferences.analytics}
|
||
|
|
onCheckedChange={(checked) =>
|
||
|
|
setPreferences((prev) => ({ ...prev, analytics: checked }))
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Marketing Cookies */}
|
||
|
|
<div className="flex items-start justify-between gap-4">
|
||
|
|
<div className="space-y-1">
|
||
|
|
<Label className="text-base font-medium">
|
||
|
|
Marketing Cookies
|
||
|
|
</Label>
|
||
|
|
<p className="text-sm text-muted-foreground">
|
||
|
|
Used to track visitors across websites to display relevant
|
||
|
|
advertisements.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
<Switch
|
||
|
|
checked={preferences.marketing}
|
||
|
|
onCheckedChange={(checked) =>
|
||
|
|
setPreferences((prev) => ({ ...prev, marketing: checked }))
|
||
|
|
}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex justify-end gap-2">
|
||
|
|
<Button variant="outline" onClick={handleRejectAll}>
|
||
|
|
Reject All
|
||
|
|
</Button>
|
||
|
|
<Button onClick={handleSavePreferences}>Save Preferences</Button>
|
||
|
|
</div>
|
||
|
|
</DialogContent>
|
||
|
|
</Dialog>
|
||
|
|
</>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default CookieConsent;
|