"use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import Link from "next/link"; import { fmtGBP, fmtDate } from "../lib/pb"; type Invoice = { id: string; invoice_no: string; invoice_date: string; buyer_name_variant: string; invoice_total: number; }; type BatchWithInvoices = { batch: { id: string; batch_ref: string; payment_date: string; pop_file: string; zar_paid?: number; notes?: string }; invoices: Invoice[]; popUrl: string | null; }; export default function RemittanceClient({ batches }: { batches: BatchWithInvoices[] }) { const router = useRouter(); const [expanded, setExpanded] = useState>(new Set()); const [busyId, setBusyId] = useState(null); const [error, setError] = useState(null); // editingId + editForm: when editingId === batch.id, render an inline form // instead of the read-only header. const [editingId, setEditingId] = useState(null); const [editForm, setEditForm] = useState<{ batch_ref: string; payment_date: string; notes: string; zar_paid: string }>({ batch_ref: "", payment_date: "", notes: "", zar_paid: "", }); function toggle(id: string) { setExpanded((prev) => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next; }); } function startEdit(batch: BatchWithInvoices["batch"]) { setEditingId(batch.id); setEditForm({ batch_ref: batch.batch_ref, payment_date: batch.payment_date ? batch.payment_date.slice(0, 10) : "", notes: batch.notes ?? "", zar_paid: typeof batch.zar_paid === "number" && batch.zar_paid > 0 ? String(batch.zar_paid) : "", }); setError(null); } function cancelEdit() { setEditingId(null); setError(null); } async function saveEdit(batchId: string) { setError(null); setBusyId(batchId); try { const resp = await fetch(`/api/payments/${batchId}/edit`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ batch_ref: editForm.batch_ref, payment_date: editForm.payment_date, notes: editForm.notes, zar_paid: editForm.zar_paid.trim() === "" ? 0 : Number(editForm.zar_paid), }), }); const json = await resp.json().catch(() => ({})); if (!resp.ok) { setError(json?.error || `HTTP ${resp.status}`); return; } setEditingId(null); router.refresh(); } catch (e: any) { setError(String(e?.message || e)); } finally { setBusyId(null); } } async function replacePop(batchId: string, file: File) { setError(null); setBusyId(batchId); try { const fd = new FormData(); fd.set("pop_file", file); const resp = await fetch(`/api/payments/${batchId}/replace-pop`, { method: "POST", body: fd }); const json = await resp.json().catch(() => ({})); if (!resp.ok) { setError(json?.error || `HTTP ${resp.status}`); return; } router.refresh(); } catch (e: any) { setError(String(e?.message || e)); } finally { setBusyId(null); } } async function unpay(batchId: string, batchRef: string) { if (!window.confirm(`Unpay batch ${batchRef}? All ${countInvoices(batchId)} invoices will return to unpaid and the batch will be deleted.`)) { return; } setError(null); setBusyId(batchId); try { const resp = await fetch(`/api/payments/${batchId}/unpay`, { method: "POST" }); const json = await resp.json().catch(() => ({})); if (!resp.ok) { setError(json?.error || `HTTP ${resp.status}`); return; } router.refresh(); } catch (e: any) { setError(String(e?.message || e)); } finally { setBusyId(null); } } function countInvoices(id: string): number { return batches.find((b) => b.batch.id === id)?.invoices.length ?? 0; } return (
{error && (
{error}
)} {batches.map(({ batch, invoices, popUrl }) => { const total = invoices.reduce((s, inv) => s + (inv.invoice_total || 0), 0); const isExpanded = expanded.has(batch.id); const isBusy = busyId === batch.id; const isEditing = editingId === batch.id; return (
{isEditing ? ( // EDIT MODE
Edit batch
setEditForm({ ...editForm, batch_ref: e.target.value })} pattern="[A-Za-z0-9 _\-\/.,:()&#'+]{1,100}" required className="w-full border rounded px-3 py-2 font-mono" />
setEditForm({ ...editForm, payment_date: e.target.value })} required className="w-full border rounded px-3 py-2" />
setEditForm({ ...editForm, zar_paid: e.target.value })} placeholder="e.g. 12500.00" className="w-full border rounded px-3 py-2 tabular-nums" />