'use client' import { useState } from 'react' import { useTranslations } from 'next-intl' import { Switch } from '@/components/ui/switch' import { Label } from '@/components/ui/label' import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' import { Loader2, CircleSlash, Lock } from 'lucide-react' import { useToast } from '@/components/ui/use-toast' import { getErrorMessage } from '@/lib/errors/get-error-message' interface Props { entryId: string initialExempt: boolean initialReason: string | null canWrite: boolean onChange: (exempted: boolean, reason: string | null) => void } /** * Lets the bookkeeper mark a posted verifikation as not needing a separate * underlag (bankavgift, ränta, intern överföring etc.). Toggling persists via * /api/bookkeeping/journal-entries/[id]/no-document-required. * * The flag lives in journal_entry_no_doc_required (sidecar) so the underlying * verifikation stays immutable per BFL. */ export default function NoDocRequiredToggle({ entryId, initialExempt, initialReason, canWrite, onChange, }: Props) { const t = useTranslations('journal_list') const { toast } = useToast() const [exempt, setExempt] = useState(initialExempt) const [reason, setReason] = useState(initialReason ?? '') const [saving, setSaving] = useState(false) const [editingReason, setEditingReason] = useState(false) const persistExempt = async ( nextExempt: boolean, nextReason: string | null, previousReason: string, ) => { setSaving(true) try { const res = nextExempt ? await fetch(`/api/bookkeeping/journal-entries/${entryId}/no-document-required`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ reason: nextReason ?? null }), }) : await fetch(`/api/bookkeeping/journal-entries/${entryId}/no-document-required`, { method: 'DELETE', }) if (!res.ok) { // Resolve via the parsed body plus the status: the route answers // thrown errors with the canonical envelope `{ error: { code, // message } }`, and rendering that object as the toast description // would crash React ("Objects are not valid as a React child"). const body = await res.json().catch(() => null) toast({ title: t('no_doc_required_save_failed'), description: getErrorMessage(body, { statusCode: res.status }), variant: 'destructive', }) // Roll back UI state on failure: both the toggle AND the reason so // the rendered state matches the DB row we failed to mutate. setExempt(!nextExempt) setReason(previousReason) return } onChange(nextExempt, nextExempt ? nextReason : null) } catch { toast({ title: t('no_doc_required_save_failed'), variant: 'destructive' }) setExempt(!nextExempt) setReason(previousReason) } finally { setSaving(false) } } const handleToggle = (checked: boolean) => { const previousReason = reason setExempt(checked) if (!checked) { setReason('') setEditingReason(false) } persistExempt(checked, checked ? (reason.trim() || null) : null, previousReason) } const handleSaveReason = () => { setEditingReason(false) persistExempt(true, reason.trim() || null, reason) } return (