From 9a6a02a9e8a1d8d18a0976aa919be053fd4ce080 Mon Sep 17 00:00:00 2001 From: seaznCode Date: Wed, 22 Apr 2026 21:14:39 +0200 Subject: [PATCH] feat: add invoice upload functionality with form validation and modal --- src/app/admin/finance-management/page.tsx | 161 ++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/src/app/admin/finance-management/page.tsx b/src/app/admin/finance-management/page.tsx index bad7358..ec9b5f0 100644 --- a/src/app/admin/finance-management/page.tsx +++ b/src/app/admin/finance-management/page.tsx @@ -121,6 +121,16 @@ export default function FinanceManagementPage() { } const [pdfLoading, setPdfLoading] = useState(null) + const [uploadModalOpen, setUploadModalOpen] = useState(false) + const [uploadForm, setUploadForm] = useState({ + buyer_name: '', buyer_email: '', buyer_street: '', buyer_postal_code: '', + buyer_city: '', buyer_country: '', currency: 'EUR', + total_gross: '', vat_rate: '20', + status: 'issued', issued_at: '', due_at: '', + }) + const [uploadFile, setUploadFile] = useState(null) + const [uploading, setUploading] = useState(false) + const [uploadError, setUploadError] = useState(null) const viewInvoicePdf = async (inv: AdminInvoice) => { setPdfLoading(inv.id) @@ -147,6 +157,52 @@ export default function FinanceManagementPage() { } } + const submitUploadInvoice = async () => { + if (!uploadForm.total_gross || isNaN(Number(uploadForm.total_gross))) { + setUploadError('Total gross (Bruttobetrag) is required.') + return + } + setUploadError(null) + setUploading(true) + try { + const base = process.env.NEXT_PUBLIC_API_BASE_URL || '' + const gross = parseFloat(uploadForm.total_gross) + const rate = parseFloat(uploadForm.vat_rate) || 0 + const net = rate > 0 ? +(gross / (1 + rate / 100)).toFixed(2) : gross + const tax = +(gross - net).toFixed(2) + const fd = new FormData() + const { total_gross, vat_rate, ...rest } = uploadForm + Object.entries(rest).forEach(([k, v]) => { if (v !== '') fd.append(k, v) }) + fd.append('total_gross', gross.toFixed(2)) + fd.append('total_net', net.toFixed(2)) + fd.append('total_tax', tax.toFixed(2)) + fd.append('vat_rate', String(rate)) + if (uploadFile) fd.append('pdf', uploadFile) + const res = await fetch(`${base}/api/admin/invoices`, { + method: 'POST', + credentials: 'include', + headers: { ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}) }, + body: fd, + }) + const body = await res.json().catch(() => ({})) + if (!res.ok || body?.success === false) throw new Error(body?.message || `Upload failed (${res.status})`) + setUploadModalOpen(false) + setUploadForm({ + buyer_name: '', buyer_email: '', buyer_street: '', buyer_postal_code: '', + buyer_city: '', buyer_country: '', currency: 'EUR', + total_gross: '', vat_rate: '20', + status: 'issued', issued_at: '', due_at: '', + }) + setUploadFile(null) + reload() + setReportMsg({ type: 'success', text: `Invoice ${body.data?.invoice_number ?? ''} created successfully.` }) + } catch (e: any) { + setUploadError(e?.message || 'Upload failed.') + } finally { + setUploading(false) + } + } + const sendEmailReport = async () => { if (!reportEmail.trim()) return setReportMsg(null) @@ -246,6 +302,7 @@ export default function FinanceManagementPage() {

Invoices

+ @@ -444,6 +501,110 @@ export default function FinanceManagementPage() { /> )} + {/* Upload Invoice Modal */} + {uploadModalOpen && ( +
+
+

Upload Invoice

+
+
+ + setUploadForm(f => ({ ...f, buyer_name: e.target.value }))} placeholder="Max Mustermann" /> +
+
+ + setUploadForm(f => ({ ...f, buyer_email: e.target.value }))} placeholder="kunde@example.com" /> +
+
+ + setUploadForm(f => ({ ...f, buyer_street: e.target.value }))} placeholder="Musterstraße 1" /> +
+
+ + setUploadForm(f => ({ ...f, buyer_postal_code: e.target.value }))} placeholder="8010" /> +
+
+ + setUploadForm(f => ({ ...f, buyer_city: e.target.value }))} placeholder="Graz" /> +
+
+ + setUploadForm(f => ({ ...f, buyer_country: e.target.value }))} placeholder="Austria" /> +
+
+ + setUploadForm(f => ({ ...f, total_gross: e.target.value }))} placeholder="0.00" /> +
+
+ + setUploadForm(f => ({ ...f, vat_rate: e.target.value }))} placeholder="20" /> +
+ {(() => { + const gross = parseFloat(uploadForm.total_gross) || 0 + const rate = parseFloat(uploadForm.vat_rate) || 0 + const net = rate > 0 ? +(gross / (1 + rate / 100)).toFixed(2) : gross + const tax = +(gross - net).toFixed(2) + return ( +
+
+
Netto (calculated)
+
{uploadForm.currency} {net.toFixed(2)}
+
+
+
MwSt. (calculated)
+
{uploadForm.currency} {tax.toFixed(2)}
+
+
+ ) + })()} +
+ + +
+
+ + +
+
+ + setUploadForm(f => ({ ...f, issued_at: e.target.value }))} /> +
+
+ + setUploadForm(f => ({ ...f, due_at: e.target.value }))} /> +
+
+ + setUploadFile(e.target.files?.[0] ?? null)} + /> + {uploadFile &&

{uploadFile.name}

} +
+
+ {uploadError && ( +
{uploadError}
+ )} +
+ + +
+
+
+ )} + {/* Email Report Dialog */} {emailDialogOpen && (