@burdenoff/microfe-assethandler 2026.1111.1 → 2026.1111.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"FinanceFinanceHubPage.js","names":["PieChartIcon","_Fragment","CREATE_TRANSACTION_MUTATION","CREATE_BUDGET_MUTATION","CREATE_INVOICE_MUTATION","APPROVE_TRANSACTION_MUTATION","REJECT_TRANSACTION_MUTATION","TRANSACTION_TYPES","TRANSACTION_STATUSES","BUDGET_STATUSES","INVOICE_STATUSES","humanize","value","c","PILL_TONE","StatusPill","cn","tone","txnStatusTone","status","txnTypeTone","type","budgetStatusTone","invoiceStatusTone","formatDate","parsed","NewTransactionDialog","setType","useState","setStatus","amount","setAmount","moduleCurrency","useAssetMoney","currency","setCurrency","category","setCategory","description","setDescription","vendor","setVendor","department","setDepartment","reset","createTransaction","useMutation","notify","data","onOpenChange","error","ResponsiveDialog","ResponsiveDialogContent","ResponsiveDialogHeader","ResponsiveDialogTitle","ResponsiveDialogDescription","Label","Select","v","SelectTrigger","SelectValue","SelectContent","SelectItem","t","s","Input","Textarea","ResponsiveDialogFooter","Button","loading","parsedAmount","Loader2","NewBudgetDialog","name","setName","period","setPeriod","allocated","setAllocated","spent","setSpent","createBudget","parsedAllocated","parsedSpent","NewInvoiceDialog","number","setNumber","issueDate","setIssueDate","dueDate","setDueDate","relatedPO","setRelatedPO","createInvoice","TransactionActions","txn","busy","actingId","e","onApprove","Check","onReject","X","FinanceFinanceHubPage","compactUsd","fmtMoney","navigate","useAssetHandlerNavigate","useAssetHandlerStore","txnDialogOpen","setTxnDialogOpen","budgetDialogOpen","setBudgetDialogOpen","invoiceDialogOpen","setInvoiceDialogOpen","activeTab","setActiveTab","txnStatusFilter","setTxnStatusFilter","txnTypeFilter","setTxnTypeFilter","txnSearch","setTxnSearch","invStatusFilter","setInvStatusFilter","invSearch","setInvSearch","setActingId","totalSpend","transactions","pendingTxns","overBudget","budgets","b","overdueInvoices","invoices","i","budgetAllocated","budgetSpent","invoicesOutstanding","outstandingCount","budgetByDept","useMemo","map","key","cur","a","spendByCategory","hasCharts","filteredTxns","q","filteredInvoices","txnPage","usePagination","invPage","approveTransaction","rejectTransaction","handleApprove","id","handleReject","GradientText","CTAOverflowMenu","stamp","downloadCsv","Download","PagePurpose","NextSteps","StatTile","DollarSign","Wallet","TrendingDown","Receipt","ChartCard","BarChart3","BarsChart","PieChart","DonutChart","Tabs","TabsList","TabsTrigger","TabsContent","IllustratedEmptyState","Fragment","Search","GlassCard","Pagination","budget","ProgressMeter","inv"],"sources":["../../../src/assethandler/pages/FinanceFinanceHubPage.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from 'react';\nimport { useAssetMoney } from '../providers/AssetHandlerProvider';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport {\n Button,\n GlassCard,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n GradientText,\n CTAOverflowMenu,\n PagePurpose,\n NextSteps,\n IllustratedEmptyState,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n DollarSign,\n TrendingDown,\n Receipt,\n Wallet,\n Download,\n Loader2,\n Check,\n X,\n Search,\n BarChart3,\n PieChart as PieChartIcon,\n} from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport type { Budget, FinanceTransaction, Invoice } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport { cn } from '../utils/cn';\nimport { downloadCsv } from '../utils/csv';\nimport {\n StatTile,\n ChartCard,\n BarsChart,\n DonutChart,\n ProgressMeter,\n Pagination,\n usePagination,\n type DonutSlice,\n} from '../components';\n\n// ─── Mutations ────────────────────────────────────────────────────────────────\nexport const CREATE_TRANSACTION_MUTATION = gql`\n mutation AssetHandlerCreateTransaction($input: CreateAssetTransactionInput!) {\n createTransaction(input: $input) {\n id\n description\n }\n }\n`;\n\nconst CREATE_BUDGET_MUTATION = gql`\n mutation AssetHandlerCreateBudget($input: AssetHandlerCreateBudgetInput!) {\n createBudget(input: $input) {\n id\n name\n }\n }\n`;\n\nconst CREATE_INVOICE_MUTATION = gql`\n mutation AssetHandlerCreateInvoice($input: AssetHandlerCreateInvoiceInput!) {\n createInvoice(input: $input) {\n id\n number\n }\n }\n`;\n\nconst APPROVE_TRANSACTION_MUTATION = gql`\n mutation AssetHandlerHubApproveTransaction($id: ID!) {\n approveTransaction(id: $id) {\n id\n status\n }\n }\n`;\n\nconst REJECT_TRANSACTION_MUTATION = gql`\n mutation AssetHandlerHubRejectTransaction($id: ID!, $reason: String) {\n rejectTransaction(id: $id, reason: $reason) {\n id\n status\n }\n }\n`;\n\ninterface CreateTransactionResult {\n createTransaction: { id: string; description: string };\n}\ninterface CreateBudgetResult {\n createBudget: { id: string; name: string };\n}\ninterface CreateInvoiceResult {\n createInvoice: { id: string; number: string };\n}\ninterface ApproveTransactionResult {\n approveTransaction: { id: string; status: string };\n}\ninterface RejectTransactionResult {\n rejectTransaction: { id: string; status: string };\n}\n\nconst TRANSACTION_TYPES = ['PURCHASE', 'LEASE', 'DISPOSAL', 'TRANSFER', 'MAINTENANCE'] as const;\nconst TRANSACTION_STATUSES = ['PENDING', 'APPROVED', 'COMPLETED', 'REJECTED'] as const;\nconst BUDGET_STATUSES = ['ON_TRACK', 'OVER_BUDGET', 'UNDER_BUDGET'] as const;\nconst INVOICE_STATUSES = ['DRAFT', 'SENT', 'PAID', 'OVERDUE', 'CANCELLED'] as const;\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\n// ─── Presentation helpers ───────────────────────────────────────────────────\ntype Tone = 'success' | 'warning' | 'error' | 'info' | 'neutral';\n\nconst PILL_TONE: Record<Tone, string> = {\n success: 'bg-status-success-subtle text-status-success-text',\n warning: 'bg-status-warning-subtle text-status-warning-text',\n error: 'bg-status-error-subtle text-status-error-text',\n info: 'bg-status-info-subtle text-status-info-text',\n neutral: 'bg-bg-sunken text-text-secondary',\n};\n\nfunction StatusPill({ tone, children }: { tone: Tone; children: ReactNode }) {\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n PILL_TONE[tone]\n )}\n >\n {children}\n </span>\n );\n}\n\nconst txnStatusTone = (status: string): Tone =>\n status === 'approved' || status === 'completed'\n ? 'success'\n : status === 'rejected'\n ? 'error'\n : status === 'pending'\n ? 'warning'\n : 'neutral';\n\nconst txnTypeTone = (type: string): Tone =>\n type === 'purchase' || type === 'lease'\n ? 'info'\n : type === 'disposal'\n ? 'error'\n : type === 'maintenance'\n ? 'warning'\n : 'neutral';\n\nconst budgetStatusTone = (status: string): Tone =>\n status === 'over_budget' ? 'error' : status === 'under_budget' ? 'success' : 'info';\n\nconst invoiceStatusTone = (status: string): Tone =>\n status === 'paid'\n ? 'success'\n : status === 'overdue'\n ? 'error'\n : status === 'sent'\n ? 'warning'\n : 'neutral';\n\nconst formatDate = (value: string): string => {\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString();\n};\n\n// ─── New Transaction dialog ───────────────────────────────────────────────────\nfunction NewTransactionDialog({\n open,\n onOpenChange,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}) {\n const [type, setType] = useState<(typeof TRANSACTION_TYPES)[number]>('PURCHASE');\n const [status, setStatus] = useState<(typeof TRANSACTION_STATUSES)[number]>('PENDING');\n const [amount, setAmount] = useState('');\n const { currency: moduleCurrency } = useAssetMoney();\n const [currency, setCurrency] = useState(moduleCurrency);\n const [category, setCategory] = useState('');\n const [description, setDescription] = useState('');\n const [vendor, setVendor] = useState('');\n const [department, setDepartment] = useState('');\n\n const reset = () => {\n setType('PURCHASE');\n setStatus('PENDING');\n setAmount('');\n setCurrency(moduleCurrency);\n setCategory('');\n setDescription('');\n setVendor('');\n setDepartment('');\n };\n\n const [createTransaction, { loading }] = useMutation<CreateTransactionResult>(\n CREATE_TRANSACTION_MUTATION,\n {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success(\n 'Transaction created',\n `${data.createTransaction.description} was recorded.`\n );\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not create transaction', error.message),\n }\n );\n\n const handleSubmit = () => {\n const parsedAmount = Number(amount);\n if (!Number.isFinite(parsedAmount) || amount.trim() === '') {\n notify.warning('Invalid amount', 'Enter a numeric amount for the transaction.');\n return;\n }\n if (!category.trim() || !description.trim()) {\n notify.warning('Missing details', 'Category and description are required.');\n return;\n }\n void createTransaction({\n variables: {\n input: {\n amount: parsedAmount,\n category: category.trim(),\n currency: currency.trim() || moduleCurrency,\n description: description.trim(),\n type,\n status,\n vendor: vendor.trim() || null,\n department: department.trim() || null,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Transaction</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Record a workspace financial movement for the asset estate.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Type</Label>\n <Select value={type} onValueChange={(v) => setType(v as typeof type)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TRANSACTION_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {humanize(t)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select value={status} onValueChange={(v) => setStatus(v as typeof status)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TRANSACTION_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {humanize(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Amount *</Label>\n <Input\n type=\"number\"\n placeholder=\"e.g., 1500\"\n value={amount}\n onChange={(e) => setAmount(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Currency</Label>\n <Input value={currency} onChange={(e) => setCurrency(e.target.value)} />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label>Category *</Label>\n <Input\n placeholder=\"e.g., it_hardware\"\n value={category}\n onChange={(e) => setCategory(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Description *</Label>\n <Textarea\n placeholder=\"What is this transaction for?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Vendor</Label>\n <Input value={vendor} onChange={(e) => setVendor(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Department</Label>\n <Input value={department} onChange={(e) => setDepartment(e.target.value)} />\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={loading}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={loading}>\n {loading ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {loading ? 'Creating…' : 'Create Transaction'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── New Budget dialog ────────────────────────────────────────────────────────\nfunction NewBudgetDialog({\n open,\n onOpenChange,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}) {\n const [name, setName] = useState('');\n const [category, setCategory] = useState('');\n const [department, setDepartment] = useState('');\n const [period, setPeriod] = useState('');\n const [allocated, setAllocated] = useState('');\n const [spent, setSpent] = useState('0');\n const [status, setStatus] = useState<(typeof BUDGET_STATUSES)[number]>('ON_TRACK');\n\n const reset = () => {\n setName('');\n setCategory('');\n setDepartment('');\n setPeriod('');\n setAllocated('');\n setSpent('0');\n setStatus('ON_TRACK');\n };\n\n const [createBudget, { loading }] = useMutation<CreateBudgetResult>(CREATE_BUDGET_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Budget created', `${data.createBudget.name} is now being tracked.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not create budget', error.message),\n });\n\n const handleSubmit = () => {\n const parsedAllocated = Number(allocated);\n const parsedSpent = Number(spent);\n if (!name.trim() || !category.trim() || !department.trim() || !period.trim()) {\n notify.warning('Missing details', 'Name, category, department and period are required.');\n return;\n }\n if (!Number.isFinite(parsedAllocated) || allocated.trim() === '') {\n notify.warning('Invalid allocation', 'Enter a numeric allocated amount.');\n return;\n }\n void createBudget({\n variables: {\n input: {\n allocated: parsedAllocated,\n category: category.trim(),\n department: department.trim(),\n name: name.trim(),\n period: period.trim(),\n spent: Number.isFinite(parsedSpent) ? parsedSpent : 0,\n status,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Budget</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Set an allocation to track departmental asset spend.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label>Name *</Label>\n <Input\n placeholder=\"e.g., IT Hardware Q1\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Category *</Label>\n <Input value={category} onChange={(e) => setCategory(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Department *</Label>\n <Input value={department} onChange={(e) => setDepartment(e.target.value)} />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Period *</Label>\n <Input\n placeholder=\"e.g., Q1 2026\"\n value={period}\n onChange={(e) => setPeriod(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select value={status} onValueChange={(v) => setStatus(v as typeof status)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {BUDGET_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {humanize(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Allocated *</Label>\n <Input\n type=\"number\"\n placeholder=\"e.g., 50000\"\n value={allocated}\n onChange={(e) => setAllocated(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Spent</Label>\n <Input type=\"number\" value={spent} onChange={(e) => setSpent(e.target.value)} />\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={loading}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={loading}>\n {loading ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {loading ? 'Creating…' : 'Create Budget'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── New Invoice dialog ───────────────────────────────────────────────────────\nfunction NewInvoiceDialog({\n open,\n onOpenChange,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}) {\n const [number, setNumber] = useState('');\n const [vendor, setVendor] = useState('');\n const [amount, setAmount] = useState('');\n const { currency: moduleCurrency } = useAssetMoney();\n const [currency, setCurrency] = useState(moduleCurrency);\n const [issueDate, setIssueDate] = useState('');\n const [dueDate, setDueDate] = useState('');\n const [relatedPO, setRelatedPO] = useState('');\n const [status, setStatus] = useState<(typeof INVOICE_STATUSES)[number]>('DRAFT');\n\n const reset = () => {\n setNumber('');\n setVendor('');\n setAmount('');\n setCurrency(moduleCurrency);\n setIssueDate('');\n setDueDate('');\n setRelatedPO('');\n setStatus('DRAFT');\n };\n\n const [createInvoice, { loading }] = useMutation<CreateInvoiceResult>(CREATE_INVOICE_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Invoice created', `Invoice ${data.createInvoice.number} was added.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not create invoice', error.message),\n });\n\n const handleSubmit = () => {\n const parsedAmount = Number(amount);\n if (!number.trim() || !vendor.trim()) {\n notify.warning('Missing details', 'Invoice number and vendor are required.');\n return;\n }\n if (!Number.isFinite(parsedAmount) || amount.trim() === '') {\n notify.warning('Invalid amount', 'Enter a numeric invoice amount.');\n return;\n }\n if (!issueDate || !dueDate) {\n notify.warning('Missing dates', 'Issue date and due date are required.');\n return;\n }\n void createInvoice({\n variables: {\n input: {\n amount: parsedAmount,\n currency: currency.trim() || moduleCurrency,\n dueDate: new Date(dueDate).toISOString(),\n issueDate: new Date(issueDate).toISOString(),\n number: number.trim(),\n relatedPO: relatedPO.trim() || null,\n status,\n vendor: vendor.trim(),\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Invoice</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Log a workspace vendor invoice for an asset purchase or service.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Invoice Number *</Label>\n <Input\n placeholder=\"e.g., INV-2026-001\"\n value={number}\n onChange={(e) => setNumber(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Vendor *</Label>\n <Input value={vendor} onChange={(e) => setVendor(e.target.value)} />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Amount *</Label>\n <Input\n type=\"number\"\n placeholder=\"e.g., 12000\"\n value={amount}\n onChange={(e) => setAmount(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Currency</Label>\n <Input value={currency} onChange={(e) => setCurrency(e.target.value)} />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Issue Date *</Label>\n <Input type=\"date\" value={issueDate} onChange={(e) => setIssueDate(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Due Date *</Label>\n <Input type=\"date\" value={dueDate} onChange={(e) => setDueDate(e.target.value)} />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Related PO</Label>\n <Input value={relatedPO} onChange={(e) => setRelatedPO(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select value={status} onValueChange={(v) => setStatus(v as typeof status)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {INVOICE_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {humanize(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={loading}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={loading}>\n {loading ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {loading ? 'Creating…' : 'Create Invoice'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── Transaction row action buttons (approve / reject pending) ────────────────\nfunction TransactionActions({\n txn,\n actingId,\n onApprove,\n onReject,\n}: {\n txn: FinanceTransaction;\n actingId: string | null;\n onApprove: (id: string) => void;\n onReject: (id: string) => void;\n}) {\n if (txn.status !== 'pending') return null;\n const busy = actingId === txn.id;\n return (\n <div className=\"flex items-center gap-1.5\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 gap-1 border-status-success-subtle px-2 text-status-success-text\"\n disabled={busy}\n onClick={(e) => {\n e.stopPropagation();\n onApprove(txn.id);\n }}\n >\n {busy ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Check className=\"h-3.5 w-3.5\" />\n )}\n Approve\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 gap-1 border-status-error-subtle px-2 text-status-error-text\"\n disabled={busy}\n onClick={(e) => {\n e.stopPropagation();\n onReject(txn.id);\n }}\n >\n <X className=\"h-3.5 w-3.5\" />\n Reject\n </Button>\n </div>\n );\n}\n\nexport function FinanceFinanceHubPage() {\n const { currency: moduleCurrency, compact: compactUsd } = useAssetMoney();\n // A record's own currency wins; the module currency is the fallback for rows\n // that never carried one.\n const fmtMoney = (value: number, currency?: string | null): string => {\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: currency || moduleCurrency,\n maximumFractionDigits: 2,\n }).format(value);\n } catch {\n // An unknown/invalid ISO code: show the code rather than a wrong symbol.\n return `${currency || moduleCurrency} ${value.toLocaleString()}`;\n }\n };\n\n const navigate = useAssetHandlerNavigate();\n const { transactions, budgets, invoices } = useAssetHandlerStore();\n\n const [txnDialogOpen, setTxnDialogOpen] = useState(false);\n const [budgetDialogOpen, setBudgetDialogOpen] = useState(false);\n const [invoiceDialogOpen, setInvoiceDialogOpen] = useState(false);\n\n const [activeTab, setActiveTab] = useState<'transactions' | 'budgets' | 'invoices'>(\n 'transactions'\n );\n\n const [txnStatusFilter, setTxnStatusFilter] = useState<string>('all');\n const [txnTypeFilter, setTxnTypeFilter] = useState<string>('all');\n const [txnSearch, setTxnSearch] = useState('');\n const [invStatusFilter, setInvStatusFilter] = useState<string>('all');\n const [invSearch, setInvSearch] = useState('');\n\n const [actingId, setActingId] = useState<string | null>(null);\n\n // ─── Headline finance figures (money-forward, real store data) ───\n const totalSpend = transactions.filter((t) => t.amount > 0).reduce((s, t) => s + t.amount, 0);\n const pendingTxns = transactions.filter((t) => t.status === 'pending');\n const overBudget = budgets.filter((b) => b.status === 'over_budget');\n const overdueInvoices = invoices.filter((i) => i.status === 'overdue');\n const budgetAllocated = budgets.reduce((s, b) => s + b.allocated, 0);\n const budgetSpent = budgets.reduce((s, b) => s + b.spent, 0);\n const invoicesOutstanding = invoices\n .filter((i) => i.status !== 'paid' && i.status !== 'cancelled')\n .reduce((s, i) => s + i.amount, 0);\n const outstandingCount = invoices.filter(\n (i) => i.status !== 'paid' && i.status !== 'cancelled'\n ).length;\n\n // ─── Chart aggregations (real data only) ───\n const budgetByDept = useMemo(() => {\n const map = new Map<string, { allocated: number; spent: number }>();\n for (const b of budgets) {\n const key = b.department || 'Unassigned';\n const cur = map.get(key) ?? { allocated: 0, spent: 0 };\n cur.allocated += b.allocated;\n cur.spent += b.spent;\n map.set(key, cur);\n }\n return Array.from(map, ([department, v]) => ({ department, ...v })).sort(\n (a, b) => b.allocated - a.allocated\n );\n }, [budgets]);\n\n const spendByCategory = useMemo<DonutSlice[]>(() => {\n const map = new Map<string, number>();\n for (const t of transactions) {\n if (t.amount > 0) map.set(t.category, (map.get(t.category) ?? 0) + t.amount);\n }\n return Array.from(map, ([name, value]) => ({ name: humanize(name), value })).sort(\n (a, b) => b.value - a.value\n );\n }, [transactions]);\n\n const hasCharts = budgetByDept.length > 0 || spendByCategory.length > 0;\n\n // ─── Filtered lists ───\n const filteredTxns = useMemo(() => {\n const q = txnSearch.trim().toLowerCase();\n return transactions.filter((t) => {\n if (txnStatusFilter !== 'all' && t.status !== txnStatusFilter) return false;\n if (txnTypeFilter !== 'all' && t.type !== txnTypeFilter) return false;\n if (\n q &&\n !`${t.description} ${t.department} ${t.vendor ?? ''} ${t.id}`.toLowerCase().includes(q)\n )\n return false;\n return true;\n });\n }, [transactions, txnStatusFilter, txnTypeFilter, txnSearch]);\n\n const filteredInvoices = useMemo(() => {\n const q = invSearch.trim().toLowerCase();\n return invoices.filter((i) => {\n if (invStatusFilter !== 'all' && i.status !== invStatusFilter) return false;\n if (q && !`${i.number} ${i.vendor}`.toLowerCase().includes(q)) return false;\n return true;\n });\n }, [invoices, invStatusFilter, invSearch]);\n\n const txnPage = usePagination(filteredTxns, {\n pageSize: 10,\n resetKey: `${txnStatusFilter}|${txnTypeFilter}|${txnSearch}`,\n });\n const invPage = usePagination(filteredInvoices, {\n pageSize: 10,\n resetKey: `${invStatusFilter}|${invSearch}`,\n });\n\n // ─── Approve / reject ───\n const [approveTransaction] = useMutation<ApproveTransactionResult>(APPROVE_TRANSACTION_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Transaction approved', 'The transaction has been approved.');\n setActingId(null);\n },\n onError: (error) => {\n notify.error('Could not approve transaction', error.message);\n setActingId(null);\n },\n });\n const [rejectTransaction] = useMutation<RejectTransactionResult>(REJECT_TRANSACTION_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Transaction rejected', 'The transaction has been rejected.');\n setActingId(null);\n },\n onError: (error) => {\n notify.error('Could not reject transaction', error.message);\n setActingId(null);\n },\n });\n\n const handleApprove = (id: string) => {\n setActingId(id);\n void approveTransaction({ variables: { id } });\n };\n const handleReject = (id: string) => {\n setActingId(id);\n void rejectTransaction({ variables: { id, reason: null } });\n };\n\n // ─── Export the active tab ───\n const handleExport = () => {\n const stamp = new Date().toISOString().slice(0, 10);\n if (activeTab === 'budgets') {\n if (budgets.length === 0) {\n notify.warning('Nothing to export', 'There are no budgets to export yet.');\n return;\n }\n downloadCsv(\n `budgets-${stamp}`,\n ['Name', 'Department', 'Category', 'Period', 'Allocated', 'Spent', 'Remaining', 'Status'],\n budgets.map((b) => [\n b.name,\n b.department,\n b.category,\n b.period,\n b.allocated,\n b.spent,\n b.remaining,\n b.status,\n ])\n );\n return;\n }\n if (activeTab === 'invoices') {\n if (invoices.length === 0) {\n notify.warning('Nothing to export', 'There are no invoices to export yet.');\n return;\n }\n downloadCsv(\n `invoices-${stamp}`,\n ['Invoice #', 'Vendor', 'Amount', 'Currency', 'Issue Date', 'Due Date', 'Status'],\n invoices.map((i) => [\n i.number,\n i.vendor,\n i.amount,\n i.currency,\n i.issueDate,\n i.dueDate,\n i.status,\n ])\n );\n return;\n }\n if (transactions.length === 0) {\n notify.warning('Nothing to export', 'There are no transactions to export yet.');\n return;\n }\n downloadCsv(\n `transactions-${stamp}`,\n ['ID', 'Type', 'Description', 'Department', 'Amount', 'Currency', 'Status', 'Date'],\n transactions.map((t) => [\n t.id,\n t.type,\n t.description,\n t.department,\n t.amount,\n t.currency,\n t.status,\n t.date,\n ])\n );\n };\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold sm:text-3xl\">\n <GradientText>Finance Hub</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Workspace budgets, transactions, and supplier invoices\n </p>\n </div>\n <CTAOverflowMenu\n primary={{ label: 'New Transaction', onSelect: () => setTxnDialogOpen(true) }}\n actions={[\n { label: 'New Budget', onSelect: () => setBudgetDialogOpen(true) },\n { label: 'New Invoice', onSelect: () => setInvoiceDialogOpen(true) },\n { label: 'Export', onSelect: handleExport, icon: <Download className=\"h-4 w-4\" /> },\n ]}\n />\n </div>\n\n <NewTransactionDialog open={txnDialogOpen} onOpenChange={setTxnDialogOpen} />\n <NewBudgetDialog open={budgetDialogOpen} onOpenChange={setBudgetDialogOpen} />\n <NewInvoiceDialog open={invoiceDialogOpen} onOpenChange={setInvoiceDialogOpen} />\n\n <PagePurpose>\n The workspace money view for your asset estate: record transactions, maintain departmental\n budget totals, and stay on top of vendor invoices. Transactions do not automatically update\n budgets, and these records are not yet allocated to an individual asset.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"assethandler-finance-hub\"\n steps={[\n {\n id: 'transactions',\n label: 'Review recent transactions',\n description: 'Check spend and approve anything still pending.',\n onClick: () => navigate('/transactions/transaction-list'),\n },\n {\n id: 'budgets',\n label: 'Review reporting dashboard',\n description: 'See budget health and spend trends across departments.',\n onClick: () => navigate('/reports/reports-dashboard'),\n },\n {\n id: 'reports',\n label: 'Open financial summaries',\n description: 'Review the fixed live financial summary and CSV-ready registers.',\n onClick: () => navigate('/reports/reports-dashboard'),\n },\n ]}\n />\n\n {/* ─── Money-forward KPI row ─── */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n label=\"Total Spend\"\n value={totalSpend}\n format={(v) => fmtMoney(v)}\n icon={DollarSign}\n hero\n delta={`${pendingTxns.length} pending`}\n deltaTone=\"neutral\"\n />\n <StatTile\n label=\"Budget Allocated\"\n value={budgetAllocated}\n format={(v) => fmtMoney(v)}\n icon={Wallet}\n />\n <StatTile\n label=\"Budget Spent\"\n value={budgetSpent}\n format={(v) => fmtMoney(v)}\n icon={TrendingDown}\n delta={overBudget.length > 0 ? `${overBudget.length} over` : undefined}\n deltaTone=\"negative\"\n deltaDirection={overBudget.length > 0 ? 'up' : 'neutral'}\n />\n <StatTile\n label=\"Invoices Outstanding\"\n value={invoicesOutstanding}\n format={(v) => fmtMoney(v)}\n icon={Receipt}\n delta={\n overdueInvoices.length > 0\n ? `${overdueInvoices.length} overdue`\n : outstandingCount > 0\n ? `${outstandingCount} open`\n : undefined\n }\n deltaTone={overdueInvoices.length > 0 ? 'negative' : 'neutral'}\n deltaDirection=\"neutral\"\n />\n </div>\n\n {/* ─── Insights charts ─── */}\n {hasCharts ? (\n <div className=\"grid gap-4 lg:grid-cols-2\">\n {budgetByDept.length > 0 ? (\n <ChartCard\n title=\"Budget vs spend\"\n subtitle=\"Allocated against actual spend by department\"\n icon={BarChart3}\n >\n <BarsChart\n data={budgetByDept}\n xKey=\"department\"\n series={[\n { key: 'allocated', label: 'Allocated' },\n { key: 'spent', label: 'Spent' },\n ]}\n height={260}\n valueFormat={compactUsd}\n ariaLabel=\"Budget allocated versus spent by department\"\n />\n </ChartCard>\n ) : null}\n {spendByCategory.length > 0 ? (\n <ChartCard\n title=\"Spend by category\"\n subtitle=\"Recorded transaction spend, grouped by category\"\n icon={PieChartIcon}\n >\n <DonutChart\n data={spendByCategory}\n centerLabel=\"Total spend\"\n centerValue={totalSpend}\n valueFormat={(v) => fmtMoney(v)}\n height={260}\n ariaLabel=\"Transaction spend by category\"\n />\n </ChartCard>\n ) : null}\n </div>\n ) : null}\n\n <Tabs\n value={activeTab}\n onValueChange={(v) => setActiveTab(v as typeof activeTab)}\n className=\"space-y-4\"\n >\n <TabsList>\n <TabsTrigger value=\"transactions\">Transactions</TabsTrigger>\n <TabsTrigger value=\"budgets\">Budgets</TabsTrigger>\n <TabsTrigger value=\"invoices\">Invoices</TabsTrigger>\n </TabsList>\n\n {/* ─── Transactions ─── */}\n <TabsContent value=\"transactions\" className=\"space-y-4\">\n {transactions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No transactions yet\"\n description=\"Asset-related spend and reimbursements will appear here as they are recorded.\"\n />\n ) : (\n <>\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-muted\" />\n <Input\n value={txnSearch}\n onChange={(e) => setTxnSearch(e.target.value)}\n placeholder=\"Search description, vendor or department\"\n className=\"pl-9\"\n />\n </div>\n <Select value={txnStatusFilter} onValueChange={setTxnStatusFilter}>\n <SelectTrigger className=\"w-full sm:w-40\">\n <SelectValue placeholder=\"Status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n {TRANSACTION_STATUSES.map((s) => (\n <SelectItem key={s} value={s.toLowerCase()}>\n {humanize(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select value={txnTypeFilter} onValueChange={setTxnTypeFilter}>\n <SelectTrigger className=\"w-full sm:w-40\">\n <SelectValue placeholder=\"Type\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All types</SelectItem>\n {TRANSACTION_TYPES.map((t) => (\n <SelectItem key={t} value={t.toLowerCase()}>\n {humanize(t)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {filteredTxns.length === 0 ? (\n <GlassCard treatment=\"glass\" className=\"border-border-seam\">\n <p className=\"p-8 text-center text-sm text-text-secondary\">\n No transactions match your filters.\n </p>\n </GlassCard>\n ) : (\n <GlassCard treatment=\"glass\" className=\"overflow-hidden border-border-seam\">\n {/* Desktop table */}\n <div className=\"hidden overflow-x-auto md:block\">\n <table className=\"w-full min-w-[820px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken/40 text-left\">\n <th className=\"p-3 font-medium\">Type</th>\n <th className=\"p-3 font-medium\">Description</th>\n <th className=\"p-3 font-medium\">Department</th>\n <th className=\"p-3 text-right font-medium\">Amount</th>\n <th className=\"p-3 font-medium\">Status</th>\n <th className=\"p-3 font-medium\">Date</th>\n <th className=\"p-3 text-right font-medium\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {txnPage.pageItems.map((txn) => (\n <tr\n key={txn.id}\n className=\"cursor-pointer border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40\"\n onClick={() => navigate('/transactions/' + txn.id)}\n >\n <td className=\"p-3\">\n <StatusPill tone={txnTypeTone(txn.type)}>{txn.type}</StatusPill>\n </td>\n <td className=\"max-w-xs truncate p-3\" title={txn.description}>\n {txn.description}\n </td>\n <td className=\"p-3 text-xs text-text-secondary\">{txn.department}</td>\n <td\n className={cn(\n 'p-3 text-right font-medium tabular-nums',\n txn.amount < 0 && 'text-status-error-text'\n )}\n >\n {fmtMoney(Math.abs(txn.amount), txn.currency)}\n </td>\n <td className=\"p-3\">\n <StatusPill tone={txnStatusTone(txn.status)}>{txn.status}</StatusPill>\n </td>\n <td className=\"p-3 text-xs text-text-secondary\">\n {formatDate(txn.date)}\n </td>\n <td className=\"p-3\">\n <div className=\"flex justify-end\">\n <TransactionActions\n txn={txn}\n actingId={actingId}\n onApprove={handleApprove}\n onReject={handleReject}\n />\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {/* Mobile cards */}\n <div className=\"divide-y divide-border-seam/60 md:hidden\">\n {txnPage.pageItems.map((txn) => (\n <button\n key={txn.id}\n type=\"button\"\n className=\"block w-full p-4 text-left hover:bg-bg-sunken/40\"\n onClick={() => navigate('/transactions/' + txn.id)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"min-w-0 flex-1 truncate font-medium\">{txn.description}</p>\n <span\n className={cn(\n 'shrink-0 font-semibold tabular-nums',\n txn.amount < 0 && 'text-status-error-text'\n )}\n >\n {fmtMoney(Math.abs(txn.amount), txn.currency)}\n </span>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-2\">\n <StatusPill tone={txnTypeTone(txn.type)}>{txn.type}</StatusPill>\n <StatusPill tone={txnStatusTone(txn.status)}>{txn.status}</StatusPill>\n <span className=\"text-xs text-text-secondary\">\n {txn.department} · {formatDate(txn.date)}\n </span>\n </div>\n {txn.status === 'pending' ? (\n <div className=\"mt-3\">\n <TransactionActions\n txn={txn}\n actingId={actingId}\n onApprove={handleApprove}\n onReject={handleReject}\n />\n </div>\n ) : null}\n </button>\n ))}\n </div>\n\n <div className=\"px-3\">\n <Pagination\n page={txnPage.page}\n pageCount={txnPage.pageCount}\n rangeStart={txnPage.rangeStart}\n rangeEnd={txnPage.rangeEnd}\n totalItems={txnPage.totalItems}\n canPrev={txnPage.canPrev}\n canNext={txnPage.canNext}\n onPrev={txnPage.prevPage}\n onNext={txnPage.nextPage}\n itemLabel=\"transactions\"\n />\n </div>\n </GlassCard>\n )}\n </>\n )}\n </TabsContent>\n\n {/* ─── Budgets ─── */}\n <TabsContent value=\"budgets\" className=\"space-y-4\">\n {budgets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No budgets defined\"\n description=\"Create departmental budgets to track allocated vs. actual asset spend.\"\n />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2\">\n {budgets.map((budget: Budget) => (\n <GlassCard\n key={budget.id}\n treatment=\"glass\"\n className=\"cursor-pointer border-border-seam\"\n >\n <div\n className=\"p-5\"\n role=\"button\"\n tabIndex={0}\n onClick={() => navigate('/finance/budgets/' + budget.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ')\n navigate('/finance/budgets/' + budget.id);\n }}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate font-semibold text-text-primary\">{budget.name}</h3>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {budget.department} · {budget.period}\n </p>\n </div>\n <StatusPill tone={budgetStatusTone(budget.status)}>\n {budget.status.replace(/_/g, ' ')}\n </StatusPill>\n </div>\n <div className=\"mt-4 space-y-3\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Allocated</span>\n <span className=\"font-medium tabular-nums\">\n {fmtMoney(budget.allocated)}\n </span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Remaining</span>\n <span\n className={cn(\n 'font-medium tabular-nums',\n budget.remaining < 0\n ? 'text-status-error-text'\n : 'text-status-success-text'\n )}\n >\n {fmtMoney(budget.remaining)}\n </span>\n </div>\n <ProgressMeter\n value={budget.spent}\n max={budget.allocated}\n label=\"Spent\"\n format={(v) => fmtMoney(v)}\n thresholds={{ warning: 0.8, danger: 1 }}\n />\n </div>\n </div>\n </GlassCard>\n ))}\n </div>\n )}\n </TabsContent>\n\n {/* ─── Invoices ─── */}\n <TabsContent value=\"invoices\" className=\"space-y-4\">\n {invoices.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-files\"\n title=\"No invoices\"\n description=\"Vendor invoices for asset purchases and services will be listed here.\"\n />\n ) : (\n <>\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-muted\" />\n <Input\n value={invSearch}\n onChange={(e) => setInvSearch(e.target.value)}\n placeholder=\"Search invoice number or vendor\"\n className=\"pl-9\"\n />\n </div>\n <Select value={invStatusFilter} onValueChange={setInvStatusFilter}>\n <SelectTrigger className=\"w-full sm:w-40\">\n <SelectValue placeholder=\"Status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n {INVOICE_STATUSES.map((s) => (\n <SelectItem key={s} value={s.toLowerCase()}>\n {humanize(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {filteredInvoices.length === 0 ? (\n <GlassCard treatment=\"glass\" className=\"border-border-seam\">\n <p className=\"p-8 text-center text-sm text-text-secondary\">\n No invoices match your filters.\n </p>\n </GlassCard>\n ) : (\n <GlassCard treatment=\"glass\" className=\"overflow-hidden border-border-seam\">\n {/* Desktop table */}\n <div className=\"hidden overflow-x-auto md:block\">\n <table className=\"w-full min-w-[720px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken/40 text-left\">\n <th className=\"p-3 font-medium\">Invoice #</th>\n <th className=\"p-3 font-medium\">Vendor</th>\n <th className=\"p-3 text-right font-medium\">Amount</th>\n <th className=\"p-3 font-medium\">Issue Date</th>\n <th className=\"p-3 font-medium\">Due Date</th>\n <th className=\"p-3 font-medium\">Status</th>\n </tr>\n </thead>\n <tbody>\n {invPage.pageItems.map((inv: Invoice) => (\n <tr\n key={inv.id}\n className=\"cursor-pointer border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40\"\n onClick={() => navigate('/finance/invoices/' + inv.id)}\n >\n <td className=\"p-3 font-mono text-xs\">{inv.number}</td>\n <td className=\"p-3\">{inv.vendor}</td>\n <td className=\"p-3 text-right font-medium tabular-nums\">\n {fmtMoney(inv.amount, inv.currency)}\n </td>\n <td className=\"p-3 text-xs text-text-secondary\">\n {formatDate(inv.issueDate)}\n </td>\n <td className=\"p-3 text-xs text-text-secondary\">\n {formatDate(inv.dueDate)}\n </td>\n <td className=\"p-3\">\n <StatusPill tone={invoiceStatusTone(inv.status)}>\n {inv.status}\n </StatusPill>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {/* Mobile cards */}\n <div className=\"divide-y divide-border-seam/60 md:hidden\">\n {invPage.pageItems.map((inv: Invoice) => (\n <button\n key={inv.id}\n type=\"button\"\n className=\"block w-full p-4 text-left hover:bg-bg-sunken/40\"\n onClick={() => navigate('/finance/invoices/' + inv.id)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate font-mono text-xs text-text-secondary\">\n {inv.number}\n </p>\n <p className=\"truncate font-medium\">{inv.vendor}</p>\n </div>\n <span className=\"shrink-0 font-semibold tabular-nums\">\n {fmtMoney(inv.amount, inv.currency)}\n </span>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-2\">\n <StatusPill tone={invoiceStatusTone(inv.status)}>{inv.status}</StatusPill>\n <span className=\"text-xs text-text-secondary\">\n Due {formatDate(inv.dueDate)}\n </span>\n </div>\n </button>\n ))}\n </div>\n\n <div className=\"px-3\">\n <Pagination\n page={invPage.page}\n pageCount={invPage.pageCount}\n rangeStart={invPage.rangeStart}\n rangeEnd={invPage.rangeEnd}\n totalItems={invPage.totalItems}\n canPrev={invPage.canPrev}\n canNext={invPage.canNext}\n onPrev={invPage.prevPage}\n onNext={invPage.nextPage}\n itemLabel=\"invoices\"\n />\n </div>\n </GlassCard>\n )}\n </>\n )}\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FinanceFinanceHubPage.js","names":["PieChartIcon","_Fragment","CREATE_TRANSACTION_MUTATION","CREATE_BUDGET_MUTATION","CREATE_INVOICE_MUTATION","APPROVE_TRANSACTION_MUTATION","REJECT_TRANSACTION_MUTATION","TRANSACTION_TYPES","TRANSACTION_STATUSES","BUDGET_STATUSES","INVOICE_STATUSES","humanize","value","c","PILL_TONE","StatusPill","cn","tone","txnStatusTone","status","txnTypeTone","type","budgetStatusTone","invoiceStatusTone","formatDate","parsed","NewTransactionDialog","setType","useState","setStatus","amount","setAmount","moduleCurrency","useAssetMoney","currency","setCurrency","category","setCategory","description","setDescription","vendor","setVendor","department","setDepartment","reset","createTransaction","useMutation","notify","data","onOpenChange","error","ResponsiveDialog","ResponsiveDialogContent","ResponsiveDialogHeader","ResponsiveDialogTitle","ResponsiveDialogDescription","Label","Select","v","SelectTrigger","SelectValue","SelectContent","SelectItem","t","s","Input","Textarea","ResponsiveDialogFooter","Button","loading","parsedAmount","Loader2","NewBudgetDialog","name","setName","period","setPeriod","allocated","setAllocated","spent","setSpent","createBudget","parsedAllocated","parsedSpent","NewInvoiceDialog","number","setNumber","issueDate","setIssueDate","dueDate","setDueDate","relatedPO","setRelatedPO","createInvoice","TransactionActions","txn","busy","actingId","e","onApprove","Check","onReject","X","FinanceFinanceHubPage","compactUsd","fmtMoney","navigate","useAssetHandlerNavigate","useAssetHandlerStore","txnDialogOpen","setTxnDialogOpen","budgetDialogOpen","setBudgetDialogOpen","invoiceDialogOpen","setInvoiceDialogOpen","exportOpen","setExportOpen","activeTab","setActiveTab","txnStatusFilter","setTxnStatusFilter","txnTypeFilter","setTxnTypeFilter","txnSearch","setTxnSearch","invStatusFilter","setInvStatusFilter","invSearch","setInvSearch","setActingId","totalSpend","transactions","pendingTxns","overBudget","budgets","b","overdueInvoices","invoices","i","budgetAllocated","budgetSpent","invoicesOutstanding","outstandingCount","budgetByDept","useMemo","map","key","cur","a","spendByCategory","hasCharts","filteredTxns","q","filteredInvoices","txnPage","usePagination","invPage","approveTransaction","rejectTransaction","handleApprove","id","handleReject","GradientText","CTAOverflowMenu","Download","FinanceRegisterExportDialog","PagePurpose","NextSteps","StatTile","DollarSign","Wallet","TrendingDown","Receipt","ChartCard","BarChart3","BarsChart","PieChart","DonutChart","Tabs","TabsList","TabsTrigger","TabsContent","IllustratedEmptyState","Fragment","Search","GlassCard","Pagination","budget","ProgressMeter","inv"],"sources":["../../../src/assethandler/pages/FinanceFinanceHubPage.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from 'react';\nimport { useAssetMoney } from '../providers/AssetHandlerProvider';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport {\n Button,\n GlassCard,\n Input,\n Label,\n Textarea,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n GradientText,\n CTAOverflowMenu,\n PagePurpose,\n NextSteps,\n IllustratedEmptyState,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n DollarSign,\n TrendingDown,\n Receipt,\n Wallet,\n Download,\n Loader2,\n Check,\n X,\n Search,\n BarChart3,\n PieChart as PieChartIcon,\n} from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport type { Budget, FinanceTransaction, Invoice } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\nimport { cn } from '../utils/cn';\nimport { FinanceRegisterExportDialog } from '../components/FinanceRegisterExportDialog';\nimport {\n StatTile,\n ChartCard,\n BarsChart,\n DonutChart,\n ProgressMeter,\n Pagination,\n usePagination,\n type DonutSlice,\n} from '../components';\n\n// ─── Mutations ────────────────────────────────────────────────────────────────\nexport const CREATE_TRANSACTION_MUTATION = gql`\n mutation AssetHandlerCreateTransaction($input: CreateAssetTransactionInput!) {\n createTransaction(input: $input) {\n id\n description\n }\n }\n`;\n\nconst CREATE_BUDGET_MUTATION = gql`\n mutation AssetHandlerCreateBudget($input: AssetHandlerCreateBudgetInput!) {\n createBudget(input: $input) {\n id\n name\n }\n }\n`;\n\nconst CREATE_INVOICE_MUTATION = gql`\n mutation AssetHandlerCreateInvoice($input: AssetHandlerCreateInvoiceInput!) {\n createInvoice(input: $input) {\n id\n number\n }\n }\n`;\n\nconst APPROVE_TRANSACTION_MUTATION = gql`\n mutation AssetHandlerHubApproveTransaction($id: ID!) {\n approveTransaction(id: $id) {\n id\n status\n }\n }\n`;\n\nconst REJECT_TRANSACTION_MUTATION = gql`\n mutation AssetHandlerHubRejectTransaction($id: ID!, $reason: String) {\n rejectTransaction(id: $id, reason: $reason) {\n id\n status\n }\n }\n`;\n\ninterface CreateTransactionResult {\n createTransaction: { id: string; description: string };\n}\ninterface CreateBudgetResult {\n createBudget: { id: string; name: string };\n}\ninterface CreateInvoiceResult {\n createInvoice: { id: string; number: string };\n}\ninterface ApproveTransactionResult {\n approveTransaction: { id: string; status: string };\n}\ninterface RejectTransactionResult {\n rejectTransaction: { id: string; status: string };\n}\n\nconst TRANSACTION_TYPES = ['PURCHASE', 'LEASE', 'DISPOSAL', 'TRANSFER', 'MAINTENANCE'] as const;\nconst TRANSACTION_STATUSES = ['PENDING', 'APPROVED', 'COMPLETED', 'REJECTED'] as const;\nconst BUDGET_STATUSES = ['ON_TRACK', 'OVER_BUDGET', 'UNDER_BUDGET'] as const;\nconst INVOICE_STATUSES = ['DRAFT', 'SENT', 'PAID', 'OVERDUE', 'CANCELLED'] as const;\n\nconst humanize = (value: string) =>\n value\n .toLowerCase()\n .replace(/_/g, ' ')\n .replace(/^\\w/, (c) => c.toUpperCase());\n\n// ─── Presentation helpers ───────────────────────────────────────────────────\ntype Tone = 'success' | 'warning' | 'error' | 'info' | 'neutral';\n\nconst PILL_TONE: Record<Tone, string> = {\n success: 'bg-status-success-subtle text-status-success-text',\n warning: 'bg-status-warning-subtle text-status-warning-text',\n error: 'bg-status-error-subtle text-status-error-text',\n info: 'bg-status-info-subtle text-status-info-text',\n neutral: 'bg-bg-sunken text-text-secondary',\n};\n\nfunction StatusPill({ tone, children }: { tone: Tone; children: ReactNode }) {\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n PILL_TONE[tone]\n )}\n >\n {children}\n </span>\n );\n}\n\nconst txnStatusTone = (status: string): Tone =>\n status === 'approved' || status === 'completed'\n ? 'success'\n : status === 'rejected'\n ? 'error'\n : status === 'pending'\n ? 'warning'\n : 'neutral';\n\nconst txnTypeTone = (type: string): Tone =>\n type === 'purchase' || type === 'lease'\n ? 'info'\n : type === 'disposal'\n ? 'error'\n : type === 'maintenance'\n ? 'warning'\n : 'neutral';\n\nconst budgetStatusTone = (status: string): Tone =>\n status === 'over_budget' ? 'error' : status === 'under_budget' ? 'success' : 'info';\n\nconst invoiceStatusTone = (status: string): Tone =>\n status === 'paid'\n ? 'success'\n : status === 'overdue'\n ? 'error'\n : status === 'sent'\n ? 'warning'\n : 'neutral';\n\nconst formatDate = (value: string): string => {\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString();\n};\n\n// ─── New Transaction dialog ───────────────────────────────────────────────────\nfunction NewTransactionDialog({\n open,\n onOpenChange,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}) {\n const [type, setType] = useState<(typeof TRANSACTION_TYPES)[number]>('PURCHASE');\n const [status, setStatus] = useState<(typeof TRANSACTION_STATUSES)[number]>('PENDING');\n const [amount, setAmount] = useState('');\n const { currency: moduleCurrency } = useAssetMoney();\n const [currency, setCurrency] = useState(moduleCurrency);\n const [category, setCategory] = useState('');\n const [description, setDescription] = useState('');\n const [vendor, setVendor] = useState('');\n const [department, setDepartment] = useState('');\n\n const reset = () => {\n setType('PURCHASE');\n setStatus('PENDING');\n setAmount('');\n setCurrency(moduleCurrency);\n setCategory('');\n setDescription('');\n setVendor('');\n setDepartment('');\n };\n\n const [createTransaction, { loading }] = useMutation<CreateTransactionResult>(\n CREATE_TRANSACTION_MUTATION,\n {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success(\n 'Transaction created',\n `${data.createTransaction.description} was recorded.`\n );\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not create transaction', error.message),\n }\n );\n\n const handleSubmit = () => {\n const parsedAmount = Number(amount);\n if (!Number.isFinite(parsedAmount) || amount.trim() === '') {\n notify.warning('Invalid amount', 'Enter a numeric amount for the transaction.');\n return;\n }\n if (!category.trim() || !description.trim()) {\n notify.warning('Missing details', 'Category and description are required.');\n return;\n }\n void createTransaction({\n variables: {\n input: {\n amount: parsedAmount,\n category: category.trim(),\n currency: currency.trim() || moduleCurrency,\n description: description.trim(),\n type,\n status,\n vendor: vendor.trim() || null,\n department: department.trim() || null,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Transaction</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Record a workspace financial movement for the asset estate.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Type</Label>\n <Select value={type} onValueChange={(v) => setType(v as typeof type)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TRANSACTION_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {humanize(t)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select value={status} onValueChange={(v) => setStatus(v as typeof status)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TRANSACTION_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {humanize(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Amount *</Label>\n <Input\n type=\"number\"\n placeholder=\"e.g., 1500\"\n value={amount}\n onChange={(e) => setAmount(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Currency</Label>\n <Input value={currency} onChange={(e) => setCurrency(e.target.value)} />\n </div>\n </div>\n <div className=\"space-y-2\">\n <Label>Category *</Label>\n <Input\n placeholder=\"e.g., it_hardware\"\n value={category}\n onChange={(e) => setCategory(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Description *</Label>\n <Textarea\n placeholder=\"What is this transaction for?\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Vendor</Label>\n <Input value={vendor} onChange={(e) => setVendor(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Department</Label>\n <Input value={department} onChange={(e) => setDepartment(e.target.value)} />\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={loading}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={loading}>\n {loading ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {loading ? 'Creating…' : 'Create Transaction'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── New Budget dialog ────────────────────────────────────────────────────────\nfunction NewBudgetDialog({\n open,\n onOpenChange,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}) {\n const [name, setName] = useState('');\n const [category, setCategory] = useState('');\n const [department, setDepartment] = useState('');\n const [period, setPeriod] = useState('');\n const [allocated, setAllocated] = useState('');\n const [spent, setSpent] = useState('0');\n const [status, setStatus] = useState<(typeof BUDGET_STATUSES)[number]>('ON_TRACK');\n\n const reset = () => {\n setName('');\n setCategory('');\n setDepartment('');\n setPeriod('');\n setAllocated('');\n setSpent('0');\n setStatus('ON_TRACK');\n };\n\n const [createBudget, { loading }] = useMutation<CreateBudgetResult>(CREATE_BUDGET_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Budget created', `${data.createBudget.name} is now being tracked.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not create budget', error.message),\n });\n\n const handleSubmit = () => {\n const parsedAllocated = Number(allocated);\n const parsedSpent = Number(spent);\n if (!name.trim() || !category.trim() || !department.trim() || !period.trim()) {\n notify.warning('Missing details', 'Name, category, department and period are required.');\n return;\n }\n if (!Number.isFinite(parsedAllocated) || allocated.trim() === '') {\n notify.warning('Invalid allocation', 'Enter a numeric allocated amount.');\n return;\n }\n void createBudget({\n variables: {\n input: {\n allocated: parsedAllocated,\n category: category.trim(),\n department: department.trim(),\n name: name.trim(),\n period: period.trim(),\n spent: Number.isFinite(parsedSpent) ? parsedSpent : 0,\n status,\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Budget</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Set an allocation to track departmental asset spend.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label>Name *</Label>\n <Input\n placeholder=\"e.g., IT Hardware Q1\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n />\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Category *</Label>\n <Input value={category} onChange={(e) => setCategory(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Department *</Label>\n <Input value={department} onChange={(e) => setDepartment(e.target.value)} />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Period *</Label>\n <Input\n placeholder=\"e.g., Q1 2026\"\n value={period}\n onChange={(e) => setPeriod(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select value={status} onValueChange={(v) => setStatus(v as typeof status)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {BUDGET_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {humanize(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Allocated *</Label>\n <Input\n type=\"number\"\n placeholder=\"e.g., 50000\"\n value={allocated}\n onChange={(e) => setAllocated(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Spent</Label>\n <Input type=\"number\" value={spent} onChange={(e) => setSpent(e.target.value)} />\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={loading}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={loading}>\n {loading ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {loading ? 'Creating…' : 'Create Budget'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── New Invoice dialog ───────────────────────────────────────────────────────\nfunction NewInvoiceDialog({\n open,\n onOpenChange,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}) {\n const [number, setNumber] = useState('');\n const [vendor, setVendor] = useState('');\n const [amount, setAmount] = useState('');\n const { currency: moduleCurrency } = useAssetMoney();\n const [currency, setCurrency] = useState(moduleCurrency);\n const [issueDate, setIssueDate] = useState('');\n const [dueDate, setDueDate] = useState('');\n const [relatedPO, setRelatedPO] = useState('');\n const [status, setStatus] = useState<(typeof INVOICE_STATUSES)[number]>('DRAFT');\n\n const reset = () => {\n setNumber('');\n setVendor('');\n setAmount('');\n setCurrency(moduleCurrency);\n setIssueDate('');\n setDueDate('');\n setRelatedPO('');\n setStatus('DRAFT');\n };\n\n const [createInvoice, { loading }] = useMutation<CreateInvoiceResult>(CREATE_INVOICE_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: (data) => {\n notify.success('Invoice created', `Invoice ${data.createInvoice.number} was added.`);\n reset();\n onOpenChange(false);\n },\n onError: (error) => notify.error('Could not create invoice', error.message),\n });\n\n const handleSubmit = () => {\n const parsedAmount = Number(amount);\n if (!number.trim() || !vendor.trim()) {\n notify.warning('Missing details', 'Invoice number and vendor are required.');\n return;\n }\n if (!Number.isFinite(parsedAmount) || amount.trim() === '') {\n notify.warning('Invalid amount', 'Enter a numeric invoice amount.');\n return;\n }\n if (!issueDate || !dueDate) {\n notify.warning('Missing dates', 'Issue date and due date are required.');\n return;\n }\n void createInvoice({\n variables: {\n input: {\n amount: parsedAmount,\n currency: currency.trim() || moduleCurrency,\n dueDate: new Date(dueDate).toISOString(),\n issueDate: new Date(issueDate).toISOString(),\n number: number.trim(),\n relatedPO: relatedPO.trim() || null,\n status,\n vendor: vendor.trim(),\n },\n },\n });\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New Invoice</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Log a workspace vendor invoice for an asset purchase or service.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Invoice Number *</Label>\n <Input\n placeholder=\"e.g., INV-2026-001\"\n value={number}\n onChange={(e) => setNumber(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Vendor *</Label>\n <Input value={vendor} onChange={(e) => setVendor(e.target.value)} />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Amount *</Label>\n <Input\n type=\"number\"\n placeholder=\"e.g., 12000\"\n value={amount}\n onChange={(e) => setAmount(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Currency</Label>\n <Input value={currency} onChange={(e) => setCurrency(e.target.value)} />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Issue Date *</Label>\n <Input type=\"date\" value={issueDate} onChange={(e) => setIssueDate(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Due Date *</Label>\n <Input type=\"date\" value={dueDate} onChange={(e) => setDueDate(e.target.value)} />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Related PO</Label>\n <Input value={relatedPO} onChange={(e) => setRelatedPO(e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select value={status} onValueChange={(v) => setStatus(v as typeof status)}>\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {INVOICE_STATUSES.map((s) => (\n <SelectItem key={s} value={s}>\n {humanize(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={loading}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={loading}>\n {loading ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {loading ? 'Creating…' : 'Create Invoice'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ─── Transaction row action buttons (approve / reject pending) ────────────────\nfunction TransactionActions({\n txn,\n actingId,\n onApprove,\n onReject,\n}: {\n txn: FinanceTransaction;\n actingId: string | null;\n onApprove: (id: string) => void;\n onReject: (id: string) => void;\n}) {\n if (txn.status !== 'pending') return null;\n const busy = actingId === txn.id;\n return (\n <div className=\"flex items-center gap-1.5\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 gap-1 border-status-success-subtle px-2 text-status-success-text\"\n disabled={busy}\n onClick={(e) => {\n e.stopPropagation();\n onApprove(txn.id);\n }}\n >\n {busy ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Check className=\"h-3.5 w-3.5\" />\n )}\n Approve\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-7 gap-1 border-status-error-subtle px-2 text-status-error-text\"\n disabled={busy}\n onClick={(e) => {\n e.stopPropagation();\n onReject(txn.id);\n }}\n >\n <X className=\"h-3.5 w-3.5\" />\n Reject\n </Button>\n </div>\n );\n}\n\nexport function FinanceFinanceHubPage() {\n const { currency: moduleCurrency, compact: compactUsd } = useAssetMoney();\n // A record's own currency wins; the module currency is the fallback for rows\n // that never carried one.\n const fmtMoney = (value: number, currency?: string | null): string => {\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: currency || moduleCurrency,\n maximumFractionDigits: 2,\n }).format(value);\n } catch {\n // An unknown/invalid ISO code: show the code rather than a wrong symbol.\n return `${currency || moduleCurrency} ${value.toLocaleString()}`;\n }\n };\n\n const navigate = useAssetHandlerNavigate();\n const { transactions, budgets, invoices } = useAssetHandlerStore();\n\n const [txnDialogOpen, setTxnDialogOpen] = useState(false);\n const [budgetDialogOpen, setBudgetDialogOpen] = useState(false);\n const [invoiceDialogOpen, setInvoiceDialogOpen] = useState(false);\n const [exportOpen, setExportOpen] = useState(false);\n\n const [activeTab, setActiveTab] = useState<'transactions' | 'budgets' | 'invoices'>(\n 'transactions'\n );\n\n const [txnStatusFilter, setTxnStatusFilter] = useState<string>('all');\n const [txnTypeFilter, setTxnTypeFilter] = useState<string>('all');\n const [txnSearch, setTxnSearch] = useState('');\n const [invStatusFilter, setInvStatusFilter] = useState<string>('all');\n const [invSearch, setInvSearch] = useState('');\n\n const [actingId, setActingId] = useState<string | null>(null);\n\n // ─── Headline finance figures (money-forward, real store data) ───\n const totalSpend = transactions.filter((t) => t.amount > 0).reduce((s, t) => s + t.amount, 0);\n const pendingTxns = transactions.filter((t) => t.status === 'pending');\n const overBudget = budgets.filter((b) => b.status === 'over_budget');\n const overdueInvoices = invoices.filter((i) => i.status === 'overdue');\n const budgetAllocated = budgets.reduce((s, b) => s + b.allocated, 0);\n const budgetSpent = budgets.reduce((s, b) => s + b.spent, 0);\n const invoicesOutstanding = invoices\n .filter((i) => i.status !== 'paid' && i.status !== 'cancelled')\n .reduce((s, i) => s + i.amount, 0);\n const outstandingCount = invoices.filter(\n (i) => i.status !== 'paid' && i.status !== 'cancelled'\n ).length;\n\n // ─── Chart aggregations (real data only) ───\n const budgetByDept = useMemo(() => {\n const map = new Map<string, { allocated: number; spent: number }>();\n for (const b of budgets) {\n const key = b.department || 'Unassigned';\n const cur = map.get(key) ?? { allocated: 0, spent: 0 };\n cur.allocated += b.allocated;\n cur.spent += b.spent;\n map.set(key, cur);\n }\n return Array.from(map, ([department, v]) => ({ department, ...v })).sort(\n (a, b) => b.allocated - a.allocated\n );\n }, [budgets]);\n\n const spendByCategory = useMemo<DonutSlice[]>(() => {\n const map = new Map<string, number>();\n for (const t of transactions) {\n if (t.amount > 0) map.set(t.category, (map.get(t.category) ?? 0) + t.amount);\n }\n return Array.from(map, ([name, value]) => ({ name: humanize(name), value })).sort(\n (a, b) => b.value - a.value\n );\n }, [transactions]);\n\n const hasCharts = budgetByDept.length > 0 || spendByCategory.length > 0;\n\n // ─── Filtered lists ───\n const filteredTxns = useMemo(() => {\n const q = txnSearch.trim().toLowerCase();\n return transactions.filter((t) => {\n if (txnStatusFilter !== 'all' && t.status !== txnStatusFilter) return false;\n if (txnTypeFilter !== 'all' && t.type !== txnTypeFilter) return false;\n if (\n q &&\n !`${t.description} ${t.department} ${t.vendor ?? ''} ${t.id}`.toLowerCase().includes(q)\n )\n return false;\n return true;\n });\n }, [transactions, txnStatusFilter, txnTypeFilter, txnSearch]);\n\n const filteredInvoices = useMemo(() => {\n const q = invSearch.trim().toLowerCase();\n return invoices.filter((i) => {\n if (invStatusFilter !== 'all' && i.status !== invStatusFilter) return false;\n if (q && !`${i.number} ${i.vendor}`.toLowerCase().includes(q)) return false;\n return true;\n });\n }, [invoices, invStatusFilter, invSearch]);\n\n const txnPage = usePagination(filteredTxns, {\n pageSize: 10,\n resetKey: `${txnStatusFilter}|${txnTypeFilter}|${txnSearch}`,\n });\n const invPage = usePagination(filteredInvoices, {\n pageSize: 10,\n resetKey: `${invStatusFilter}|${invSearch}`,\n });\n\n // ─── Approve / reject ───\n const [approveTransaction] = useMutation<ApproveTransactionResult>(APPROVE_TRANSACTION_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Transaction approved', 'The transaction has been approved.');\n setActingId(null);\n },\n onError: (error) => {\n notify.error('Could not approve transaction', error.message);\n setActingId(null);\n },\n });\n const [rejectTransaction] = useMutation<RejectTransactionResult>(REJECT_TRANSACTION_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Transaction rejected', 'The transaction has been rejected.');\n setActingId(null);\n },\n onError: (error) => {\n notify.error('Could not reject transaction', error.message);\n setActingId(null);\n },\n });\n\n const handleApprove = (id: string) => {\n setActingId(id);\n void approveTransaction({ variables: { id } });\n };\n const handleReject = (id: string) => {\n setActingId(id);\n void rejectTransaction({ variables: { id, reason: null } });\n };\n\n return (\n <div className=\"space-y-6 p-4 sm:p-6\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold sm:text-3xl\">\n <GradientText>Finance Hub</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Workspace budgets, transactions, and supplier invoices\n </p>\n </div>\n <CTAOverflowMenu\n primary={{ label: 'New Transaction', onSelect: () => setTxnDialogOpen(true) }}\n actions={[\n { label: 'New Budget', onSelect: () => setBudgetDialogOpen(true) },\n { label: 'New Invoice', onSelect: () => setInvoiceDialogOpen(true) },\n {\n label: 'Export',\n onSelect: () => setExportOpen(true),\n icon: <Download className=\"h-4 w-4\" />,\n },\n ]}\n />\n </div>\n\n <NewTransactionDialog open={txnDialogOpen} onOpenChange={setTxnDialogOpen} />\n <NewBudgetDialog open={budgetDialogOpen} onOpenChange={setBudgetDialogOpen} />\n <NewInvoiceDialog open={invoiceDialogOpen} onOpenChange={setInvoiceDialogOpen} />\n <FinanceRegisterExportDialog\n open={exportOpen}\n onOpenChange={setExportOpen}\n register={activeTab}\n />\n\n <PagePurpose>\n The workspace money view for your asset estate: record transactions, maintain departmental\n budget totals, and stay on top of vendor invoices. Transactions do not automatically update\n budgets, and these records are not yet allocated to an individual asset.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"assethandler-finance-hub\"\n steps={[\n {\n id: 'transactions',\n label: 'Review recent transactions',\n description: 'Check spend and approve anything still pending.',\n onClick: () => navigate('/transactions/transaction-list'),\n },\n {\n id: 'budgets',\n label: 'Review reporting dashboard',\n description: 'See budget health and spend trends across departments.',\n onClick: () => navigate('/reports/reports-dashboard'),\n },\n {\n id: 'reports',\n label: 'Open financial summaries',\n description: 'Review the fixed live financial summary and CSV-ready registers.',\n onClick: () => navigate('/reports/reports-dashboard'),\n },\n ]}\n />\n\n {/* ─── Money-forward KPI row ─── */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatTile\n label=\"Total Spend\"\n value={totalSpend}\n format={(v) => fmtMoney(v)}\n icon={DollarSign}\n hero\n delta={`${pendingTxns.length} pending`}\n deltaTone=\"neutral\"\n />\n <StatTile\n label=\"Budget Allocated\"\n value={budgetAllocated}\n format={(v) => fmtMoney(v)}\n icon={Wallet}\n />\n <StatTile\n label=\"Budget Spent\"\n value={budgetSpent}\n format={(v) => fmtMoney(v)}\n icon={TrendingDown}\n delta={overBudget.length > 0 ? `${overBudget.length} over` : undefined}\n deltaTone=\"negative\"\n deltaDirection={overBudget.length > 0 ? 'up' : 'neutral'}\n />\n <StatTile\n label=\"Invoices Outstanding\"\n value={invoicesOutstanding}\n format={(v) => fmtMoney(v)}\n icon={Receipt}\n delta={\n overdueInvoices.length > 0\n ? `${overdueInvoices.length} overdue`\n : outstandingCount > 0\n ? `${outstandingCount} open`\n : undefined\n }\n deltaTone={overdueInvoices.length > 0 ? 'negative' : 'neutral'}\n deltaDirection=\"neutral\"\n />\n </div>\n\n {/* ─── Insights charts ─── */}\n {hasCharts ? (\n <div className=\"grid gap-4 lg:grid-cols-2\">\n {budgetByDept.length > 0 ? (\n <ChartCard\n title=\"Budget vs spend\"\n subtitle=\"Allocated against actual spend by department\"\n icon={BarChart3}\n >\n <BarsChart\n data={budgetByDept}\n xKey=\"department\"\n series={[\n { key: 'allocated', label: 'Allocated' },\n { key: 'spent', label: 'Spent' },\n ]}\n height={260}\n valueFormat={compactUsd}\n ariaLabel=\"Budget allocated versus spent by department\"\n />\n </ChartCard>\n ) : null}\n {spendByCategory.length > 0 ? (\n <ChartCard\n title=\"Spend by category\"\n subtitle=\"Recorded transaction spend, grouped by category\"\n icon={PieChartIcon}\n >\n <DonutChart\n data={spendByCategory}\n centerLabel=\"Total spend\"\n centerValue={totalSpend}\n valueFormat={(v) => fmtMoney(v)}\n height={260}\n ariaLabel=\"Transaction spend by category\"\n />\n </ChartCard>\n ) : null}\n </div>\n ) : null}\n\n <Tabs\n value={activeTab}\n onValueChange={(v) => setActiveTab(v as typeof activeTab)}\n className=\"space-y-4\"\n >\n <TabsList>\n <TabsTrigger value=\"transactions\">Transactions</TabsTrigger>\n <TabsTrigger value=\"budgets\">Budgets</TabsTrigger>\n <TabsTrigger value=\"invoices\">Invoices</TabsTrigger>\n </TabsList>\n\n {/* ─── Transactions ─── */}\n <TabsContent value=\"transactions\" className=\"space-y-4\">\n {transactions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No transactions yet\"\n description=\"Asset-related spend and reimbursements will appear here as they are recorded.\"\n />\n ) : (\n <>\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-muted\" />\n <Input\n value={txnSearch}\n onChange={(e) => setTxnSearch(e.target.value)}\n placeholder=\"Search description, vendor or department\"\n className=\"pl-9\"\n />\n </div>\n <Select value={txnStatusFilter} onValueChange={setTxnStatusFilter}>\n <SelectTrigger className=\"w-full sm:w-40\">\n <SelectValue placeholder=\"Status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n {TRANSACTION_STATUSES.map((s) => (\n <SelectItem key={s} value={s.toLowerCase()}>\n {humanize(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select value={txnTypeFilter} onValueChange={setTxnTypeFilter}>\n <SelectTrigger className=\"w-full sm:w-40\">\n <SelectValue placeholder=\"Type\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All types</SelectItem>\n {TRANSACTION_TYPES.map((t) => (\n <SelectItem key={t} value={t.toLowerCase()}>\n {humanize(t)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {filteredTxns.length === 0 ? (\n <GlassCard treatment=\"glass\" className=\"border-border-seam\">\n <p className=\"p-8 text-center text-sm text-text-secondary\">\n No transactions match your filters.\n </p>\n </GlassCard>\n ) : (\n <GlassCard treatment=\"glass\" className=\"overflow-hidden border-border-seam\">\n {/* Desktop table */}\n <div className=\"hidden overflow-x-auto md:block\">\n <table className=\"w-full min-w-[820px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken/40 text-left\">\n <th className=\"p-3 font-medium\">Type</th>\n <th className=\"p-3 font-medium\">Description</th>\n <th className=\"p-3 font-medium\">Department</th>\n <th className=\"p-3 text-right font-medium\">Amount</th>\n <th className=\"p-3 font-medium\">Status</th>\n <th className=\"p-3 font-medium\">Date</th>\n <th className=\"p-3 text-right font-medium\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {txnPage.pageItems.map((txn) => (\n <tr\n key={txn.id}\n className=\"cursor-pointer border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40\"\n onClick={() => navigate('/transactions/' + txn.id)}\n >\n <td className=\"p-3\">\n <StatusPill tone={txnTypeTone(txn.type)}>{txn.type}</StatusPill>\n </td>\n <td className=\"max-w-xs truncate p-3\" title={txn.description}>\n {txn.description}\n </td>\n <td className=\"p-3 text-xs text-text-secondary\">{txn.department}</td>\n <td\n className={cn(\n 'p-3 text-right font-medium tabular-nums',\n txn.amount < 0 && 'text-status-error-text'\n )}\n >\n {fmtMoney(Math.abs(txn.amount), txn.currency)}\n </td>\n <td className=\"p-3\">\n <StatusPill tone={txnStatusTone(txn.status)}>{txn.status}</StatusPill>\n </td>\n <td className=\"p-3 text-xs text-text-secondary\">\n {formatDate(txn.date)}\n </td>\n <td className=\"p-3\">\n <div className=\"flex justify-end\">\n <TransactionActions\n txn={txn}\n actingId={actingId}\n onApprove={handleApprove}\n onReject={handleReject}\n />\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {/* Mobile cards */}\n <div className=\"divide-y divide-border-seam/60 md:hidden\">\n {txnPage.pageItems.map((txn) => (\n <button\n key={txn.id}\n type=\"button\"\n className=\"block w-full p-4 text-left hover:bg-bg-sunken/40\"\n onClick={() => navigate('/transactions/' + txn.id)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"min-w-0 flex-1 truncate font-medium\">{txn.description}</p>\n <span\n className={cn(\n 'shrink-0 font-semibold tabular-nums',\n txn.amount < 0 && 'text-status-error-text'\n )}\n >\n {fmtMoney(Math.abs(txn.amount), txn.currency)}\n </span>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-2\">\n <StatusPill tone={txnTypeTone(txn.type)}>{txn.type}</StatusPill>\n <StatusPill tone={txnStatusTone(txn.status)}>{txn.status}</StatusPill>\n <span className=\"text-xs text-text-secondary\">\n {txn.department} · {formatDate(txn.date)}\n </span>\n </div>\n {txn.status === 'pending' ? (\n <div className=\"mt-3\">\n <TransactionActions\n txn={txn}\n actingId={actingId}\n onApprove={handleApprove}\n onReject={handleReject}\n />\n </div>\n ) : null}\n </button>\n ))}\n </div>\n\n <div className=\"px-3\">\n <Pagination\n page={txnPage.page}\n pageCount={txnPage.pageCount}\n rangeStart={txnPage.rangeStart}\n rangeEnd={txnPage.rangeEnd}\n totalItems={txnPage.totalItems}\n canPrev={txnPage.canPrev}\n canNext={txnPage.canNext}\n onPrev={txnPage.prevPage}\n onNext={txnPage.nextPage}\n itemLabel=\"transactions\"\n />\n </div>\n </GlassCard>\n )}\n </>\n )}\n </TabsContent>\n\n {/* ─── Budgets ─── */}\n <TabsContent value=\"budgets\" className=\"space-y-4\">\n {budgets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No budgets defined\"\n description=\"Create departmental budgets to track allocated vs. actual asset spend.\"\n />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2\">\n {budgets.map((budget: Budget) => (\n <GlassCard\n key={budget.id}\n treatment=\"glass\"\n className=\"cursor-pointer border-border-seam\"\n >\n <div\n className=\"p-5\"\n role=\"button\"\n tabIndex={0}\n onClick={() => navigate('/finance/budgets/' + budget.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ')\n navigate('/finance/budgets/' + budget.id);\n }}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate font-semibold text-text-primary\">{budget.name}</h3>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {budget.department} · {budget.period}\n </p>\n </div>\n <StatusPill tone={budgetStatusTone(budget.status)}>\n {budget.status.replace(/_/g, ' ')}\n </StatusPill>\n </div>\n <div className=\"mt-4 space-y-3\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Allocated</span>\n <span className=\"font-medium tabular-nums\">\n {fmtMoney(budget.allocated)}\n </span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Remaining</span>\n <span\n className={cn(\n 'font-medium tabular-nums',\n budget.remaining < 0\n ? 'text-status-error-text'\n : 'text-status-success-text'\n )}\n >\n {fmtMoney(budget.remaining)}\n </span>\n </div>\n <ProgressMeter\n value={budget.spent}\n max={budget.allocated}\n label=\"Spent\"\n format={(v) => fmtMoney(v)}\n thresholds={{ warning: 0.8, danger: 1 }}\n />\n </div>\n </div>\n </GlassCard>\n ))}\n </div>\n )}\n </TabsContent>\n\n {/* ─── Invoices ─── */}\n <TabsContent value=\"invoices\" className=\"space-y-4\">\n {invoices.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-files\"\n title=\"No invoices\"\n description=\"Vendor invoices for asset purchases and services will be listed here.\"\n />\n ) : (\n <>\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-muted\" />\n <Input\n value={invSearch}\n onChange={(e) => setInvSearch(e.target.value)}\n placeholder=\"Search invoice number or vendor\"\n className=\"pl-9\"\n />\n </div>\n <Select value={invStatusFilter} onValueChange={setInvStatusFilter}>\n <SelectTrigger className=\"w-full sm:w-40\">\n <SelectValue placeholder=\"Status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n {INVOICE_STATUSES.map((s) => (\n <SelectItem key={s} value={s.toLowerCase()}>\n {humanize(s)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {filteredInvoices.length === 0 ? (\n <GlassCard treatment=\"glass\" className=\"border-border-seam\">\n <p className=\"p-8 text-center text-sm text-text-secondary\">\n No invoices match your filters.\n </p>\n </GlassCard>\n ) : (\n <GlassCard treatment=\"glass\" className=\"overflow-hidden border-border-seam\">\n {/* Desktop table */}\n <div className=\"hidden overflow-x-auto md:block\">\n <table className=\"w-full min-w-[720px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken/40 text-left\">\n <th className=\"p-3 font-medium\">Invoice #</th>\n <th className=\"p-3 font-medium\">Vendor</th>\n <th className=\"p-3 text-right font-medium\">Amount</th>\n <th className=\"p-3 font-medium\">Issue Date</th>\n <th className=\"p-3 font-medium\">Due Date</th>\n <th className=\"p-3 font-medium\">Status</th>\n </tr>\n </thead>\n <tbody>\n {invPage.pageItems.map((inv: Invoice) => (\n <tr\n key={inv.id}\n className=\"cursor-pointer border-b border-border-seam/60 last:border-0 hover:bg-bg-sunken/40\"\n onClick={() => navigate('/finance/invoices/' + inv.id)}\n >\n <td className=\"p-3 font-mono text-xs\">{inv.number}</td>\n <td className=\"p-3\">{inv.vendor}</td>\n <td className=\"p-3 text-right font-medium tabular-nums\">\n {fmtMoney(inv.amount, inv.currency)}\n </td>\n <td className=\"p-3 text-xs text-text-secondary\">\n {formatDate(inv.issueDate)}\n </td>\n <td className=\"p-3 text-xs text-text-secondary\">\n {formatDate(inv.dueDate)}\n </td>\n <td className=\"p-3\">\n <StatusPill tone={invoiceStatusTone(inv.status)}>\n {inv.status}\n </StatusPill>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {/* Mobile cards */}\n <div className=\"divide-y divide-border-seam/60 md:hidden\">\n {invPage.pageItems.map((inv: Invoice) => (\n <button\n key={inv.id}\n type=\"button\"\n className=\"block w-full p-4 text-left hover:bg-bg-sunken/40\"\n onClick={() => navigate('/finance/invoices/' + inv.id)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <p className=\"truncate font-mono text-xs text-text-secondary\">\n {inv.number}\n </p>\n <p className=\"truncate font-medium\">{inv.vendor}</p>\n </div>\n <span className=\"shrink-0 font-semibold tabular-nums\">\n {fmtMoney(inv.amount, inv.currency)}\n </span>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-2\">\n <StatusPill tone={invoiceStatusTone(inv.status)}>{inv.status}</StatusPill>\n <span className=\"text-xs text-text-secondary\">\n Due {formatDate(inv.dueDate)}\n </span>\n </div>\n </button>\n ))}\n </div>\n\n <div className=\"px-3\">\n <Pagination\n page={invPage.page}\n pageCount={invPage.pageCount}\n rangeStart={invPage.rangeStart}\n rangeEnd={invPage.rangeEnd}\n totalItems={invPage.totalItems}\n canPrev={invPage.canPrev}\n canNext={invPage.canNext}\n onPrev={invPage.prevPage}\n onNext={invPage.nextPage}\n itemLabel=\"invoices\"\n />\n </div>\n </GlassCard>\n )}\n </>\n )}\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { CsvCell } from './csv';
2
+ export type FinanceRegister = 'budgets' | 'transactions' | 'invoices';
3
+ export interface FinanceExportRow {
4
+ id: string;
5
+ workspaceId: string;
6
+ cells: CsvCell[];
7
+ }
8
+ export declare const FINANCE_EXPORT_PAGE_SIZE = 50;
9
+ export declare const FINANCE_EXPORT_MAX_ROWS = 10000;
10
+ export declare const FINANCE_EXPORT_MAX_BYTES: number;
11
+ export declare const FINANCE_EXPORT_HEADERS: Record<FinanceRegister, string[]>;
12
+ export declare class FinanceExportError extends Error {
13
+ readonly reason: 'cancelled' | 'scope' | 'page' | 'capacity';
14
+ constructor(reason: 'cancelled' | 'scope' | 'page' | 'capacity');
15
+ }
16
+ export interface FinanceExportOptions {
17
+ register: FinanceRegister;
18
+ workspaceId: string;
19
+ signal: AbortSignal;
20
+ isCurrentScope: () => boolean;
21
+ fetchPage: (pagination: {
22
+ limit: number;
23
+ cursor: string | null;
24
+ }, signal: AbortSignal) => Promise<FinanceExportRow[]>;
25
+ onProgress?: (count: number) => void;
26
+ download: (filename: string, headers: string[], rows: CsvCell[][]) => void;
27
+ }
28
+ /** Exhaust the legacy bounded cursor API; filters/current UI page are not inputs.
29
+ * This exports the whole register, as promised by the finance story. Each query
30
+ * stays at the platform default of 50. Exact-full pages require a terminal probe.
31
+ */
32
+ export declare function exportFinanceRegister(options: FinanceExportOptions): Promise<number>;
@@ -0,0 +1,85 @@
1
+ import { toCsv as e } from "./csv.js";
2
+ var t = 1e4, n = 10485760, r = {
3
+ budgets: [
4
+ "Name",
5
+ "Department",
6
+ "Category",
7
+ "Period",
8
+ "Allocated",
9
+ "Spent",
10
+ "Remaining",
11
+ "Status"
12
+ ],
13
+ transactions: [
14
+ "ID",
15
+ "Type",
16
+ "Description",
17
+ "Department",
18
+ "Amount",
19
+ "Currency",
20
+ "Status",
21
+ "Date"
22
+ ],
23
+ invoices: [
24
+ "Invoice #",
25
+ "Vendor",
26
+ "Amount",
27
+ "Currency",
28
+ "Issue Date",
29
+ "Due Date",
30
+ "Status"
31
+ ]
32
+ }, i = class extends Error {
33
+ reason;
34
+ constructor(e) {
35
+ super(`Finance export ${e}`), this.reason = e;
36
+ }
37
+ };
38
+ function a(e) {
39
+ return typeof e == "string" && /^[\s]*[=+\-@\t\r]/.test(e) ? `'${e}` : e;
40
+ }
41
+ async function o(e, t) {
42
+ return new Promise((n, r) => {
43
+ let a = () => r(new i("cancelled"));
44
+ e.signal.addEventListener("abort", a, { once: !0 }), e.signal.aborted ? (e.signal.removeEventListener("abort", a), a()) : Promise.resolve().then(() => {
45
+ if (e.signal.aborted) throw new i("cancelled");
46
+ if (!e.isCurrentScope()) throw new i("scope");
47
+ return e.fetchPage(t, e.signal);
48
+ }).then((t) => {
49
+ e.signal.removeEventListener("abort", a), n(t);
50
+ }, (t) => {
51
+ e.signal.removeEventListener("abort", a), r(t);
52
+ });
53
+ });
54
+ }
55
+ async function s(n) {
56
+ let s = () => {
57
+ if (n.signal.aborted) throw new i("cancelled");
58
+ if (!n.workspaceId || !n.isCurrentScope()) throw new i("scope");
59
+ };
60
+ s();
61
+ let c = [], l = /* @__PURE__ */ new Set(), u = r[n.register], d = new TextEncoder(), f = d.encode(`\ufeff${e(u, [])}`).byteLength, p = null;
62
+ for (let r = 0; r <= t / 50; r++) {
63
+ s();
64
+ let t = await o(n, {
65
+ limit: 50,
66
+ cursor: p
67
+ });
68
+ if (s(), t.length > 50) throw new i("page");
69
+ for (let r of t) {
70
+ if (!r.id || l.has(r.id)) throw new i("page");
71
+ if (r.workspaceId !== n.workspaceId) throw new i("scope");
72
+ if (r.cells.length !== u.length) throw new i("page");
73
+ if (c.length >= 1e4) throw new i("capacity");
74
+ let t = r.cells.map(a);
75
+ if (f += d.encode(e([], [t])).byteLength, f > 10485760) throw new i("capacity");
76
+ l.add(r.id), c.push(t);
77
+ }
78
+ if (n.onProgress?.(c.length), s(), t.length < 50) return c.length > 0 && (s(), n.download(`${n.register}-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`, u, c)), c.length;
79
+ p = t[t.length - 1].id;
80
+ }
81
+ throw new i("capacity");
82
+ }
83
+ export { r as FINANCE_EXPORT_HEADERS, n as FINANCE_EXPORT_MAX_BYTES, t as FINANCE_EXPORT_MAX_ROWS, i as FinanceExportError, s as exportFinanceRegister };
84
+
85
+ //# sourceMappingURL=financeExport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"financeExport.js","names":["FINANCE_EXPORT_MAX_ROWS","FINANCE_EXPORT_MAX_BYTES","FINANCE_EXPORT_HEADERS","FinanceExportError","reason","spreadsheetCell","value","abortablePage","cancelled","reject","options","pagination","resolve","error","exportFinanceRegister","check","rows","ids","headers","encoder","bytes","toCsv","cursor","page","batch","row","cells"],"sources":["../../../src/assethandler/utils/financeExport.ts"],"sourcesContent":["import { toCsv, type CsvCell } from './csv';\n\nexport type FinanceRegister = 'budgets' | 'transactions' | 'invoices';\nexport interface FinanceExportRow {\n id: string;\n workspaceId: string;\n cells: CsvCell[];\n}\n\nexport const FINANCE_EXPORT_PAGE_SIZE = 50;\n// Browser resource limits, not customer quotas. Never download a truncated file.\n// Larger registers belong in the existing asynchronous workspace Export service.\nexport const FINANCE_EXPORT_MAX_ROWS = 10_000;\nexport const FINANCE_EXPORT_MAX_BYTES = 10 * 1024 * 1024;\n\nexport const FINANCE_EXPORT_HEADERS: Record<FinanceRegister, string[]> = {\n budgets: [\n 'Name',\n 'Department',\n 'Category',\n 'Period',\n 'Allocated',\n 'Spent',\n 'Remaining',\n 'Status',\n ],\n transactions: ['ID', 'Type', 'Description', 'Department', 'Amount', 'Currency', 'Status', 'Date'],\n invoices: ['Invoice #', 'Vendor', 'Amount', 'Currency', 'Issue Date', 'Due Date', 'Status'],\n};\n\nexport class FinanceExportError extends Error {\n constructor(readonly reason: 'cancelled' | 'scope' | 'page' | 'capacity') {\n super(`Finance export ${reason}`);\n }\n}\n\nexport interface FinanceExportOptions {\n register: FinanceRegister;\n workspaceId: string;\n signal: AbortSignal;\n isCurrentScope: () => boolean;\n fetchPage: (\n pagination: { limit: number; cursor: string | null },\n signal: AbortSignal\n ) => Promise<FinanceExportRow[]>;\n onProgress?: (count: number) => void;\n download: (filename: string, headers: string[], rows: CsvCell[][]) => void;\n}\n\nfunction spreadsheetCell(value: CsvCell): CsvCell {\n // Quoting alone does not prevent spreadsheet formula execution. Preserve\n // numeric amounts, while treating formula-like user text as literal text.\n return typeof value === 'string' && /^[\\s]*[=+\\-@\\t\\r]/.test(value) ? `'${value}` : value;\n}\n\nasync function abortablePage(\n options: FinanceExportOptions,\n pagination: { limit: number; cursor: string | null }\n): Promise<FinanceExportRow[]> {\n return new Promise((resolve, reject) => {\n const cancelled = () => reject(new FinanceExportError('cancelled'));\n options.signal.addEventListener('abort', cancelled, { once: true });\n if (options.signal.aborted) {\n options.signal.removeEventListener('abort', cancelled);\n cancelled();\n return;\n }\n void Promise.resolve()\n .then(() => {\n if (options.signal.aborted) throw new FinanceExportError('cancelled');\n if (!options.isCurrentScope()) throw new FinanceExportError('scope');\n return options.fetchPage(pagination, options.signal);\n })\n .then(\n (value) => {\n options.signal.removeEventListener('abort', cancelled);\n resolve(value);\n },\n (error) => {\n options.signal.removeEventListener('abort', cancelled);\n reject(error);\n }\n );\n });\n}\n\n/** Exhaust the legacy bounded cursor API; filters/current UI page are not inputs.\n * This exports the whole register, as promised by the finance story. Each query\n * stays at the platform default of 50. Exact-full pages require a terminal probe.\n */\nexport async function exportFinanceRegister(options: FinanceExportOptions): Promise<number> {\n const check = () => {\n if (options.signal.aborted) throw new FinanceExportError('cancelled');\n if (!options.workspaceId || !options.isCurrentScope()) throw new FinanceExportError('scope');\n };\n check();\n const rows: CsvCell[][] = [];\n const ids = new Set<string>();\n const headers = FINANCE_EXPORT_HEADERS[options.register];\n const encoder = new TextEncoder();\n let bytes = encoder.encode(`\\ufeff${toCsv(headers, [])}`).byteLength;\n let cursor: string | null = null;\n // At most 200 full pages plus a terminal/overflow probe. No unlimited loop.\n for (let page = 0; page <= FINANCE_EXPORT_MAX_ROWS / FINANCE_EXPORT_PAGE_SIZE; page++) {\n check();\n const batch = await abortablePage(options, { limit: FINANCE_EXPORT_PAGE_SIZE, cursor });\n check();\n if (batch.length > FINANCE_EXPORT_PAGE_SIZE) throw new FinanceExportError('page');\n for (const row of batch) {\n if (!row.id || ids.has(row.id)) throw new FinanceExportError('page');\n if (row.workspaceId !== options.workspaceId) throw new FinanceExportError('scope');\n if (row.cells.length !== headers.length) throw new FinanceExportError('page');\n if (rows.length >= FINANCE_EXPORT_MAX_ROWS) throw new FinanceExportError('capacity');\n const cells = row.cells.map(spreadsheetCell);\n bytes += encoder.encode(toCsv([], [cells])).byteLength;\n if (bytes > FINANCE_EXPORT_MAX_BYTES) throw new FinanceExportError('capacity');\n ids.add(row.id);\n rows.push(cells);\n }\n options.onProgress?.(rows.length);\n check();\n if (batch.length < FINANCE_EXPORT_PAGE_SIZE) {\n if (rows.length > 0) {\n check();\n options.download(\n `${options.register}-${new Date().toISOString().slice(0, 10)}`,\n headers,\n rows\n );\n }\n return rows.length;\n }\n cursor = batch[batch.length - 1].id;\n }\n throw new FinanceExportError('capacity');\n}\n"],"mappings":";AAYA,IAAaA,IAA0B,KAC1BC,IAA2B,UAE3BC,IAA4D;CACvE,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,cAAc;EAAC;EAAM;EAAQ;EAAe;EAAc;EAAU;EAAY;EAAU;CAAM;CAChG,UAAU;EAAC;EAAa;EAAU;EAAU;EAAY;EAAc;EAAY;CAAQ;AAC5F,GAEaC,IAAb,cAAwC,MAAM;CACvB;CAArB,YAAY,GAA8D;EAArD,AACnB,MAAM,kBAAkBC,GAAQ,GADb,KAAA,SAAAA;CAErB;AACF;AAeA,SAASC,EAAgB,GAAyB;CAGhD,OAAO,OAAOC,KAAU,YAAY,oBAAoB,KAAKA,CAAK,IAAI,IAAIA,MAAUA;AACtF;AAEA,eAAeC,EACb,GACA,GAC6B;CAC7B,OAAO,IAAI,SAAS,GAAS,MAAW;EACtC,IAAMC,UAAkBC,EAAO,IAAIN,EAAmB,WAAW,CAAC;EAElE,AADAO,EAAQ,OAAO,iBAAiB,SAASF,GAAW,EAAE,MAAM,GAAK,CAAC,GAC9DE,EAAQ,OAAO,WACjBA,EAAQ,OAAO,oBAAoB,SAASF,CAAS,GACrDA,EAAU,KAGZ,QAAa,QAAQ,CAAC,CACnB,WAAW;GACV,IAAIE,EAAQ,OAAO,SAAS,MAAM,IAAIP,EAAmB,WAAW;GACpE,IAAI,CAACO,EAAQ,eAAe,GAAG,MAAM,IAAIP,EAAmB,OAAO;GACnE,OAAOO,EAAQ,UAAUC,GAAYD,EAAQ,MAAM;EACrD,CAAC,CAAC,CACD,MACE,MAAU;GAET,AADAA,EAAQ,OAAO,oBAAoB,SAASF,CAAS,GACrDI,EAAQN,CAAK;EACf,IACC,MAAU;GAET,AADAI,EAAQ,OAAO,oBAAoB,SAASF,CAAS,GACrDC,EAAOI,CAAK;EACd,CACF;CACJ,CAAC;AACH;AAMA,eAAsBC,EAAsB,GAAgD;CAC1F,IAAMC,UAAc;EAClB,IAAIL,EAAQ,OAAO,SAAS,MAAM,IAAIP,EAAmB,WAAW;EACpE,IAAI,CAACO,EAAQ,eAAe,CAACA,EAAQ,eAAe,GAAG,MAAM,IAAIP,EAAmB,OAAO;CAC7F;CACA,EAAM;CACN,IAAMa,IAAoB,CAAC,GACrBC,oBAAM,IAAI,IAAY,GACtBC,IAAUhB,EAAuBQ,EAAQ,WACzCS,IAAU,IAAI,YAAY,GAC5BC,IAAQD,EAAQ,OAAO,SAASE,EAAMH,GAAS,CAAC,CAAC,GAAG,CAAC,CAAC,YACtDI,IAAwB;CAE5B,KAAK,IAAIC,IAAO,GAAGA,KAAQvB,IAAA,IAAoD,KAAQ;EACrF,EAAM;EACN,IAAMwB,IAAQ,MAAMjB,EAAcG,GAAS;GAAE,OAAA;GAAiC;EAAO,CAAC;EAEtF,IADAK,EAAM,GACFS,EAAM,SAAA,IAAmC,MAAM,IAAIrB,EAAmB,MAAM;EAChF,KAAK,IAAMsB,KAAOD,GAAO;GACvB,IAAI,CAACC,EAAI,MAAMR,EAAI,IAAIQ,EAAI,EAAE,GAAG,MAAM,IAAItB,EAAmB,MAAM;GACnE,IAAIsB,EAAI,gBAAgBf,EAAQ,aAAa,MAAM,IAAIP,EAAmB,OAAO;GACjF,IAAIsB,EAAI,MAAM,WAAWP,EAAQ,QAAQ,MAAM,IAAIf,EAAmB,MAAM;GAC5E,IAAIa,EAAK,UAAA,KAAmC,MAAM,IAAIb,EAAmB,UAAU;GACnF,IAAMuB,IAAQD,EAAI,MAAM,IAAIpB,CAAe;GAE3C,IADA,KAASc,EAAQ,OAAOE,EAAM,CAAC,GAAG,CAACK,CAAK,CAAC,CAAC,CAAC,CAAC,YACxCN,IAAA,UAAkC,MAAM,IAAIjB,EAAmB,UAAU;GAE7E,AADAc,EAAI,IAAIQ,EAAI,EAAE,GACdT,EAAK,KAAKU,CAAK;EACjB;EAGA,IAFAhB,EAAQ,aAAaM,EAAK,MAAM,GAChCD,EAAM,GACFS,EAAM,SAAA,IASR,OARIR,EAAK,SAAS,MAChBD,EAAM,GACNL,EAAQ,SACN,GAAGA,EAAQ,SAAS,oBAAG,IAAI,KAAK,EAAA,CAAE,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE,KAC3DQ,GACAF,CACF,IAEKA,EAAK;EAEd,IAASQ,EAAMA,EAAM,SAAS,EAAE,CAAC;CACnC;CACA,MAAM,IAAIrB,EAAmB,UAAU;AACzC"}