nsfwapp/frontend/src/components/ui/CookieModal.tsx
2026-03-27 13:17:16 +01:00

224 lines
8.8 KiB
TypeScript

// frontend\src\components\ui\CookieModal.tsx
'use client'
import { Dialog } from '@headlessui/react'
import { useEffect, useState, useRef } from 'react'
import Button from './Button'
export type CookieEntry = { name: string; value: string }
type CookieModalProps = {
open: boolean
onClose: () => void
onApply: (cookies: CookieEntry[]) => void
initialCookies: CookieEntry[]
}
export default function CookieModal({
open,
onClose,
onApply,
initialCookies,
}: CookieModalProps) {
const [name, setName] = useState('')
const [value, setValue] = useState('')
const [cookies, setCookies] = useState<CookieEntry[]>([])
const wasOpen = useRef(false)
useEffect(() => {
if (open && !wasOpen.current) {
setName('')
setValue('')
setCookies(initialCookies ?? [])
}
wasOpen.current = open
}, [open, initialCookies])
function addCookie() {
const n = name.trim()
const v = value.trim()
if (!n || !v) return
setCookies((prev) => {
const filtered = prev.filter((c) => c.name !== n)
return [...filtered, { name: n, value: v }]
})
setName('')
setValue('')
}
function removeCookie(n: string) {
setCookies((prev) => prev.filter((c) => c.name !== n))
}
function applyAndClose() {
onApply(cookies)
onClose()
}
return (
<Dialog open={open} onClose={onClose} className="relative z-[100]">
<div className="fixed inset-0 bg-black/40 backdrop-blur-[2px]" aria-hidden="true" />
<div className="fixed inset-0 flex items-center justify-center p-4">
<Dialog.Panel
className="
w-full max-w-2xl rounded-xl
border border-gray-200/80 bg-white shadow-xl
dark:border-white/10 dark:bg-gray-800 dark:outline dark:-outline-offset-1 dark:outline-white/10
"
>
<div className="border-b border-gray-200/70 px-6 py-4 dark:border-white/10">
<Dialog.Title className="text-base font-semibold text-gray-900 dark:text-white">
Zusätzliche Cookies
</Dialog.Title>
<p className="mt-1 text-sm text-gray-600 dark:text-gray-300">
Füge zusätzliche Cookies hinzu oder aktualisiere bestehende Werte.
</p>
</div>
<div className="space-y-4 px-6 py-5">
<div className="rounded-lg border border-gray-200/70 bg-gray-50/70 p-3 dark:border-white/10 dark:bg-white/5">
<div className="grid grid-cols-1 gap-2 sm:grid-cols-3">
<div className="sm:col-span-1">
<label className="mb-1 block text-xs font-medium text-gray-700 dark:text-gray-300">
Name
</label>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="z. B. cf_clearance"
className="
w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 shadow-sm
placeholder:text-gray-400
focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/20
dark:border-white/10 dark:bg-white/10 dark:text-white dark:placeholder:text-gray-400
dark:focus:border-indigo-400 dark:focus:ring-indigo-400/20
"
/>
</div>
<div className="sm:col-span-2">
<label className="mb-1 block text-xs font-medium text-gray-700 dark:text-gray-300">
Wert
</label>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Cookie-Wert"
className="
w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 shadow-sm
placeholder:text-gray-400
focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/20
dark:border-white/10 dark:bg-white/10 dark:text-white dark:placeholder:text-gray-400
dark:focus:border-indigo-400 dark:focus:ring-indigo-400/20
"
/>
</div>
</div>
<div className="mt-3 flex justify-end">
<Button
size="sm"
variant="secondary"
onClick={addCookie}
disabled={!name.trim() || !value.trim()}
>
Hinzufügen
</Button>
</div>
</div>
<div className="rounded-lg border border-gray-200/70 bg-white/70 shadow-sm dark:border-white/10 dark:bg-white/5">
<div className="border-b border-gray-200/70 px-4 py-3 dark:border-white/10">
<div className="text-sm font-semibold text-gray-900 dark:text-white">
Aktuelle Cookies
</div>
<div className="mt-0.5 text-xs text-gray-600 dark:text-gray-300">
{cookies.length} Eintrag{cookies.length === 1 ? '' : 'e'}
</div>
</div>
{cookies.length === 0 ? (
<div className="px-4 py-6 text-sm text-gray-500 dark:text-gray-400">
Noch keine Cookies hinzugefügt.
</div>
) : (
<div className="overflow-hidden">
<table className="w-full table-fixed text-sm">
<thead className="bg-gray-50 text-gray-700 dark:bg-white/5 dark:text-gray-200">
<tr className="border-b border-gray-200/70 dark:border-white/10">
<th className="w-[180px] px-4 py-2.5 text-left font-medium">Name</th>
<th className="w-[calc(100%-290px)] px-4 py-2.5 text-left font-medium">Wert</th>
<th className="w-[110px] px-4 py-2.5 text-right font-medium">Aktion</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200/70 dark:divide-white/10">
{cookies.map((c) => (
<tr
key={c.name}
className="bg-white hover:bg-gray-50/70 dark:bg-transparent dark:hover:bg-white/5"
>
<td className="px-4 py-3 align-top">
<div
className="
inline-flex max-w-full items-center rounded-md
border border-gray-200 bg-gray-50 px-3 py-2
font-mono text-xs text-gray-800
dark:border-white/10 dark:bg-white/5 dark:text-gray-100
"
title={c.name}
>
<span className="truncate">{c.name}</span>
</div>
</td>
<td className="px-4 py-3 align-top">
<div
className="
rounded-md border border-gray-200 bg-gray-50 px-3 py-2
font-mono text-xs text-gray-700
dark:border-white/10 dark:bg-black/10 dark:text-gray-200
"
title={c.value}
>
<span className="block truncate">{c.value}</span>
</div>
</td>
<td className="w-[110px] px-4 py-3 text-right align-top whitespace-nowrap">
<Button
type="button"
size="sm"
variant="secondary"
onClick={() => removeCookie(c.name)}
className="text-red-700 dark:text-red-300"
>
Entfernen
</Button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
<div className="flex justify-end gap-2 border-t border-gray-200/70 px-6 py-4 dark:border-white/10">
<Button variant="secondary" onClick={onClose}>
Abbrechen
</Button>
<Button variant="primary" onClick={applyAndClose}>
Übernehmen
</Button>
</div>
</Dialog.Panel>
</div>
</Dialog>
)
}