import { useEffect, useRef, useState } from 'react' import { FaFloppyDisk, FaSpinner } from 'react-icons/fa6' import { Button, Card } from '../ui' import FormField from './FormField' import { useFinanceResource } from '../../hooks/useFinanceResource' import { getSettings, patchSettings } from '../../lib/financeApi' import { BANK_DETAILS_EXAMPLE, SETTINGS_FIELD_ORDER, SETTINGS_LIMITS, apiErrorMessage, bankDetailsLength, settingsToForm, validateSettingsForm, } from '../../lib/financeSettings' const LOAD_ERROR = 'Die Einstellungen konnten nicht geladen werden.' const fieldId = (name) => `fin-set-${name}` /** * Karte „Einstellungen“: Mindestpuffer, Steuer-Rücklage, Vorjahresumsatz, ESt-Vorauszahlung und Bankverbindungstext * für den Rechnungsfuß. Beträge werden in Euro eingegeben (z. B. 1.234,56; 0 ist bei Mindestpuffer und ESt erlaubt), * ein leerer Vorjahresumsatz heißt „unbekannt“. Fehler stehen am Feld, Fehler des Servers über dem Knopf. */ export default function SettingsCard() { const res = useFinanceResource(getSettings, LOAD_ERROR) if (!res.data) { return ( {res.loading ? (
) : (

{res.error || LOAD_ERROR}

)}
) } return } function SettingsForm({ initial }) { const [form, setForm] = useState(() => settingsToForm(initial)) const [errors, setErrors] = useState({}) const [busy, setBusy] = useState(false) const [notice, setNotice] = useState(null) // { tone: 'ok' | 'warn', text } const lockRef = useRef(false) const aliveRef = useRef(true) useEffect(() => { aliveRef.current = true return () => { aliveRef.current = false } }, []) const change = (name) => (e) => { const value = e.target.value setForm((f) => ({ ...f, [name]: value })) setErrors((er) => (er[name] ? { ...er, [name]: undefined } : er)) if (notice) setNotice(null) } async function submit(e) { e.preventDefault() if (lockRef.current) return const check = validateSettingsForm(form) if (!check.ok) { setErrors(check.errors) setNotice({ tone: 'warn', text: 'Bitte die markierten Felder prüfen. Es wurde nichts gespeichert.' }) const first = SETTINGS_FIELD_ORDER.find((name) => check.errors[name]) if (first) document.getElementById(fieldId(first))?.focus() return } lockRef.current = true setBusy(true) setErrors({}) setNotice(null) try { const data = await patchSettings(check.payload) if (!aliveRef.current) return if (data?.einstellungen) setForm(settingsToForm(data.einstellungen)) setNotice({ tone: 'ok', text: 'Gespeichert.' }) } catch (err) { if (aliveRef.current) setNotice({ tone: 'warn', text: apiErrorMessage(err) }) } finally { lockRef.current = false if (aliveRef.current) setBusy(false) } } const length = bankDetailsLength(form.bankDetailsText) const tooLong = length > SETTINGS_LIMITS.maxBankDetails return (
{/* Während des Speicherns gesperrt, damit die Antwort keine neuen Eingaben überschreibt */}