mirror of
https://github.com/soconnor0919/beenvoice.git
synced 2026-05-08 17:48:55 -04:00
Add bulk actions, multi-currency, expenses, templates, and reports
Schema (migration 0001): - clients: add currency column (default USD) - invoices: add currency column (default USD) - New expenses table: amount, currency, category, billable, reimbursable, client/invoice/business relations, notes - New invoice_templates table: name, type (notes|terms), content, isDefault API: - invoices: add bulkUpdateStatus and bulkDelete procedures (ownership-safe) - invoices: currency field threaded through create/update schemas - clients: currency field added to create/update schemas - New expenses router: full CRUD with authorization - New invoiceTemplates router: full CRUD, isDefault management per type - Root router: wire in expenses and invoiceTemplates Currency (src/lib/currency.ts): - Shared formatCurrency(amount, currency) utility replacing hardcoded USD - SUPPORTED_CURRENCIES list (17 currencies) - Invoice form: currency selector in Config card, auto-fills from client - Client form: currency selector in Billing Information card Bulk actions (invoices list): - Checkbox column with select-all support - Selection toolbar: Mark as Sent/Paid/Draft dropdown, Delete (N) button - DataTable: new selectionActions prop renders toolbar when rows selected Notes templates: - Invoice form: Notes card with textarea in Details tab - Template dropdown button appears when templates exist - /dashboard/invoices/templates: full CRUD page for notes and terms templates New pages: - /dashboard/expenses: expense list with summary cards, add/edit dialog - /dashboard/reports: KPI cards, 12-month revenue area chart, top clients bar chart, status breakdown, recent activity - Navigation: Expenses and Reports added to Main section https://claude.ai/code/session_012sqEgNQpx676isepeoX4Mi
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
export const SUPPORTED_CURRENCIES = [
|
||||
{ code: "USD", label: "USD – US Dollar" },
|
||||
{ code: "EUR", label: "EUR – Euro" },
|
||||
{ code: "GBP", label: "GBP – British Pound" },
|
||||
{ code: "CAD", label: "CAD – Canadian Dollar" },
|
||||
{ code: "AUD", label: "AUD – Australian Dollar" },
|
||||
{ code: "NZD", label: "NZD – New Zealand Dollar" },
|
||||
{ code: "CHF", label: "CHF – Swiss Franc" },
|
||||
{ code: "JPY", label: "JPY – Japanese Yen" },
|
||||
{ code: "SGD", label: "SGD – Singapore Dollar" },
|
||||
{ code: "HKD", label: "HKD – Hong Kong Dollar" },
|
||||
{ code: "SEK", label: "SEK – Swedish Krona" },
|
||||
{ code: "NOK", label: "NOK – Norwegian Krone" },
|
||||
{ code: "DKK", label: "DKK – Danish Krone" },
|
||||
{ code: "MXN", label: "MXN – Mexican Peso" },
|
||||
{ code: "BRL", label: "BRL – Brazilian Real" },
|
||||
{ code: "INR", label: "INR – Indian Rupee" },
|
||||
{ code: "ZAR", label: "ZAR – South African Rand" },
|
||||
] as const;
|
||||
|
||||
export type CurrencyCode = (typeof SUPPORTED_CURRENCIES)[number]["code"];
|
||||
|
||||
export function formatCurrency(amount: number, currency = "USD"): string {
|
||||
return new Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency,
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(amount);
|
||||
}
|
||||
Reference in New Issue
Block a user