Files
Hotel-Booking/Frontend/src/shared/components/CookiePreferencesModal.tsx
Iliyan Angelov 39fcfff811 update
2025-11-30 22:43:09 +02:00

224 lines
9.4 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { X } from 'lucide-react';
import { useCookieConsent } from '../contexts/CookieConsentContext';
import { useClickOutside } from '../hooks/useClickOutside';
const CookiePreferencesModal: React.FC = () => {
const { consent, updateConsent } = useCookieConsent();
const [isOpen, setIsOpen] = useState(false);
const [analyticsChecked, setAnalyticsChecked] = useState(false);
const [marketingChecked, setMarketingChecked] = useState(false);
const [preferencesChecked, setPreferencesChecked] = useState(false);
const modalRef = React.useRef<HTMLDivElement>(null);
useClickOutside(modalRef, () => {
if (isOpen) {
setIsOpen(false);
}
});
useEffect(() => {
if (consent) {
setAnalyticsChecked(consent.categories.analytics);
setMarketingChecked(consent.categories.marketing);
setPreferencesChecked(consent.categories.preferences);
}
}, [consent]);
useEffect(() => {
const handleOpenPreferences = () => {
setIsOpen(true);
};
window.addEventListener('open-cookie-preferences', handleOpenPreferences);
return () => {
window.removeEventListener('open-cookie-preferences', handleOpenPreferences);
};
}, []);
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = '';
}
return () => {
document.body.style.overflow = '';
};
}, [isOpen]);
if (!isOpen) {
return null;
}
const handleAcceptAll = async () => {
await updateConsent({
analytics: true,
marketing: true,
preferences: true,
});
setIsOpen(false);
};
const handleRejectNonEssential = async () => {
await updateConsent({
analytics: false,
marketing: false,
preferences: false,
});
setIsOpen(false);
};
const handleSaveSelection = async () => {
await updateConsent({
analytics: analyticsChecked,
marketing: marketingChecked,
preferences: preferencesChecked,
});
setIsOpen(false);
};
return (
<div className="fixed inset-0 z-50 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
{/* Backdrop */}
<div
className="fixed inset-0 bg-black/70 backdrop-blur-sm transition-opacity"
onClick={() => setIsOpen(false)}
aria-hidden="true"
/>
{/* Modal */}
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
<div
ref={modalRef}
className="relative transform overflow-hidden rounded-2xl bg-gradient-to-br from-zinc-950/95 via-zinc-900/95 to-black/95 text-left shadow-2xl border border-[#d4af37]/30 transition-all sm:my-8 sm:w-full sm:max-w-2xl"
>
{/* Close button */}
<button
onClick={() => setIsOpen(false)}
className="absolute right-4 top-4 z-10 rounded-full p-2 text-gray-400 hover:bg-zinc-800/50 hover:text-white transition-colors"
aria-label="Close"
>
<X className="w-5 h-5" />
</button>
<div className="px-6 py-6 sm:px-8 sm:py-8">
{/* Header */}
<div className="mb-6">
<div className="inline-flex items-center gap-2 rounded-full bg-black/60 px-3 py-1 text-[11px] font-medium uppercase tracking-[0.16em] text-[#d4af37]/90 ring-1 ring-[#d4af37]/30 mb-4">
<span className="h-1.5 w-1.5 rounded-full bg-[#d4af37]" />
Privacy Suite
</div>
<h2 className="text-2xl font-elegant font-bold text-white mb-2">
Cookie Preferences
</h2>
<p className="text-sm text-zinc-300">
Manage your cookie preferences. You can enable or disable different types of cookies below.
</p>
</div>
{/* Cookie Categories */}
<div className="space-y-4 mb-6">
<div className="rounded-xl bg-black/40 p-4 ring-1 ring-zinc-800/80 backdrop-blur-md">
<div className="flex items-start gap-3">
<div className="mt-0.5 h-5 w-5 rounded border border-[#d4af37]/50 bg-[#d4af37]/20 flex items-center justify-center">
<span className="text-[#d4af37] text-xs"></span>
</div>
<div className="flex-1">
<p className="font-semibold text-zinc-50 mb-1">Strictly necessary</p>
<p className="text-xs text-zinc-400">
Essential for security, authentication, and core booking flows. These are always enabled.
</p>
</div>
</div>
</div>
<div className="rounded-xl bg-black/40 p-4 ring-1 ring-zinc-800/80 backdrop-blur-md">
<div className="flex items-start gap-3">
<input
id="cookie-analytics-modal"
type="checkbox"
className="mt-0.5 h-5 w-5 rounded border-zinc-500 bg-black/40 text-[#d4af37] focus:ring-[#d4af37] cursor-pointer"
checked={analyticsChecked}
onChange={(e) => setAnalyticsChecked(e.target.checked)}
/>
<label htmlFor="cookie-analytics-modal" className="flex-1 cursor-pointer">
<p className="font-semibold text-zinc-50 mb-1">Analytics</p>
<p className="text-xs text-zinc-400">
Anonymous insights that help us refine performance and guest experience throughout the site.
</p>
</label>
</div>
</div>
<div className="rounded-xl bg-black/40 p-4 ring-1 ring-zinc-800/80 backdrop-blur-md">
<div className="flex items-start gap-3">
<input
id="cookie-marketing-modal"
type="checkbox"
className="mt-0.5 h-5 w-5 rounded border-zinc-500 bg-black/40 text-[#d4af37] focus:ring-[#d4af37] cursor-pointer"
checked={marketingChecked}
onChange={(e) => setMarketingChecked(e.target.checked)}
/>
<label htmlFor="cookie-marketing-modal" className="flex-1 cursor-pointer">
<p className="font-semibold text-zinc-50 mb-1">Tailored offers</p>
<p className="text-xs text-zinc-400">
Allow us to present bespoke promotions and experiences aligned with your interests.
</p>
</label>
</div>
</div>
<div className="rounded-xl bg-black/40 p-4 ring-1 ring-zinc-800/80 backdrop-blur-md">
<div className="flex items-start gap-3">
<input
id="cookie-preferences-modal"
type="checkbox"
className="mt-0.5 h-5 w-5 rounded border-zinc-500 bg-black/40 text-[#d4af37] focus:ring-[#d4af37] cursor-pointer"
checked={preferencesChecked}
onChange={(e) => setPreferencesChecked(e.target.checked)}
/>
<label htmlFor="cookie-preferences-modal" className="flex-1 cursor-pointer">
<p className="font-semibold text-zinc-50 mb-1">Comfort preferences</p>
<p className="text-xs text-zinc-400">
Remember your choices such as language, currency, and layout for a smoother return visit.
</p>
</label>
</div>
</div>
</div>
{/* Action Buttons */}
<div className="flex flex-col sm:flex-row gap-3 pt-4 border-t border-zinc-800/50">
<button
type="button"
onClick={handleAcceptAll}
className="flex-1 inline-flex items-center justify-center rounded-full bg-gradient-to-r from-[#d4af37] via-[#f2cf74] to-[#d4af37] px-6 py-3 text-sm font-semibold uppercase tracking-[0.16em] text-black shadow-lg transition hover:from-[#f8e4a6] hover:via-[#ffe6a3] hover:to-[#f2cf74] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#d4af37]/70"
>
Accept all
</button>
<button
type="button"
onClick={handleRejectNonEssential}
className="flex-1 inline-flex items-center justify-center rounded-full border border-zinc-600/80 bg-black/40 px-6 py-3 text-sm font-semibold uppercase tracking-[0.16em] text-zinc-100 shadow-lg transition hover:border-zinc-400 hover:bg-black/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-500/70"
>
Essential only
</button>
<button
type="button"
onClick={handleSaveSelection}
className="flex-1 inline-flex items-center justify-center rounded-full border border-[#d4af37]/60 bg-zinc-900/80 px-6 py-3 text-sm font-semibold uppercase tracking-[0.16em] text-[#d4af37] shadow-lg transition hover:border-[#d4af37] hover:bg-zinc-800/80 hover:text-[#f5e9c6] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#d4af37]/70"
>
Save selection
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default CookiePreferencesModal;