125 lines
4.0 KiB
TypeScript
125 lines
4.0 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import { useLanguage } from '@/lib/i18n'
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from '@/components/ui/dialog'
|
|
import { getConsent, setConsent } from '@/lib/consent/cookie-consent'
|
|
import { toast } from 'sonner'
|
|
|
|
interface CookiePreferencesDialogProps {
|
|
open: boolean
|
|
onOpenChange: (open: boolean) => void
|
|
}
|
|
|
|
export function CookiePreferencesDialog({ open, onOpenChange }: CookiePreferencesDialogProps) {
|
|
const { t } = useLanguage()
|
|
const [analytics, setAnalytics] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (!open) return
|
|
const current = getConsent()
|
|
setAnalytics(current?.analytics ?? false)
|
|
}, [open])
|
|
|
|
const handleSave = () => {
|
|
setConsent({ analytics, marketing: false })
|
|
toast.success(t('consent.preferences.saved'))
|
|
onOpenChange(false)
|
|
}
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="max-w-lg border-border bg-memento-paper dark:bg-background">
|
|
<DialogHeader>
|
|
<DialogTitle className="font-memento-serif text-xl text-ink">
|
|
{t('consent.preferences.title')}
|
|
</DialogTitle>
|
|
<DialogDescription className="text-[11px] text-concrete leading-relaxed">
|
|
{t('consent.preferences.description')}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-6 py-2">
|
|
<CategoryRow
|
|
title={t('consent.preferences.necessaryTitle')}
|
|
description={t('consent.preferences.necessaryDesc')}
|
|
alwaysOnLabel={t('consent.preferences.alwaysOn')}
|
|
locked
|
|
checked
|
|
/>
|
|
<CategoryRow
|
|
title={t('consent.preferences.analyticsTitle')}
|
|
description={t('consent.preferences.analyticsDesc')}
|
|
checked={analytics}
|
|
onChange={setAnalytics}
|
|
/>
|
|
</div>
|
|
|
|
<DialogFooter className="gap-2 sm:gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={() => onOpenChange(false)}
|
|
className="px-5 py-2.5 text-[10px] font-bold uppercase tracking-[0.2em] text-concrete hover:text-ink transition-colors"
|
|
>
|
|
{t('consent.preferences.cancel')}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={handleSave}
|
|
className="px-6 py-2.5 bg-foreground text-background rounded-xl text-[10px] font-bold uppercase tracking-[0.2em] hover:opacity-90 transition-opacity"
|
|
>
|
|
{t('consent.preferences.save')}
|
|
</button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|
|
|
|
function CategoryRow({
|
|
title,
|
|
description,
|
|
checked,
|
|
onChange,
|
|
locked,
|
|
alwaysOnLabel,
|
|
}: {
|
|
title: string
|
|
description: string
|
|
checked: boolean
|
|
onChange?: (v: boolean) => void
|
|
locked?: boolean
|
|
alwaysOnLabel?: string
|
|
}) {
|
|
return (
|
|
<div className="flex items-start justify-between gap-4 border-b border-border/40 pb-5 last:border-0 last:pb-0">
|
|
<div className="space-y-1 pe-4">
|
|
<p className="text-xs font-bold text-ink">{title}</p>
|
|
<p className="text-[10px] text-concrete leading-relaxed">{description}</p>
|
|
{locked && alwaysOnLabel && (
|
|
<p className="text-[9px] font-bold uppercase tracking-widest text-concrete/80">{alwaysOnLabel}</p>
|
|
)}
|
|
</div>
|
|
<label
|
|
className={`relative inline-flex shrink-0 items-center ${locked ? 'opacity-50 pointer-events-none' : 'cursor-pointer'}`}
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
className="sr-only peer"
|
|
checked={checked}
|
|
disabled={locked}
|
|
onChange={(e) => onChange?.(e.target.checked)}
|
|
/>
|
|
<div className="w-11 h-6 bg-gray-200 dark:bg-white/10 rounded-full peer peer-checked:after:translate-x-[20px] after:content-[''] after:absolute after:top-[4px] after:left-[4px] after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-ink" />
|
|
</label>
|
|
</div>
|
|
)
|
|
}
|