'use client' import { useState, useEffect } from 'react' import { useFetch } from '@/hooks/use-fetch' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Skeleton } from '@/components/ui/skeleton' import { Separator } from '@/components/ui/separator' import { PageHeader } from '../primitives' import { useAos } from '@/store/aos' import { LOCALES } from '@/lib/i18n' import { Loader2, Save, Building2, FileText, Percent, Radar, FileCheck2, Languages, Lock, CheckCircle2 } from 'lucide-react' import { toast } from 'sonner' import { cn } from '@/lib/utils' interface SettingsData { settings: Record } export function GlobalSettingsView() { const { data, loading, refetch } = useFetch('/api/aos/settings') const { locale, setLocale } = useAos() const [form, setForm] = useState>({}) const [saving, setSaving] = useState(false) const [dirty, setDirty] = useState(false) useEffect(() => { if (data?.settings) { setForm(data.settings) setDirty(false) } }, [data]) if (loading || !data) { return (
) } const update = (key: string, value: string) => { setForm((f) => ({ ...f, [key]: value })) setDirty(true) } const save = async () => { setSaving(true) const t = toast.loading('Einstellungen werden gespeichert…') try { const r = await fetch('/api/aos/settings', { method: 'PATCH', headers: { 'content-type': 'application/json' }, body: JSON.stringify(form), }) const res = await r.json() if (!r.ok) throw new Error(res.error || 'save failed') toast.success(`${res.updated} Einstellungen gespeichert (Audit protokolliert)`, { id: t }) setDirty(false) refetch() } catch (err) { toast.error(err instanceof Error ? err.message : 'Speichern fehlgeschlagen', { id: t }) } finally { setSaving(false) } } const Field = ({ label, k, mono, suffix }: { label: string; k: string; mono?: boolean; suffix?: string }) => (
update(k, e.target.value)} className={cn('text-sm', mono && 'font-mono')} /> {suffix && {suffix}}
) return (
{saving ? : } Speichern } /> {/* i18n / Locale */} Sprache & Lokalisierung
{LOCALES.map((l) => ( ))}

Zahlen-/Datumsformat folgt der UI-Sprache: DE = 1.234,56 € / 26.10.2026, EN = 1,234.56 € / 10/26/2026, TR = 1.234,56 ₺ / 26.10.2026

{/* Company data */} Unternehmens-Stammdaten {/* Invoice numbering */} Rechnungs-Nummernkreis (GoBD: lückenlos)

Vorschau: {form['invoice.numberPrefix'] ?? 'RE'}-{form['invoice.numberYear'] ?? '2026'}-{String(form['invoice.numberNext'] ?? '0042').padStart(4, '0')} — GoBD-konform, kein Löschen, Korrekturen als Storno (neue Zeile).

{/* Taxes */} Steuersätze (Basispunkte) {/* Lead Finder */} Leads Finder {/* E-Rechnung */} E-Rechnung (ZUGFeRD / XRechnung)

Empfangs-Pflicht seit 2025, Versand-Pflicht ab 2027/2028 (umsatzabhängig). Validierung via @e-invoice-eu/core (EN 16931).

{/* Compliance / Retention */} Compliance & Aufbewahrung

Secrets werden AES-256-GCM verschlüsselt

Der ENCRYPTION_KEY wird separat vom DB-Backup verwahrt. Ohne ihn sind SMTP, API-Keys und Signier-Schlüssel unwiederherstellbar.

{/* Mail config */} E-Mail (SMTP/IMAP) {/* Save bar */} {dirty && (
Ungespeichert Änderungen werden im Audit-Log protokolliert.
)}
) }