From 7a8801274fa63443361105995774ff754d218e76 Mon Sep 17 00:00:00 2001 From: seaznCode Date: Tue, 7 Apr 2026 16:49:56 +0200 Subject: [PATCH] feat: add email report functionality to finance management page --- src/app/admin/finance-management/page.tsx | 84 +++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/app/admin/finance-management/page.tsx b/src/app/admin/finance-management/page.tsx index 96d72ae..1d4db21 100644 --- a/src/app/admin/finance-management/page.tsx +++ b/src/app/admin/finance-management/page.tsx @@ -18,6 +18,10 @@ export default function FinanceManagementPage() { const [diagData, setDiagData] = useState(null) const [selectedInvoice, setSelectedInvoice] = useState(null) const [detailModalOpen, setDetailModalOpen] = useState(false) + const [emailDialogOpen, setEmailDialogOpen] = useState(false) + const [reportEmail, setReportEmail] = useState('') + const [sendingReport, setSendingReport] = useState(false) + const [reportMsg, setReportMsg] = useState<{ type: 'success' | 'error'; text: string } | null>(null) // NEW: fetch invoices from backend const { @@ -116,6 +120,39 @@ export default function FinanceManagementPage() { URL.revokeObjectURL(url) } + const sendEmailReport = async () => { + if (!reportEmail.trim()) return + setReportMsg(null) + setSendingReport(true) + try { + const base = process.env.NEXT_PUBLIC_API_BASE_URL || '' + const res = await fetch(`${base}/api/admin/invoices/email-report`, { + method: 'POST', + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}), + }, + body: JSON.stringify({ + email: reportEmail.trim(), + from: billFilter.from || undefined, + to: billFilter.to || undefined, + }), + }) + const body = await res.json().catch(() => ({})) + if (!res.ok || body?.success === false) { + throw new Error(body?.message || `Request failed (${res.status})`) + } + setReportMsg({ type: 'success', text: `Report sent to ${reportEmail.trim()} (${body.data?.sentCount ?? 0} paid invoice(s)).` }) + setEmailDialogOpen(false) + setReportEmail('') + } catch (e: any) { + setReportMsg({ type: 'error', text: e?.message || 'Failed to send email report.' }) + } finally { + setSendingReport(false) + } + } + return (
@@ -182,6 +219,7 @@ export default function FinanceManagementPage() {

Invoices

+ @@ -222,6 +260,11 @@ export default function FinanceManagementPage() {
+ {reportMsg && ( +
+ {reportMsg.text} +
+ )} {invError && (
{invError} @@ -366,6 +409,47 @@ export default function FinanceManagementPage() { onExport={(inv) => exportInvoice(inv)} /> )} + + {/* Email Report Dialog */} + {emailDialogOpen && ( +
+
+

Send Email Report

+
+ Only paid invoices will be included in the report, regardless of the status filter. + {(billFilter.from || billFilter.to) && ( + The current date range filter ({billFilter.from || '…'} – {billFilter.to || '…'}) will be applied. + )} +
+ + setReportEmail(e.target.value)} + placeholder="email@example.com" + className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm text-gray-900 placeholder:text-gray-400 focus:ring-2 focus:ring-blue-900 focus:border-transparent" + autoFocus + onKeyDown={e => { if (e.key === 'Enter' && !sendingReport) sendEmailReport() }} + /> +
+ + +
+
+
+ )}