Files
Momento/memento-note/components/legal/cookie-preferences-dialog.tsx
Antigravity 0ccad50d6c
Some checks failed
CI / Lint, Test & Build (push) Failing after 5m36s
Deploy to Production / Build and Deploy (push) Has been cancelled
fix: replace invalid motionless tag with div in cookie dialog
2026-05-16 22:54:44 +00:00

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>
)
}