import { useEffect, useRef, useState } from "react"; import { Button, Column, Grid, InlineLoading, NumberInput, Table, TableBody, TableCell, TableContainer, TableHead, TableHeader, TableRow, Tag, TextInput, } from "@carbon/react"; import { confirm } from "@tauri-apps/plugin-dialog"; import { api } from "../lib/api"; import type { InvoiceSeries } from "../lib/types"; import { useToast } from "../components/ToastProvider"; export default function SeriesSettings({ active: visible, embedded = false, }: { active: boolean; /** Rendered inside Settings: no page wrapper or title. */ embedded?: boolean; }) { const toast = useToast(); const [, setActive] = useState(null); const [all, setAll] = useState([]); const [loading, setLoading] = useState(true); const [prefix, setPrefix] = useState(`INV/${new Date().getFullYear()}-`); const [padding, setPadding] = useState(3); const [busy, setBusy] = useState(false); const seeded = useRef(false); const load = async () => { try { const [current, list] = await Promise.all([api.getActiveSeries(), api.listSeries()]); setActive(current); setAll(list); // Only seed the form once, so a refetch on re-entry does not overwrite what was typed. if (current && !seeded.current) { setPrefix(current.prefix); setPadding(current.padding); seeded.current = true; } } catch (e) { toast.error("Could not load series", String(e)); } finally { setLoading(false); } }; // The view stays mounted, so refetch every time it is shown again. useEffect(() => { if (visible) void load(); }, [visible]); const startNew = async () => { const ok = await confirm( `Start a new series from ${prefix}${String(1).padStart(padding, "0")}? ` + "The current series will be retired and the counter resets to 1.", { title: "Start new series", kind: "warning" }, ); if (!ok) return; setBusy(true); try { const created = await api.startNewSeries(prefix, padding); toast.success("New series started", `Next invoice: ${created.nextInvoiceNumber}`); await load(); } catch (e) { toast.error("Could not start a new series", String(e)); } finally { setBusy(false); } }; const pageClass = embedded ? undefined : "voiced-page"; if (loading) { return (
); } return (
{embedded ? null :

Invoice series

}

The active series drives the automatic invoice numbers. Start a new series to reset the counter.

setPrefix(e.target.value)} /> setPadding(Number(value) || 3)} /> Prefix Next number Digits Status Created {all.map((series) => ( {series.prefix} {series.nextInvoiceNumber} {series.padding} {series.isActive ? Active : Retired} {new Date(series.createdAt).toLocaleDateString("en-GB")} ))}
); }