From afbad3d7e4945120670ebd844ca66d2e02e1261f Mon Sep 17 00:00:00 2001 From: Leopoldadmin Date: Sat, 4 Jul 2026 00:26:22 +0000 Subject: [PATCH] feat(core): add SettingsSystem page --- frontend/src/pages/SettingsSystem.tsx | 277 ++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 frontend/src/pages/SettingsSystem.tsx diff --git a/frontend/src/pages/SettingsSystem.tsx b/frontend/src/pages/SettingsSystem.tsx new file mode 100644 index 0000000..ab61d77 --- /dev/null +++ b/frontend/src/pages/SettingsSystem.tsx @@ -0,0 +1,277 @@ +import React, { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { + useSystemSettings, + useUpdateSystemSettings, + useCurrencies, + useTaxes, +} from '@/api/hooks'; +import { Input } from '@/components/ui/Input'; +import { Button } from '@/components/ui/Button'; +import { Card } from '@/components/ui/Card'; +import { Skeleton } from '@/components/ui/Skeleton'; +import { useToast } from '@/components/ui/Toast'; + +const settingsSchema = z.object({ + company_name: z.string().min(1, 'Name ist erforderlich').max(200), + company_legal_form: z.string().max(50).optional().or(z.literal('')), + company_street: z.string().min(1, 'Straße ist erforderlich').max(255), + company_city: z.string().min(1, 'Stadt ist erforderlich').max(100), + company_zip: z.string().min(1, 'PLZ ist erforderlich').max(20), + company_country: z.string().min(2, 'Land ist erforderlich').max(2), + tax_number: z.string().max(50).optional().or(z.literal('')), + vat_id: z.string().max(50).optional().or(z.literal('')), + iban: z.string().max(34).optional().or(z.literal('')), + bic: z.string().max(11).optional().or(z.literal('')), + bank_name: z.string().max(100).optional().or(z.literal('')), + ceo: z.string().max(100).optional().or(z.literal('')), + trade_register: z.string().max(100).optional().or(z.literal('')), + default_currency_id: z.string().optional().or(z.literal('')), + default_tax_id: z.string().optional().or(z.literal('')), + invoice_prefix: z.string().max(20).optional().or(z.literal('RE-')), + quote_prefix: z.string().max(20).optional().or(z.literal('AN-')), + payment_terms_days: z.number().int().min(0).max(365).default(14), +}); + +type SettingsFormValues = z.infer; + +export function SettingsSystemPage() { + const { t } = useTranslation(); + const toast = useToast(); + + const { data: settings, isLoading } = useSystemSettings(); + const { data: currenciesData } = useCurrencies(); + const { data: taxesData } = useTaxes(); + const updateMutation = useUpdateSystemSettings(); + + const currencies = currenciesData?.items ?? []; + const taxes = taxesData?.items ?? []; + + const { + register, + handleSubmit, + reset, + formState: { errors, isDirty, isSubmitting }, + } = useForm({ + resolver: zodResolver(settingsSchema), + defaultValues: { + company_name: '', + company_legal_form: '', + company_street: '', + company_city: '', + company_zip: '', + company_country: 'DE', + tax_number: '', + vat_id: '', + iban: '', + bic: '', + bank_name: '', + ceo: '', + trade_register: '', + default_currency_id: '', + default_tax_id: '', + invoice_prefix: 'RE-', + quote_prefix: 'AN-', + payment_terms_days: 14, + }, + }); + + useEffect(() => { + if (settings && Object.keys(settings).length > 0) { + reset({ + company_name: settings.company_name || '', + company_legal_form: settings.company_legal_form || '', + company_street: settings.company_street || '', + company_city: settings.company_city || '', + company_zip: settings.company_zip || '', + company_country: settings.company_country || 'DE', + tax_number: settings.tax_number || '', + vat_id: settings.vat_id || '', + iban: settings.iban || '', + bic: settings.bic || '', + bank_name: settings.bank_name || '', + ceo: settings.ceo || '', + trade_register: settings.trade_register || '', + default_currency_id: settings.default_currency_id || '', + default_tax_id: settings.default_tax_id || '', + invoice_prefix: settings.invoice_prefix || 'RE-', + quote_prefix: settings.quote_prefix || 'AN-', + payment_terms_days: settings.payment_terms_days || 14, + }); + } + }, [settings, reset]); + + const onSubmit = async (values: SettingsFormValues) => { + try { + const payload = { ...values }; + if (!payload.default_currency_id) payload.default_currency_id = null as any; + if (!payload.default_tax_id) payload.default_tax_id = null as any; + await updateMutation.mutateAsync(payload); + toast.success(t('systemSettings.saved')); + } catch (err: any) { + toast.error(err.message || t('systemSettings.saveFailed')); + } + }; + + if (isLoading) { + return ( +
+ + +
+ ); + } + + return ( +
+

{t('systemSettings.title')}

+ +
+ +
+ + +
+
+ + +
+ + + + +
+
+ + +
+ + + + + + + +
+
+ + +
+
+ + +
+
+ + +
+ + + +
+
+ +
+ +
+
+
+ ); +}