mirror of
https://github.com/soconnor0919/beenvoice.git
synced 2026-05-08 09:38: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:
@@ -72,6 +72,11 @@ interface DataTableProps<TData, TValue> {
|
||||
options: { label: string; value: string }[];
|
||||
}[];
|
||||
onRowClick?: (row: TData) => void;
|
||||
/** Render bulk-action buttons when rows are selected. Receives selected rows and a clear function. */
|
||||
selectionActions?: (
|
||||
selectedRows: TData[],
|
||||
clearSelection: () => void,
|
||||
) => React.ReactNode;
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
@@ -89,6 +94,7 @@ export function DataTable<TData, TValue>({
|
||||
actions,
|
||||
filterableColumns = [],
|
||||
onRowClick,
|
||||
selectionActions,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
@@ -335,6 +341,23 @@ export function DataTable<TData, TValue>({
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Selection Toolbar */}
|
||||
{selectionActions && table.getSelectedRowModel().rows.length > 0 && (
|
||||
<Card className="bg-primary/5 border-primary/20 border py-2">
|
||||
<CardContent className="flex items-center justify-between gap-3 px-3 py-0">
|
||||
<span className="text-foreground text-sm font-medium">
|
||||
{table.getSelectedRowModel().rows.length} selected
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
{selectionActions(
|
||||
table.getSelectedRowModel().rows.map((r) => r.original),
|
||||
() => table.resetRowSelection(),
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Table Content Card */}
|
||||
<Card className="bg-card border-border overflow-hidden border p-0">
|
||||
<div className="w-full overflow-x-auto">
|
||||
|
||||
Reference in New Issue
Block a user