diff --git a/frontend/src/pages/SettingsTaxes.tsx b/frontend/src/pages/SettingsTaxes.tsx new file mode 100644 index 0000000..f62c13d --- /dev/null +++ b/frontend/src/pages/SettingsTaxes.tsx @@ -0,0 +1,143 @@ +import React, { useState, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { apiGet, apiPost, apiPatch, apiDelete } from '@/api/client'; + +interface TaxRate { + id: string; + name: string; + rate: number; + is_default: boolean; + country: string | null; +} + +export function SettingsTaxesPage() { + const { t } = useTranslation(); + const [taxes, setTaxes] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [showForm, setShowForm] = useState(false); + const [editing, setEditing] = useState(null); + const [formData, setFormData] = useState({ name: '', rate: 0, is_default: false, country: '' }); + + const fetchTaxes = useCallback(async () => { + setLoading(true); + setError(null); + try { + const data = await apiGet<{ items: TaxRate[]; total: number }>('/taxes'); + setTaxes(data.items); + } catch (err: any) { + setError(err.message || t('common.error')); + } finally { + setLoading(false); + } + }, [t]); + + React.useEffect(() => { fetchTaxes(); }, [fetchTaxes]); + + const handleSave = async (e: React.FormEvent) => { + e.preventDefault(); + try { + const payload = { ...formData, country: formData.country || null }; + if (editing) { + await apiPatch(`/taxes/${editing.id}`, payload); + } else { + await apiPost('/taxes', payload); + } + setShowForm(false); + setEditing(null); + setFormData({ name: '', rate: 0, is_default: false, country: '' }); + await fetchTaxes(); + } catch (err: any) { + setError(err.message || t('common.error')); + } + }; + + const handleDelete = async (id: string) => { + if (!window.confirm(t('taxes.confirmDelete'))) return; + try { + await apiDelete(`/taxes/${id}`); + await fetchTaxes(); + } catch (err: any) { + setError(err.message || t('common.error')); + } + }; + + const handleEdit = (tax: TaxRate) => { + setEditing(tax); + setFormData({ name: tax.name, rate: tax.rate, is_default: tax.is_default, country: tax.country || '' }); + setShowForm(true); + }; + + if (loading) return
{t('common.loading')}
; + + return ( +
+
+

{t('taxes.title')}

+ +
+ + {error &&
{error}
} + + {showForm && ( +
+
+ + setFormData({ ...formData, name: e.target.value })} required className="mt-1 block w-full rounded-md border border-secondary-300 px-3 py-1.5 text-sm" /> +
+
+
+ + setFormData({ ...formData, rate: parseFloat(e.target.value) || 0 })} required className="mt-1 block w-full rounded-md border border-secondary-300 px-3 py-1.5 text-sm" /> +
+
+ + setFormData({ ...formData, country: e.target.value })} maxLength={2} placeholder="DE" className="mt-1 block w-full rounded-md border border-secondary-300 px-3 py-1.5 text-sm" /> +
+
+ +
+ + +
+
+ )} + +
+ + + + + + + + + + + + {taxes.map((tax) => ( + + + + + + + + ))} + +
{t('taxes.name')}{t('taxes.rate')}{t('taxes.isDefault')}{t('taxes.country')}
{tax.name}{tax.rate}%{tax.is_default && }{tax.country || '—'} + + +
+
+
+ ); +}