@burdenoff/microfe-assethandler 2026.703.8 → 2026.703.10
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.
- package/dist/assethandler/components/ChartCard.js +2 -0
- package/dist/assethandler/components/ListEmptyState.js +2 -0
- package/dist/assethandler/components/SectionHeader.js +2 -0
- package/dist/assethandler/components/StatTile.js +4 -0
- package/dist/assethandler/components/charts/BarsChart.js +3 -0
- package/dist/assethandler/components/charts/ChartParts.js +3 -0
- package/dist/assethandler/components/charts/DonutChart.js +3 -0
- package/dist/assethandler/components/charts/ProgressMeter.js +2 -0
- package/dist/assethandler/components/charts/RadialGauge.js +2 -0
- package/dist/assethandler/components/charts/Sparkline.js +3 -0
- package/dist/assethandler/components/charts/TrendChart.js +4 -0
- package/dist/assethandler/components/charts/chartTheme.js +1 -0
- package/dist/assethandler/components/charts/index.js +8 -0
- package/dist/assethandler/components/index.js +5 -0
- package/dist/assethandler/pages/AnalyticsPage.js +21 -18
- package/dist/assethandler/pages/AnalyticsPage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetCheckInOutPage.js +37 -34
- package/dist/assethandler/pages/AssetsAssetCheckInOutPage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetCreatePage.js +1 -0
- package/dist/assethandler/pages/AssetsAssetCreatePage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetDetailPage.js +22 -18
- package/dist/assethandler/pages/AssetsAssetDetailPage.js.map +1 -1
- package/dist/assethandler/pages/AssetsAssetListPage.js +6 -0
- package/dist/assethandler/pages/AssetsAssetListPage.js.map +1 -1
- package/dist/assethandler/pages/CmdbBrowserPage.js +3 -3
- package/dist/assethandler/pages/CmdbBrowserPage.js.map +1 -1
- package/dist/assethandler/pages/DiscoveryJobsPage.js +3 -3
- package/dist/assethandler/pages/DiscoveryJobsPage.js.map +1 -1
- package/dist/assethandler/pages/FinanceBudgetDetailPage.js +1 -0
- package/dist/assethandler/pages/FinanceBudgetDetailPage.js.map +1 -1
- package/dist/assethandler/pages/FinanceInvoiceDetailPage.js +1 -0
- package/dist/assethandler/pages/FinanceInvoiceDetailPage.js.map +1 -1
- package/dist/assethandler/pages/IotDevicesPage.js +3 -3
- package/dist/assethandler/pages/IotDevicesPage.js.map +1 -1
- package/dist/assethandler/pages/ItilChangeDetailPage.js +1 -0
- package/dist/assethandler/pages/ItilChangeDetailPage.js.map +1 -1
- package/dist/assethandler/pages/ItilIncidentDetailPage.js +1 -0
- package/dist/assethandler/pages/ItilIncidentDetailPage.js.map +1 -1
- package/dist/assethandler/pages/ItilProblemDetailPage.js +1 -0
- package/dist/assethandler/pages/ItilProblemDetailPage.js.map +1 -1
- package/dist/assethandler/pages/MaintenanceMaintenanceCalendarPage.js +36 -33
- package/dist/assethandler/pages/MaintenanceMaintenanceCalendarPage.js.map +1 -1
- package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js +1 -0
- package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js.map +1 -1
- package/dist/assethandler/pages/OperationsWorkOrderDetailPage.js +1 -0
- package/dist/assethandler/pages/OperationsWorkOrderDetailPage.js.map +1 -1
- package/dist/assethandler/pages/ProvenancePage.js +3 -3
- package/dist/assethandler/pages/ProvenancePage.js.map +1 -1
- package/dist/assethandler/pages/ReportsDashboardPage.js +21 -18
- package/dist/assethandler/pages/ReportsDashboardPage.js.map +1 -1
- package/dist/assethandler/pages/SamLicensesPage.js +3 -3
- package/dist/assethandler/pages/SamLicensesPage.js.map +1 -1
- package/dist/assethandler/pages/SecurityComplianceDetailPage.js +1 -0
- package/dist/assethandler/pages/SecurityComplianceDetailPage.js.map +1 -1
- package/dist/assethandler/pages/SecurityPolicyDetailPage.js +1 -0
- package/dist/assethandler/pages/SecurityPolicyDetailPage.js.map +1 -1
- package/dist/assethandler/pages/SecuritySecurityEventDetailPage.js +1 -0
- package/dist/assethandler/pages/SecuritySecurityEventDetailPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "react";
|
|
@@ -3,3 +3,8 @@ import "./CommentsPanel.js";
|
|
|
3
3
|
import "./ActivityTimeline.js";
|
|
4
4
|
import "./Pagination.js";
|
|
5
5
|
import "./NotFoundState.js";
|
|
6
|
+
import "./ListEmptyState.js";
|
|
7
|
+
import "./charts/index.js";
|
|
8
|
+
import "./StatTile.js";
|
|
9
|
+
import "./ChartCard.js";
|
|
10
|
+
import "./SectionHeader.js";
|
|
@@ -56,24 +56,27 @@ function P() {
|
|
|
56
56
|
/* @__PURE__ */ h(O, {
|
|
57
57
|
defaultValue: "overview",
|
|
58
58
|
children: [
|
|
59
|
-
/* @__PURE__ */ h(ee, {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
59
|
+
/* @__PURE__ */ h(ee, {
|
|
60
|
+
className: "max-w-full justify-start overflow-x-auto",
|
|
61
|
+
children: [
|
|
62
|
+
/* @__PURE__ */ m(A, {
|
|
63
|
+
value: "overview",
|
|
64
|
+
children: "Overview"
|
|
65
|
+
}),
|
|
66
|
+
/* @__PURE__ */ m(A, {
|
|
67
|
+
value: "financial",
|
|
68
|
+
children: "Financial"
|
|
69
|
+
}),
|
|
70
|
+
/* @__PURE__ */ m(A, {
|
|
71
|
+
value: "operational",
|
|
72
|
+
children: "Operational"
|
|
73
|
+
}),
|
|
74
|
+
/* @__PURE__ */ m(A, {
|
|
75
|
+
value: "insights",
|
|
76
|
+
children: "Insights"
|
|
77
|
+
})
|
|
78
|
+
]
|
|
79
|
+
}),
|
|
77
80
|
/* @__PURE__ */ h(k, {
|
|
78
81
|
value: "overview",
|
|
79
82
|
className: "space-y-6 mt-6",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/assethandler/pages/AnalyticsPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Separator,\n GradientText,\n GlassCard,\n EmphasisPanel,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Box,\n PieChart,\n Activity,\n Wrench,\n AlertTriangle,\n Wallet,\n DollarSign,\n TrendingDown,\n Users,\n Gauge,\n} from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\n\n// ─── Backend rollups (wspace-assethandler-svc, @rbac-guarded, no args) ────────\ninterface AssetHandlerFinancialSummary {\n totalAssetValue: number;\n totalDepreciation: number;\n totalMaintenanceSpend: number;\n budgetAllocated: number;\n budgetSpent: number;\n invoicesOutstanding: number;\n invoicesOutstandingCount: number;\n transactionExpense: number;\n}\n\ninterface AssetHandlerStatusCount {\n status: string;\n count: number;\n}\n\ninterface AssetHandlerUtilizationReport {\n totalAssets: number;\n assignedAssets: number;\n unassignedAssets: number;\n idleAssets: number;\n overdueAssignments: number;\n byStatus: AssetHandlerStatusCount[];\n}\n\nconst ANALYTICS_ROLLUPS_QUERY = gql`\n query AssetHandlerAnalyticsRollups {\n financialSummary {\n totalAssetValue\n totalDepreciation\n totalMaintenanceSpend\n budgetAllocated\n budgetSpent\n invoicesOutstanding\n invoicesOutstandingCount\n transactionExpense\n }\n utilizationReport {\n totalAssets\n assignedAssets\n unassignedAssets\n idleAssets\n overdueAssignments\n byStatus {\n status\n count\n }\n }\n }\n`;\n\nconst formatMoney = (value: number | undefined): string =>\n value === undefined ? '—' : `$${Math.round(value).toLocaleString()}`;\n\nconst statusVariant = (status: string): 'default' | 'secondary' | 'outline' =>\n status === 'active' ? 'default' : status === 'maintenance' ? 'secondary' : 'outline';\n\nexport function AnalyticsPage() {\n const { assets, transactions, incidents, maintenanceRecords, budgets, invoices, workOrders } =\n useAssetHandlerStore();\n\n const { data: rollupData } = useQuery<{\n financialSummary: AssetHandlerFinancialSummary;\n utilizationReport: AssetHandlerUtilizationReport;\n }>(ANALYTICS_ROLLUPS_QUERY, { fetchPolicy: 'cache-and-network', errorPolicy: 'all' });\n const financialSummary = rollupData?.financialSummary;\n const utilizationReport = rollupData?.utilizationReport;\n const maxStatusCount = Math.max(1, ...(utilizationReport?.byStatus.map((s) => s.count) ?? [1]));\n\n // ─── Asset aggregations (count-based only — no monetary asset value exists) ──\n const categoryBreakdown = useMemo(\n () =>\n assets.reduce<Record<string, number>>((acc, a) => {\n const cat = a.category.replace('_', ' ');\n acc[cat] = (acc[cat] || 0) + 1;\n return acc;\n }, {}),\n [assets]\n );\n\n const statusBreakdown = useMemo(\n () =>\n assets.reduce<Record<string, number>>((acc, a) => {\n acc[a.status] = (acc[a.status] || 0) + 1;\n return acc;\n }, {}),\n [assets]\n );\n\n const conditionBreakdown = useMemo(\n () =>\n assets.reduce<Record<string, number>>((acc, a) => {\n acc[a.condition] = (acc[a.condition] || 0) + 1;\n return acc;\n }, {}),\n [assets]\n );\n\n const activeAssets = assets.filter((a) => a.status === 'active').length;\n const attentionAssets = assets.filter(\n (a) => a.condition === 'poor' || a.condition === 'damaged' || a.status === 'maintenance'\n );\n\n // ─── Financial aggregations (from real transactions / budgets / invoices) ────\n const transactionsTotal = transactions.reduce((s, t) => s + Math.abs(t.amount), 0);\n const budgetAllocated = budgets.reduce((s, b) => s + b.allocated, 0);\n const budgetSpent = budgets.reduce((s, b) => s + b.spent, 0);\n const budgetRemaining = budgets.reduce((s, b) => s + b.remaining, 0);\n const maintenanceCost = maintenanceRecords.reduce(\n (s, m) => s + (m.actualCost ?? m.estimatedCost ?? 0),\n 0\n );\n const outstandingInvoices = invoices.filter(\n (i) => i.status !== 'paid' && i.status !== 'cancelled'\n );\n const outstandingInvoiceTotal = outstandingInvoices.reduce((s, i) => s + i.amount, 0);\n\n // ─── Operational aggregations ────────────────────────────────────────────────\n const resolvedIncidents = incidents.filter(\n (i) => i.status === 'resolved' || i.status === 'closed'\n );\n const slaBreached = incidents.filter((i) => i.slaBreached);\n const completedMaintenance = maintenanceRecords.filter((m) => m.status === 'completed');\n const scheduledMaintenance = maintenanceRecords.filter((m) => m.status === 'scheduled');\n const openWorkOrders = workOrders.filter(\n (w) => w.status !== 'completed' && w.status !== 'cancelled'\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-3xl font-bold\">\n <GradientText>Analytics & Business Intelligence</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n Asset, financial and operational insights across your estate\n </p>\n </div>\n </div>\n\n <PagePurpose>\n Turn raw asset data into decisions. See how your estate is distributed by category, status\n and condition, where budget is being spent, and how operations are trending — so you can\n plan work, justify replacements and spot risk before it shows up as downtime.\n </PagePurpose>\n\n <Tabs defaultValue=\"overview\">\n <TabsList>\n <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n <TabsTrigger value=\"financial\">Financial</TabsTrigger>\n <TabsTrigger value=\"operational\">Operational</TabsTrigger>\n <TabsTrigger value=\"insights\">Insights</TabsTrigger>\n </TabsList>\n\n {/* Overview Tab */}\n <TabsContent value=\"overview\" className=\"space-y-6 mt-6\">\n {/* Key Metrics */}\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Assets</CardTitle>\n <Box className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{assets.length}</div>\n <p className=\"text-xs text-text-secondary\">{activeAssets} currently active</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Categories</CardTitle>\n <PieChart className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {Object.keys(categoryBreakdown).length}\n </div>\n <p className=\"text-xs text-text-secondary\">Distinct asset categories</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Needs Attention</CardTitle>\n <AlertTriangle className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{attentionAssets.length}</div>\n <p className=\"text-xs text-text-secondary\">Poor / damaged / in maintenance</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Open Incidents</CardTitle>\n <Activity className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {incidents.length - resolvedIncidents.length}\n </div>\n <p className=\"text-xs text-text-secondary\">{incidents.length} total logged</p>\n </CardContent>\n </GlassCard>\n </div>\n\n {/* Category & Status Breakdown */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <PieChart className=\"h-4 w-4\" />\n Category Distribution\n </CardTitle>\n <CardDescription>Assets grouped by category</CardDescription>\n </CardHeader>\n <CardContent>\n {assets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No assets yet\"\n description=\"Add assets to see how your estate is distributed.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {Object.entries(categoryBreakdown)\n .sort((a, b) => b[1] - a[1])\n .map(([cat, count]) => {\n const pct = Math.round((count / assets.length) * 100);\n return (\n <div key={cat} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium capitalize\">{cat}</span>\n <span className=\"text-text-secondary\">\n {count} · {pct}%\n </span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </div>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Activity className=\"h-4 w-4\" />\n Status & Condition\n </CardTitle>\n <CardDescription>Current asset status and condition mix</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {Object.entries(statusBreakdown).map(([status, count]) => {\n const pct = assets.length > 0 ? Math.round((count / assets.length) * 100) : 0;\n return (\n <div key={status} className=\"flex items-center justify-between\">\n <Badge variant={statusVariant(status)}>{status.replace('_', ' ')}</Badge>\n <div className=\"flex items-center gap-3\">\n <div className=\"h-2 w-24 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-8 text-right\">{count}</span>\n </div>\n </div>\n );\n })}\n </div>\n\n <Separator className=\"my-4\" />\n\n <div>\n <h4 className=\"text-sm font-medium mb-3\">By Condition</h4>\n <div className=\"flex flex-wrap gap-2\">\n {Object.entries(conditionBreakdown).map(([condition, count]) => (\n <Badge key={condition} variant=\"outline\" className=\"capitalize\">\n {condition}: {count}\n </Badge>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </TabsContent>\n\n {/* Financial Tab */}\n <TabsContent value=\"financial\" className=\"space-y-6 mt-6\">\n {/* Financial rollup (from backend financialSummary) */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <DollarSign className=\"h-4 w-4\" />\n Financial Summary\n </CardTitle>\n <CardDescription>\n Portfolio valuation, budget and receivables across the estate\n </CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <DollarSign className=\"h-3.5 w-3.5\" />\n Total Asset Value\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.totalAssetValue)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <TrendingDown className=\"h-3.5 w-3.5\" />\n Total Depreciation\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.totalDepreciation)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Wrench className=\"h-3.5 w-3.5\" />\n Maintenance Spend\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.totalMaintenanceSpend)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Activity className=\"h-3.5 w-3.5\" />\n Transaction Expense\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.transactionExpense)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Wallet className=\"h-3.5 w-3.5\" />\n Budget Allocated\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.budgetAllocated)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Wallet className=\"h-3.5 w-3.5\" />\n Budget Spent\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.budgetSpent)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3 md:col-span-2\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <AlertTriangle className=\"h-3.5 w-3.5\" />\n Invoices Outstanding\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.invoicesOutstanding)}\n <span className=\"ml-2 text-xs font-normal text-text-secondary\">\n {financialSummary\n ? `${financialSummary.invoicesOutstandingCount} unpaid`\n : '—'}\n </span>\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Transactions</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{transactions.length}</div>\n <p className=\"text-xs text-text-secondary\">\n ${transactionsTotal.toLocaleString()} total value\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Budget Allocated</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${budgetAllocated.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">${budgetSpent.toLocaleString()} spent</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Maintenance Cost</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${maintenanceCost.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {maintenanceRecords.length} maintenance records\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Outstanding Invoices</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${outstandingInvoiceTotal.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {outstandingInvoices.length} of {invoices.length} unpaid\n </p>\n </CardContent>\n </Card>\n </div>\n\n {/* Budget Utilization */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Wallet className=\"h-4 w-4\" />\n Budget Utilization\n </CardTitle>\n <CardDescription>Allocated vs. spent per budget</CardDescription>\n </CardHeader>\n <CardContent>\n {budgets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No budgets yet\"\n description=\"Create a budget to track allocation and spend here.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {budgets.map((budget) => {\n const pct =\n budget.allocated > 0\n ? Math.min(100, Math.round((budget.spent / budget.allocated) * 100))\n : 0;\n return (\n <div key={budget.id} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium\">{budget.name}</span>\n <span className=\"text-text-secondary\">\n ${budget.spent.toLocaleString()} / ${budget.allocated.toLocaleString()}\n </span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className={`h-full rounded-full ${pct >= 100 ? 'bg-status-error-text' : 'bg-action-primary-bg'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n <Separator />\n <div className=\"flex items-center justify-between text-sm font-medium\">\n <span>Remaining across all budgets</span>\n <span>${budgetRemaining.toLocaleString()}</span>\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Recent Transactions */}\n <Card>\n <CardHeader>\n <CardTitle>Recent Transactions</CardTitle>\n </CardHeader>\n <CardContent>\n {transactions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No transactions\"\n description=\"Asset purchases, leases and disposals will appear here.\"\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b\">\n <th className=\"text-left py-2 font-medium\">Type</th>\n <th className=\"text-left py-2 font-medium\">Description</th>\n <th className=\"text-right py-2 font-medium\">Amount</th>\n <th className=\"text-left py-2 font-medium\">Status</th>\n </tr>\n </thead>\n <tbody>\n {transactions.slice(0, 5).map((txn) => (\n <tr key={txn.id} className=\"border-b hover:bg-bg-sunken/50\">\n <td className=\"py-2 capitalize\">{txn.type}</td>\n <td className=\"py-2 truncate max-w-xs\">{txn.description}</td>\n <td\n className={`py-2 text-right font-medium ${txn.amount < 0 ? 'text-status-error-text' : ''}`}\n >\n ${Math.abs(txn.amount).toLocaleString()}\n </td>\n <td className=\"py-2\">\n <Badge variant=\"outline\">{txn.status}</Badge>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </CardContent>\n </Card>\n </TabsContent>\n\n {/* Operational Tab */}\n <TabsContent value=\"operational\" className=\"space-y-6 mt-6\">\n {/* Utilization rollup (from backend utilizationReport) */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Gauge className=\"h-4 w-4\" />\n Asset Utilization\n </CardTitle>\n <CardDescription>\n Assignment coverage and idle capacity across the estate\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n <div className=\"grid gap-4 md:grid-cols-3 lg:grid-cols-5\">\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Box className=\"h-3.5 w-3.5\" />\n Total Assets\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {utilizationReport?.totalAssets ?? '—'}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Users className=\"h-3.5 w-3.5\" />\n Assigned\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {utilizationReport?.assignedAssets ?? '—'}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Box className=\"h-3.5 w-3.5\" />\n Unassigned\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {utilizationReport?.unassignedAssets ?? '—'}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Activity className=\"h-3.5 w-3.5\" />\n Idle\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {utilizationReport?.idleAssets ?? '—'}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <AlertTriangle className=\"h-3.5 w-3.5 text-status-error-text\" />\n Overdue\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1 text-status-error-text\">\n {utilizationReport?.overdueAssignments ?? '—'}\n </p>\n </div>\n </div>\n\n <div>\n <h4 className=\"text-sm font-medium mb-3\">Assets by Status</h4>\n {utilizationReport && utilizationReport.byStatus.length > 0 ? (\n <div className=\"space-y-3\">\n {utilizationReport.byStatus.map(({ status, count }) => (\n <div key={status} className=\"flex items-center justify-between\">\n <Badge variant={statusVariant(status)} className=\"capitalize\">\n {status.replace(/_/g, ' ')}\n </Badge>\n <div className=\"flex items-center gap-3\">\n <div className=\"h-2 w-24 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${Math.round((count / maxStatusCount) * 100)}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-8 text-right tabular-nums\">\n {count}\n </span>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">—</p>\n )}\n </div>\n </CardContent>\n </Card>\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Incidents</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{incidents.length}</div>\n <p className=\"text-xs text-text-secondary\">{resolvedIncidents.length} resolved</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">SLA Breaches</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold text-status-error-text\">\n {slaBreached.length}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {((1 - slaBreached.length / Math.max(incidents.length, 1)) * 100).toFixed(1)}%\n within SLA\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Maintenance Done</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{completedMaintenance.length}</div>\n <p className=\"text-xs text-text-secondary\">of {maintenanceRecords.length} total</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Open Work Orders</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{openWorkOrders.length}</div>\n <p className=\"text-xs text-text-secondary\">of {workOrders.length} total</p>\n </CardContent>\n </Card>\n </div>\n </TabsContent>\n\n {/* Insights Tab */}\n <TabsContent value=\"insights\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 md:grid-cols-3\">\n <Card className=\"border-status-info-border bg-status-info-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-info-text text-base\">Diversification</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-info-text\">\n Your estate spans {Object.keys(categoryBreakdown).length} categories across{' '}\n {assets.length} assets. {activeAssets} are currently active.\n </p>\n </CardContent>\n </Card>\n <Card className=\"border-status-success-border bg-status-success-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-success-text text-base\">Budget</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-success-text\">\n ${budgetSpent.toLocaleString()} spent of ${budgetAllocated.toLocaleString()}{' '}\n allocated, leaving ${budgetRemaining.toLocaleString()} across {budgets.length}{' '}\n budgets.\n </p>\n </CardContent>\n </Card>\n <Card className=\"border-status-warning-border bg-status-warning-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-warning-text text-base\">Maintenance</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-warning-text\">\n {scheduledMaintenance.length} maintenance tasks are scheduled. Total maintenance\n spend is ${maintenanceCost.toLocaleString()}. Stay on top of preventive\n maintenance.\n </p>\n </CardContent>\n </Card>\n </div>\n\n {/* Assets needing attention */}\n <EmphasisPanel>\n <CardHeader className=\"px-0 pt-0\">\n <CardTitle className=\"flex items-center gap-2\">\n <Wrench className=\"h-4 w-4\" />\n Assets Needing Attention\n </CardTitle>\n <CardDescription>\n Assets in poor or damaged condition, or currently under maintenance\n </CardDescription>\n </CardHeader>\n <CardContent className=\"px-0 pb-0\">\n {attentionAssets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-success\"\n title=\"Everything looks healthy\"\n description=\"No assets are flagged as poor, damaged or in maintenance right now.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {attentionAssets.slice(0, 6).map((asset, i) => (\n <div\n key={asset.id}\n className=\"flex items-center justify-between p-3 rounded-lg border border-border-seam bg-gloss-card\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center text-xs font-medium text-primary\">\n {i + 1}\n </div>\n <div>\n <p className=\"text-sm font-medium\">{asset.name}</p>\n <p className=\"text-xs text-text-secondary capitalize\">\n {asset.category.replace('_', ' ')}\n {asset.location ? ` · ${asset.location}` : ''}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"outline\" className=\"capitalize\">\n {asset.condition}\n </Badge>\n <Badge variant={statusVariant(asset.status)}>\n {asset.status.replace('_', ' ')}\n </Badge>\n </div>\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </EmphasisPanel>\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/assethandler/pages/AnalyticsPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Separator,\n GradientText,\n GlassCard,\n EmphasisPanel,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Box,\n PieChart,\n Activity,\n Wrench,\n AlertTriangle,\n Wallet,\n DollarSign,\n TrendingDown,\n Users,\n Gauge,\n} from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\n\n// ─── Backend rollups (wspace-assethandler-svc, @rbac-guarded, no args) ────────\ninterface AssetHandlerFinancialSummary {\n totalAssetValue: number;\n totalDepreciation: number;\n totalMaintenanceSpend: number;\n budgetAllocated: number;\n budgetSpent: number;\n invoicesOutstanding: number;\n invoicesOutstandingCount: number;\n transactionExpense: number;\n}\n\ninterface AssetHandlerStatusCount {\n status: string;\n count: number;\n}\n\ninterface AssetHandlerUtilizationReport {\n totalAssets: number;\n assignedAssets: number;\n unassignedAssets: number;\n idleAssets: number;\n overdueAssignments: number;\n byStatus: AssetHandlerStatusCount[];\n}\n\nconst ANALYTICS_ROLLUPS_QUERY = gql`\n query AssetHandlerAnalyticsRollups {\n financialSummary {\n totalAssetValue\n totalDepreciation\n totalMaintenanceSpend\n budgetAllocated\n budgetSpent\n invoicesOutstanding\n invoicesOutstandingCount\n transactionExpense\n }\n utilizationReport {\n totalAssets\n assignedAssets\n unassignedAssets\n idleAssets\n overdueAssignments\n byStatus {\n status\n count\n }\n }\n }\n`;\n\nconst formatMoney = (value: number | undefined): string =>\n value === undefined ? '—' : `$${Math.round(value).toLocaleString()}`;\n\nconst statusVariant = (status: string): 'default' | 'secondary' | 'outline' =>\n status === 'active' ? 'default' : status === 'maintenance' ? 'secondary' : 'outline';\n\nexport function AnalyticsPage() {\n const { assets, transactions, incidents, maintenanceRecords, budgets, invoices, workOrders } =\n useAssetHandlerStore();\n\n const { data: rollupData } = useQuery<{\n financialSummary: AssetHandlerFinancialSummary;\n utilizationReport: AssetHandlerUtilizationReport;\n }>(ANALYTICS_ROLLUPS_QUERY, { fetchPolicy: 'cache-and-network', errorPolicy: 'all' });\n const financialSummary = rollupData?.financialSummary;\n const utilizationReport = rollupData?.utilizationReport;\n const maxStatusCount = Math.max(1, ...(utilizationReport?.byStatus.map((s) => s.count) ?? [1]));\n\n // ─── Asset aggregations (count-based only — no monetary asset value exists) ──\n const categoryBreakdown = useMemo(\n () =>\n assets.reduce<Record<string, number>>((acc, a) => {\n const cat = a.category.replace('_', ' ');\n acc[cat] = (acc[cat] || 0) + 1;\n return acc;\n }, {}),\n [assets]\n );\n\n const statusBreakdown = useMemo(\n () =>\n assets.reduce<Record<string, number>>((acc, a) => {\n acc[a.status] = (acc[a.status] || 0) + 1;\n return acc;\n }, {}),\n [assets]\n );\n\n const conditionBreakdown = useMemo(\n () =>\n assets.reduce<Record<string, number>>((acc, a) => {\n acc[a.condition] = (acc[a.condition] || 0) + 1;\n return acc;\n }, {}),\n [assets]\n );\n\n const activeAssets = assets.filter((a) => a.status === 'active').length;\n const attentionAssets = assets.filter(\n (a) => a.condition === 'poor' || a.condition === 'damaged' || a.status === 'maintenance'\n );\n\n // ─── Financial aggregations (from real transactions / budgets / invoices) ────\n const transactionsTotal = transactions.reduce((s, t) => s + Math.abs(t.amount), 0);\n const budgetAllocated = budgets.reduce((s, b) => s + b.allocated, 0);\n const budgetSpent = budgets.reduce((s, b) => s + b.spent, 0);\n const budgetRemaining = budgets.reduce((s, b) => s + b.remaining, 0);\n const maintenanceCost = maintenanceRecords.reduce(\n (s, m) => s + (m.actualCost ?? m.estimatedCost ?? 0),\n 0\n );\n const outstandingInvoices = invoices.filter(\n (i) => i.status !== 'paid' && i.status !== 'cancelled'\n );\n const outstandingInvoiceTotal = outstandingInvoices.reduce((s, i) => s + i.amount, 0);\n\n // ─── Operational aggregations ────────────────────────────────────────────────\n const resolvedIncidents = incidents.filter(\n (i) => i.status === 'resolved' || i.status === 'closed'\n );\n const slaBreached = incidents.filter((i) => i.slaBreached);\n const completedMaintenance = maintenanceRecords.filter((m) => m.status === 'completed');\n const scheduledMaintenance = maintenanceRecords.filter((m) => m.status === 'scheduled');\n const openWorkOrders = workOrders.filter(\n (w) => w.status !== 'completed' && w.status !== 'cancelled'\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-3xl font-bold\">\n <GradientText>Analytics & Business Intelligence</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n Asset, financial and operational insights across your estate\n </p>\n </div>\n </div>\n\n <PagePurpose>\n Turn raw asset data into decisions. See how your estate is distributed by category, status\n and condition, where budget is being spent, and how operations are trending — so you can\n plan work, justify replacements and spot risk before it shows up as downtime.\n </PagePurpose>\n\n <Tabs defaultValue=\"overview\">\n <TabsList className=\"max-w-full justify-start overflow-x-auto\">\n <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n <TabsTrigger value=\"financial\">Financial</TabsTrigger>\n <TabsTrigger value=\"operational\">Operational</TabsTrigger>\n <TabsTrigger value=\"insights\">Insights</TabsTrigger>\n </TabsList>\n\n {/* Overview Tab */}\n <TabsContent value=\"overview\" className=\"space-y-6 mt-6\">\n {/* Key Metrics */}\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Assets</CardTitle>\n <Box className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{assets.length}</div>\n <p className=\"text-xs text-text-secondary\">{activeAssets} currently active</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Categories</CardTitle>\n <PieChart className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {Object.keys(categoryBreakdown).length}\n </div>\n <p className=\"text-xs text-text-secondary\">Distinct asset categories</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Needs Attention</CardTitle>\n <AlertTriangle className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{attentionAssets.length}</div>\n <p className=\"text-xs text-text-secondary\">Poor / damaged / in maintenance</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Open Incidents</CardTitle>\n <Activity className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {incidents.length - resolvedIncidents.length}\n </div>\n <p className=\"text-xs text-text-secondary\">{incidents.length} total logged</p>\n </CardContent>\n </GlassCard>\n </div>\n\n {/* Category & Status Breakdown */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <PieChart className=\"h-4 w-4\" />\n Category Distribution\n </CardTitle>\n <CardDescription>Assets grouped by category</CardDescription>\n </CardHeader>\n <CardContent>\n {assets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No assets yet\"\n description=\"Add assets to see how your estate is distributed.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {Object.entries(categoryBreakdown)\n .sort((a, b) => b[1] - a[1])\n .map(([cat, count]) => {\n const pct = Math.round((count / assets.length) * 100);\n return (\n <div key={cat} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium capitalize\">{cat}</span>\n <span className=\"text-text-secondary\">\n {count} · {pct}%\n </span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </div>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Activity className=\"h-4 w-4\" />\n Status & Condition\n </CardTitle>\n <CardDescription>Current asset status and condition mix</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {Object.entries(statusBreakdown).map(([status, count]) => {\n const pct = assets.length > 0 ? Math.round((count / assets.length) * 100) : 0;\n return (\n <div key={status} className=\"flex items-center justify-between\">\n <Badge variant={statusVariant(status)}>{status.replace('_', ' ')}</Badge>\n <div className=\"flex items-center gap-3\">\n <div className=\"h-2 w-24 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-8 text-right\">{count}</span>\n </div>\n </div>\n );\n })}\n </div>\n\n <Separator className=\"my-4\" />\n\n <div>\n <h4 className=\"text-sm font-medium mb-3\">By Condition</h4>\n <div className=\"flex flex-wrap gap-2\">\n {Object.entries(conditionBreakdown).map(([condition, count]) => (\n <Badge key={condition} variant=\"outline\" className=\"capitalize\">\n {condition}: {count}\n </Badge>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </TabsContent>\n\n {/* Financial Tab */}\n <TabsContent value=\"financial\" className=\"space-y-6 mt-6\">\n {/* Financial rollup (from backend financialSummary) */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <DollarSign className=\"h-4 w-4\" />\n Financial Summary\n </CardTitle>\n <CardDescription>\n Portfolio valuation, budget and receivables across the estate\n </CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <DollarSign className=\"h-3.5 w-3.5\" />\n Total Asset Value\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.totalAssetValue)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <TrendingDown className=\"h-3.5 w-3.5\" />\n Total Depreciation\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.totalDepreciation)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Wrench className=\"h-3.5 w-3.5\" />\n Maintenance Spend\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.totalMaintenanceSpend)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Activity className=\"h-3.5 w-3.5\" />\n Transaction Expense\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.transactionExpense)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Wallet className=\"h-3.5 w-3.5\" />\n Budget Allocated\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.budgetAllocated)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Wallet className=\"h-3.5 w-3.5\" />\n Budget Spent\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.budgetSpent)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3 md:col-span-2\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <AlertTriangle className=\"h-3.5 w-3.5\" />\n Invoices Outstanding\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {formatMoney(financialSummary?.invoicesOutstanding)}\n <span className=\"ml-2 text-xs font-normal text-text-secondary\">\n {financialSummary\n ? `${financialSummary.invoicesOutstandingCount} unpaid`\n : '—'}\n </span>\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Transactions</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{transactions.length}</div>\n <p className=\"text-xs text-text-secondary\">\n ${transactionsTotal.toLocaleString()} total value\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Budget Allocated</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${budgetAllocated.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">${budgetSpent.toLocaleString()} spent</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Maintenance Cost</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${maintenanceCost.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {maintenanceRecords.length} maintenance records\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Outstanding Invoices</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${outstandingInvoiceTotal.toLocaleString()}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {outstandingInvoices.length} of {invoices.length} unpaid\n </p>\n </CardContent>\n </Card>\n </div>\n\n {/* Budget Utilization */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Wallet className=\"h-4 w-4\" />\n Budget Utilization\n </CardTitle>\n <CardDescription>Allocated vs. spent per budget</CardDescription>\n </CardHeader>\n <CardContent>\n {budgets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No budgets yet\"\n description=\"Create a budget to track allocation and spend here.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {budgets.map((budget) => {\n const pct =\n budget.allocated > 0\n ? Math.min(100, Math.round((budget.spent / budget.allocated) * 100))\n : 0;\n return (\n <div key={budget.id} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium\">{budget.name}</span>\n <span className=\"text-text-secondary\">\n ${budget.spent.toLocaleString()} / ${budget.allocated.toLocaleString()}\n </span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className={`h-full rounded-full ${pct >= 100 ? 'bg-status-error-text' : 'bg-action-primary-bg'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n <Separator />\n <div className=\"flex items-center justify-between text-sm font-medium\">\n <span>Remaining across all budgets</span>\n <span>${budgetRemaining.toLocaleString()}</span>\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Recent Transactions */}\n <Card>\n <CardHeader>\n <CardTitle>Recent Transactions</CardTitle>\n </CardHeader>\n <CardContent>\n {transactions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No transactions\"\n description=\"Asset purchases, leases and disposals will appear here.\"\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b\">\n <th className=\"text-left py-2 font-medium\">Type</th>\n <th className=\"text-left py-2 font-medium\">Description</th>\n <th className=\"text-right py-2 font-medium\">Amount</th>\n <th className=\"text-left py-2 font-medium\">Status</th>\n </tr>\n </thead>\n <tbody>\n {transactions.slice(0, 5).map((txn) => (\n <tr key={txn.id} className=\"border-b hover:bg-bg-sunken/50\">\n <td className=\"py-2 capitalize\">{txn.type}</td>\n <td className=\"py-2 truncate max-w-xs\">{txn.description}</td>\n <td\n className={`py-2 text-right font-medium ${txn.amount < 0 ? 'text-status-error-text' : ''}`}\n >\n ${Math.abs(txn.amount).toLocaleString()}\n </td>\n <td className=\"py-2\">\n <Badge variant=\"outline\">{txn.status}</Badge>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </CardContent>\n </Card>\n </TabsContent>\n\n {/* Operational Tab */}\n <TabsContent value=\"operational\" className=\"space-y-6 mt-6\">\n {/* Utilization rollup (from backend utilizationReport) */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Gauge className=\"h-4 w-4\" />\n Asset Utilization\n </CardTitle>\n <CardDescription>\n Assignment coverage and idle capacity across the estate\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n <div className=\"grid gap-4 md:grid-cols-3 lg:grid-cols-5\">\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Box className=\"h-3.5 w-3.5\" />\n Total Assets\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {utilizationReport?.totalAssets ?? '—'}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Users className=\"h-3.5 w-3.5\" />\n Assigned\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {utilizationReport?.assignedAssets ?? '—'}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Box className=\"h-3.5 w-3.5\" />\n Unassigned\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {utilizationReport?.unassignedAssets ?? '—'}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <Activity className=\"h-3.5 w-3.5\" />\n Idle\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1\">\n {utilizationReport?.idleAssets ?? '—'}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-seam p-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <AlertTriangle className=\"h-3.5 w-3.5 text-status-error-text\" />\n Overdue\n </div>\n <p className=\"text-xl font-bold tabular-nums mt-1 text-status-error-text\">\n {utilizationReport?.overdueAssignments ?? '—'}\n </p>\n </div>\n </div>\n\n <div>\n <h4 className=\"text-sm font-medium mb-3\">Assets by Status</h4>\n {utilizationReport && utilizationReport.byStatus.length > 0 ? (\n <div className=\"space-y-3\">\n {utilizationReport.byStatus.map(({ status, count }) => (\n <div key={status} className=\"flex items-center justify-between\">\n <Badge variant={statusVariant(status)} className=\"capitalize\">\n {status.replace(/_/g, ' ')}\n </Badge>\n <div className=\"flex items-center gap-3\">\n <div className=\"h-2 w-24 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${Math.round((count / maxStatusCount) * 100)}%` }}\n />\n </div>\n <span className=\"text-sm font-medium w-8 text-right tabular-nums\">\n {count}\n </span>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">—</p>\n )}\n </div>\n </CardContent>\n </Card>\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Incidents</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{incidents.length}</div>\n <p className=\"text-xs text-text-secondary\">{resolvedIncidents.length} resolved</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">SLA Breaches</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold text-status-error-text\">\n {slaBreached.length}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {((1 - slaBreached.length / Math.max(incidents.length, 1)) * 100).toFixed(1)}%\n within SLA\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Maintenance Done</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{completedMaintenance.length}</div>\n <p className=\"text-xs text-text-secondary\">of {maintenanceRecords.length} total</p>\n </CardContent>\n </Card>\n <Card>\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-sm font-medium\">Open Work Orders</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{openWorkOrders.length}</div>\n <p className=\"text-xs text-text-secondary\">of {workOrders.length} total</p>\n </CardContent>\n </Card>\n </div>\n </TabsContent>\n\n {/* Insights Tab */}\n <TabsContent value=\"insights\" className=\"space-y-6 mt-6\">\n <div className=\"grid gap-4 md:grid-cols-3\">\n <Card className=\"border-status-info-border bg-status-info-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-info-text text-base\">Diversification</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-info-text\">\n Your estate spans {Object.keys(categoryBreakdown).length} categories across{' '}\n {assets.length} assets. {activeAssets} are currently active.\n </p>\n </CardContent>\n </Card>\n <Card className=\"border-status-success-border bg-status-success-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-success-text text-base\">Budget</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-success-text\">\n ${budgetSpent.toLocaleString()} spent of ${budgetAllocated.toLocaleString()}{' '}\n allocated, leaving ${budgetRemaining.toLocaleString()} across {budgets.length}{' '}\n budgets.\n </p>\n </CardContent>\n </Card>\n <Card className=\"border-status-warning-border bg-status-warning-bg-subtle\">\n <CardHeader>\n <CardTitle className=\"text-status-warning-text text-base\">Maintenance</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-status-warning-text\">\n {scheduledMaintenance.length} maintenance tasks are scheduled. Total maintenance\n spend is ${maintenanceCost.toLocaleString()}. Stay on top of preventive\n maintenance.\n </p>\n </CardContent>\n </Card>\n </div>\n\n {/* Assets needing attention */}\n <EmphasisPanel>\n <CardHeader className=\"px-0 pt-0\">\n <CardTitle className=\"flex items-center gap-2\">\n <Wrench className=\"h-4 w-4\" />\n Assets Needing Attention\n </CardTitle>\n <CardDescription>\n Assets in poor or damaged condition, or currently under maintenance\n </CardDescription>\n </CardHeader>\n <CardContent className=\"px-0 pb-0\">\n {attentionAssets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-success\"\n title=\"Everything looks healthy\"\n description=\"No assets are flagged as poor, damaged or in maintenance right now.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {attentionAssets.slice(0, 6).map((asset, i) => (\n <div\n key={asset.id}\n className=\"flex items-center justify-between p-3 rounded-lg border border-border-seam bg-gloss-card\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center text-xs font-medium text-primary\">\n {i + 1}\n </div>\n <div>\n <p className=\"text-sm font-medium\">{asset.name}</p>\n <p className=\"text-xs text-text-secondary capitalize\">\n {asset.category.replace('_', ' ')}\n {asset.location ? ` · ${asset.location}` : ''}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"outline\" className=\"capitalize\">\n {asset.condition}\n </Badge>\n <Badge variant={statusVariant(asset.status)}>\n {asset.status.replace('_', ' ')}\n </Badge>\n </div>\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </EmphasisPanel>\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -200,40 +200,43 @@ function j() {
|
|
|
200
200
|
}),
|
|
201
201
|
/* @__PURE__ */ f(S, {
|
|
202
202
|
defaultValue: "all",
|
|
203
|
-
children: [/* @__PURE__ */ f(w, {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
203
|
+
children: [/* @__PURE__ */ f(w, {
|
|
204
|
+
className: "max-w-full justify-start overflow-x-auto",
|
|
205
|
+
children: [
|
|
206
|
+
/* @__PURE__ */ f(T, {
|
|
207
|
+
value: "all",
|
|
208
|
+
children: [
|
|
209
|
+
"All (",
|
|
210
|
+
$.length,
|
|
211
|
+
")"
|
|
212
|
+
]
|
|
213
|
+
}),
|
|
214
|
+
/* @__PURE__ */ f(T, {
|
|
215
|
+
value: "active",
|
|
216
|
+
children: [
|
|
217
|
+
"Active (",
|
|
218
|
+
X.length,
|
|
219
|
+
")"
|
|
220
|
+
]
|
|
221
|
+
}),
|
|
222
|
+
/* @__PURE__ */ f(T, {
|
|
223
|
+
value: "overdue",
|
|
224
|
+
children: [
|
|
225
|
+
"Overdue (",
|
|
226
|
+
Z.length,
|
|
227
|
+
")"
|
|
228
|
+
]
|
|
229
|
+
}),
|
|
230
|
+
/* @__PURE__ */ f(T, {
|
|
231
|
+
value: "returned",
|
|
232
|
+
children: [
|
|
233
|
+
"Returned (",
|
|
234
|
+
Q.length,
|
|
235
|
+
")"
|
|
236
|
+
]
|
|
237
|
+
})
|
|
238
|
+
]
|
|
239
|
+
}), [
|
|
237
240
|
["all", null],
|
|
238
241
|
["active", "CHECKED_OUT"],
|
|
239
242
|
["overdue", "OVERDUE"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetsAssetCheckInOutPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetCheckInOutPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n Input,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowDownToLine, ArrowUpFromLine, Clock, Search, AlertTriangle } from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\n\n// ─── Backend types (mirror wspace-assethandler-svc AssetAssignment) ───────────\ntype AssignmentStatus = 'CHECKED_OUT' | 'OVERDUE' | 'RETURNED';\n\ninterface AssetAssignment {\n id: string;\n assetId: string;\n assetName: string;\n assignedTo: string;\n assignedBy: string;\n checkedOutAt: string;\n dueAt: string | null;\n checkedInAt: string | null;\n status: AssignmentStatus;\n notes: string | null;\n}\n\nconst ASSET_ASSIGNMENTS_QUERY = gql`\n query AssetHandlerAssetAssignments {\n assetAssignments {\n id\n assetId\n assetName\n assignedTo\n assignedBy\n checkedOutAt\n dueAt\n checkedInAt\n status\n notes\n }\n }\n`;\n\nconst CHECK_OUT_ASSET_MUTATION = gql`\n mutation AssetHandlerCheckOutAsset($input: CheckOutAssetInput!) {\n checkOutAsset(input: $input) {\n id\n status\n }\n }\n`;\n\nconst CHECK_IN_ASSET_MUTATION = gql`\n mutation AssetHandlerCheckInAsset($input: CheckInAssetInput!) {\n checkInAsset(input: $input) {\n id\n status\n }\n }\n`;\n\nconst statusColors: Record<AssignmentStatus, 'default' | 'secondary' | 'destructive' | 'outline'> =\n {\n CHECKED_OUT: 'default',\n RETURNED: 'secondary',\n OVERDUE: 'destructive',\n };\n\nconst statusLabels: Record<AssignmentStatus, string> = {\n CHECKED_OUT: 'checked out',\n RETURNED: 'returned',\n OVERDUE: 'overdue',\n};\n\nexport function AssetsAssetCheckInOutPage() {\n const { assets } = useAssetHandlerStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [showCheckout, setShowCheckout] = useState(false);\n const [assetId, setAssetId] = useState('');\n const [assignedTo, setAssignedTo] = useState('');\n const [dueAt, setDueAt] = useState('');\n const [notes, setNotes] = useState('');\n\n const { data, loading, refetch } = useQuery<{ assetAssignments: AssetAssignment[] }>(\n ASSET_ASSIGNMENTS_QUERY,\n { fetchPolicy: 'cache-and-network' }\n );\n\n const [checkOutAsset, { loading: checkingOut }] = useMutation(CHECK_OUT_ASSET_MUTATION, {\n onCompleted: () => {\n notify.success('Asset checked out', 'The assignment has been recorded.');\n setShowCheckout(false);\n setAssetId('');\n setAssignedTo('');\n setDueAt('');\n setNotes('');\n void refetch();\n },\n onError: (error) => notify.error('Check-out failed', error.message),\n });\n\n const [checkInAsset, { loading: checkingIn }] = useMutation(CHECK_IN_ASSET_MUTATION, {\n onCompleted: () => {\n notify.success('Asset checked in', 'The asset has been returned.');\n void refetch();\n },\n onError: (error) => notify.error('Check-in failed', error.message),\n });\n\n const assignments = useMemo(() => data?.assetAssignments ?? [], [data]);\n\n const activeCheckouts = assignments.filter((a) => a.status === 'CHECKED_OUT');\n const overdueCheckouts = assignments.filter((a) => a.status === 'OVERDUE');\n const returnedRecords = assignments.filter((a) => a.status === 'RETURNED');\n\n const filteredRecords = assignments.filter(\n (a) =>\n searchQuery === '' ||\n a.assetName.toLowerCase().includes(searchQuery.toLowerCase()) ||\n a.assignedTo.toLowerCase().includes(searchQuery.toLowerCase())\n );\n\n const submitCheckout = (event: React.FormEvent) => {\n event.preventDefault();\n if (!assetId || !assignedTo.trim()) {\n notify.warning('Missing details', 'Choose an asset and who is receiving it.');\n return;\n }\n void checkOutAsset({\n variables: {\n input: {\n assetId,\n assignedTo: assignedTo.trim(),\n dueAt: dueAt ? new Date(dueAt).toISOString() : null,\n notes: notes.trim() || null,\n },\n },\n });\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold\">\n <GradientText>Asset Check-In / Check-Out</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Manage asset assignments and returns</p>\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" onClick={() => setShowCheckout((v) => !v)}>\n <ArrowUpFromLine className=\"mr-2 h-4 w-4\" />\n Check Out\n </Button>\n </div>\n </div>\n\n <PagePurpose>\n Know who has what, right now. Check assets out to people for a job or loan period and check\n them back in on return. Use it to chase overdue items and make sure nothing walks off\n without a trace.\n </PagePurpose>\n\n {showCheckout && (\n <EmphasisPanel>\n <h2 className=\"text-sm font-semibold mb-3\">Check out an asset</h2>\n <form className=\"grid gap-3 md:grid-cols-2 lg:grid-cols-4\" onSubmit={submitCheckout}>\n <select\n className=\"rounded-md border px-3 py-2 bg-bg-base\"\n value={assetId}\n onChange={(event) => setAssetId(event.target.value)}\n aria-label=\"Asset to check out\"\n >\n <option value=\"\">Select an asset…</option>\n {assets.map((asset) => (\n <option key={asset.id} value={asset.id}>\n {asset.name} ({asset.assetTag})\n </option>\n ))}\n </select>\n <Input\n placeholder=\"Assign to (name or email)\"\n value={assignedTo}\n onChange={(event) => setAssignedTo(event.target.value)}\n />\n <Input\n type=\"date\"\n aria-label=\"Due date\"\n value={dueAt}\n onChange={(event) => setDueAt(event.target.value)}\n />\n <Input\n placeholder=\"Notes (optional)\"\n value={notes}\n onChange={(event) => setNotes(event.target.value)}\n />\n <div className=\"md:col-span-2 lg:col-span-4 flex gap-2\">\n <Button type=\"submit\" size=\"sm\" disabled={checkingOut}>\n {checkingOut ? 'Checking out…' : 'Confirm check-out'}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setShowCheckout(false)}\n >\n Cancel\n </Button>\n </div>\n </form>\n </EmphasisPanel>\n )}\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <EmphasisPanel>\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <ArrowUpFromLine className=\"h-4 w-4\" />\n Active Checkouts\n </div>\n <p className=\"text-2xl font-bold mt-1 tabular-nums\">{activeCheckouts.length}</p>\n </EmphasisPanel>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <AlertTriangle className=\"h-4 w-4 text-status-error-text\" />\n Overdue\n </div>\n <p className=\"text-2xl font-bold mt-1 text-status-error-text\">\n {overdueCheckouts.length}\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <ArrowDownToLine className=\"h-4 w-4\" />\n Returned\n </div>\n <p className=\"text-2xl font-bold mt-1\">{returnedRecords.length}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Clock className=\"h-4 w-4\" />\n Total Records\n </div>\n <p className=\"text-2xl font-bold mt-1\">{assignments.length}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"relative max-w-md\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <Input\n placeholder=\"Search by asset or person…\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n\n <Tabs defaultValue=\"all\">\n <TabsList>\n <TabsTrigger value=\"all\">All ({filteredRecords.length})</TabsTrigger>\n <TabsTrigger value=\"active\">Active ({activeCheckouts.length})</TabsTrigger>\n <TabsTrigger value=\"overdue\">Overdue ({overdueCheckouts.length})</TabsTrigger>\n <TabsTrigger value=\"returned\">Returned ({returnedRecords.length})</TabsTrigger>\n </TabsList>\n {(\n [\n ['all', null],\n ['active', 'CHECKED_OUT'],\n ['overdue', 'OVERDUE'],\n ['returned', 'RETURNED'],\n ] as const\n ).map(([tab, status]) => {\n const records =\n status === null ? filteredRecords : filteredRecords.filter((r) => r.status === status);\n return (\n <TabsContent key={tab} value={tab} className=\"mt-4\">\n <Card>\n <CardContent className=\"p-0\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-bg-sunken/50\">\n <th className=\"text-left p-3 font-medium\">Asset</th>\n <th className=\"text-left p-3 font-medium\">Assigned To</th>\n <th className=\"text-left p-3 font-medium\">Checked Out</th>\n <th className=\"text-left p-3 font-medium\">Due</th>\n <th className=\"text-left p-3 font-medium\">Returned</th>\n <th className=\"text-left p-3 font-medium\">Status</th>\n <th className=\"text-right p-3 font-medium\">Action</th>\n </tr>\n </thead>\n <tbody>\n {records.map((rec) => (\n <tr key={rec.id} className=\"border-b hover:bg-bg-sunken/50\">\n <td className=\"p-3\">\n <p className=\"font-medium\">{rec.assetName}</p>\n <p className=\"text-xs text-text-secondary\">{rec.assetId}</p>\n </td>\n <td className=\"p-3\">\n <p className=\"text-sm\">{rec.assignedTo}</p>\n <p className=\"text-xs text-text-secondary\">by {rec.assignedBy}</p>\n </td>\n <td className=\"p-3 text-xs\">\n {new Date(rec.checkedOutAt).toLocaleDateString()}\n </td>\n <td className=\"p-3 text-xs\">\n {rec.dueAt ? new Date(rec.dueAt).toLocaleDateString() : '—'}\n </td>\n <td className=\"p-3 text-xs\">\n {rec.checkedInAt\n ? new Date(rec.checkedInAt).toLocaleDateString()\n : '—'}\n </td>\n <td className=\"p-3\">\n <Badge variant={statusColors[rec.status]}>\n {statusLabels[rec.status]}\n </Badge>\n </td>\n <td className=\"p-3 text-right\">\n {rec.status !== 'RETURNED' ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={checkingIn}\n onClick={() =>\n void checkInAsset({\n variables: { input: { assignmentId: rec.id } },\n })\n }\n >\n <ArrowDownToLine className=\"mr-1.5 h-3.5 w-3.5\" />\n Check In\n </Button>\n ) : null}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n {records.length === 0 && !loading && (\n <IllustratedEmptyState\n illustration={searchQuery ? 'empty-search' : 'empty-people'}\n title={searchQuery ? 'No matching records' : 'No check-in / out records'}\n description={\n searchQuery\n ? 'No records match your search — try a different name.'\n : 'Check an asset out to someone and the activity will appear here.'\n }\n />\n )}\n {loading && records.length === 0 ? (\n <div className=\"space-y-2 p-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={i}\n className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : null}\n </div>\n </CardContent>\n </Card>\n </TabsContent>\n );\n })}\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AssetsAssetCheckInOutPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetCheckInOutPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n Input,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowDownToLine, ArrowUpFromLine, Clock, Search, AlertTriangle } from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\nimport { notify } from '../utils/toast';\n\n// ─── Backend types (mirror wspace-assethandler-svc AssetAssignment) ───────────\ntype AssignmentStatus = 'CHECKED_OUT' | 'OVERDUE' | 'RETURNED';\n\ninterface AssetAssignment {\n id: string;\n assetId: string;\n assetName: string;\n assignedTo: string;\n assignedBy: string;\n checkedOutAt: string;\n dueAt: string | null;\n checkedInAt: string | null;\n status: AssignmentStatus;\n notes: string | null;\n}\n\nconst ASSET_ASSIGNMENTS_QUERY = gql`\n query AssetHandlerAssetAssignments {\n assetAssignments {\n id\n assetId\n assetName\n assignedTo\n assignedBy\n checkedOutAt\n dueAt\n checkedInAt\n status\n notes\n }\n }\n`;\n\nconst CHECK_OUT_ASSET_MUTATION = gql`\n mutation AssetHandlerCheckOutAsset($input: CheckOutAssetInput!) {\n checkOutAsset(input: $input) {\n id\n status\n }\n }\n`;\n\nconst CHECK_IN_ASSET_MUTATION = gql`\n mutation AssetHandlerCheckInAsset($input: CheckInAssetInput!) {\n checkInAsset(input: $input) {\n id\n status\n }\n }\n`;\n\nconst statusColors: Record<AssignmentStatus, 'default' | 'secondary' | 'destructive' | 'outline'> =\n {\n CHECKED_OUT: 'default',\n RETURNED: 'secondary',\n OVERDUE: 'destructive',\n };\n\nconst statusLabels: Record<AssignmentStatus, string> = {\n CHECKED_OUT: 'checked out',\n RETURNED: 'returned',\n OVERDUE: 'overdue',\n};\n\nexport function AssetsAssetCheckInOutPage() {\n const { assets } = useAssetHandlerStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [showCheckout, setShowCheckout] = useState(false);\n const [assetId, setAssetId] = useState('');\n const [assignedTo, setAssignedTo] = useState('');\n const [dueAt, setDueAt] = useState('');\n const [notes, setNotes] = useState('');\n\n const { data, loading, refetch } = useQuery<{ assetAssignments: AssetAssignment[] }>(\n ASSET_ASSIGNMENTS_QUERY,\n { fetchPolicy: 'cache-and-network' }\n );\n\n const [checkOutAsset, { loading: checkingOut }] = useMutation(CHECK_OUT_ASSET_MUTATION, {\n onCompleted: () => {\n notify.success('Asset checked out', 'The assignment has been recorded.');\n setShowCheckout(false);\n setAssetId('');\n setAssignedTo('');\n setDueAt('');\n setNotes('');\n void refetch();\n },\n onError: (error) => notify.error('Check-out failed', error.message),\n });\n\n const [checkInAsset, { loading: checkingIn }] = useMutation(CHECK_IN_ASSET_MUTATION, {\n onCompleted: () => {\n notify.success('Asset checked in', 'The asset has been returned.');\n void refetch();\n },\n onError: (error) => notify.error('Check-in failed', error.message),\n });\n\n const assignments = useMemo(() => data?.assetAssignments ?? [], [data]);\n\n const activeCheckouts = assignments.filter((a) => a.status === 'CHECKED_OUT');\n const overdueCheckouts = assignments.filter((a) => a.status === 'OVERDUE');\n const returnedRecords = assignments.filter((a) => a.status === 'RETURNED');\n\n const filteredRecords = assignments.filter(\n (a) =>\n searchQuery === '' ||\n a.assetName.toLowerCase().includes(searchQuery.toLowerCase()) ||\n a.assignedTo.toLowerCase().includes(searchQuery.toLowerCase())\n );\n\n const submitCheckout = (event: React.FormEvent) => {\n event.preventDefault();\n if (!assetId || !assignedTo.trim()) {\n notify.warning('Missing details', 'Choose an asset and who is receiving it.');\n return;\n }\n void checkOutAsset({\n variables: {\n input: {\n assetId,\n assignedTo: assignedTo.trim(),\n dueAt: dueAt ? new Date(dueAt).toISOString() : null,\n notes: notes.trim() || null,\n },\n },\n });\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold\">\n <GradientText>Asset Check-In / Check-Out</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Manage asset assignments and returns</p>\n </div>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" onClick={() => setShowCheckout((v) => !v)}>\n <ArrowUpFromLine className=\"mr-2 h-4 w-4\" />\n Check Out\n </Button>\n </div>\n </div>\n\n <PagePurpose>\n Know who has what, right now. Check assets out to people for a job or loan period and check\n them back in on return. Use it to chase overdue items and make sure nothing walks off\n without a trace.\n </PagePurpose>\n\n {showCheckout && (\n <EmphasisPanel>\n <h2 className=\"text-sm font-semibold mb-3\">Check out an asset</h2>\n <form className=\"grid gap-3 md:grid-cols-2 lg:grid-cols-4\" onSubmit={submitCheckout}>\n <select\n className=\"rounded-md border px-3 py-2 bg-bg-base\"\n value={assetId}\n onChange={(event) => setAssetId(event.target.value)}\n aria-label=\"Asset to check out\"\n >\n <option value=\"\">Select an asset…</option>\n {assets.map((asset) => (\n <option key={asset.id} value={asset.id}>\n {asset.name} ({asset.assetTag})\n </option>\n ))}\n </select>\n <Input\n placeholder=\"Assign to (name or email)\"\n value={assignedTo}\n onChange={(event) => setAssignedTo(event.target.value)}\n />\n <Input\n type=\"date\"\n aria-label=\"Due date\"\n value={dueAt}\n onChange={(event) => setDueAt(event.target.value)}\n />\n <Input\n placeholder=\"Notes (optional)\"\n value={notes}\n onChange={(event) => setNotes(event.target.value)}\n />\n <div className=\"md:col-span-2 lg:col-span-4 flex gap-2\">\n <Button type=\"submit\" size=\"sm\" disabled={checkingOut}>\n {checkingOut ? 'Checking out…' : 'Confirm check-out'}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setShowCheckout(false)}\n >\n Cancel\n </Button>\n </div>\n </form>\n </EmphasisPanel>\n )}\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <EmphasisPanel>\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <ArrowUpFromLine className=\"h-4 w-4\" />\n Active Checkouts\n </div>\n <p className=\"text-2xl font-bold mt-1 tabular-nums\">{activeCheckouts.length}</p>\n </EmphasisPanel>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <AlertTriangle className=\"h-4 w-4 text-status-error-text\" />\n Overdue\n </div>\n <p className=\"text-2xl font-bold mt-1 text-status-error-text\">\n {overdueCheckouts.length}\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <ArrowDownToLine className=\"h-4 w-4\" />\n Returned\n </div>\n <p className=\"text-2xl font-bold mt-1\">{returnedRecords.length}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Clock className=\"h-4 w-4\" />\n Total Records\n </div>\n <p className=\"text-2xl font-bold mt-1\">{assignments.length}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"relative max-w-md\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <Input\n placeholder=\"Search by asset or person…\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n\n <Tabs defaultValue=\"all\">\n <TabsList className=\"max-w-full justify-start overflow-x-auto\">\n <TabsTrigger value=\"all\">All ({filteredRecords.length})</TabsTrigger>\n <TabsTrigger value=\"active\">Active ({activeCheckouts.length})</TabsTrigger>\n <TabsTrigger value=\"overdue\">Overdue ({overdueCheckouts.length})</TabsTrigger>\n <TabsTrigger value=\"returned\">Returned ({returnedRecords.length})</TabsTrigger>\n </TabsList>\n {(\n [\n ['all', null],\n ['active', 'CHECKED_OUT'],\n ['overdue', 'OVERDUE'],\n ['returned', 'RETURNED'],\n ] as const\n ).map(([tab, status]) => {\n const records =\n status === null ? filteredRecords : filteredRecords.filter((r) => r.status === status);\n return (\n <TabsContent key={tab} value={tab} className=\"mt-4\">\n <Card>\n <CardContent className=\"p-0\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-bg-sunken/50\">\n <th className=\"text-left p-3 font-medium\">Asset</th>\n <th className=\"text-left p-3 font-medium\">Assigned To</th>\n <th className=\"text-left p-3 font-medium\">Checked Out</th>\n <th className=\"text-left p-3 font-medium\">Due</th>\n <th className=\"text-left p-3 font-medium\">Returned</th>\n <th className=\"text-left p-3 font-medium\">Status</th>\n <th className=\"text-right p-3 font-medium\">Action</th>\n </tr>\n </thead>\n <tbody>\n {records.map((rec) => (\n <tr key={rec.id} className=\"border-b hover:bg-bg-sunken/50\">\n <td className=\"p-3\">\n <p className=\"font-medium\">{rec.assetName}</p>\n <p className=\"text-xs text-text-secondary\">{rec.assetId}</p>\n </td>\n <td className=\"p-3\">\n <p className=\"text-sm\">{rec.assignedTo}</p>\n <p className=\"text-xs text-text-secondary\">by {rec.assignedBy}</p>\n </td>\n <td className=\"p-3 text-xs\">\n {new Date(rec.checkedOutAt).toLocaleDateString()}\n </td>\n <td className=\"p-3 text-xs\">\n {rec.dueAt ? new Date(rec.dueAt).toLocaleDateString() : '—'}\n </td>\n <td className=\"p-3 text-xs\">\n {rec.checkedInAt\n ? new Date(rec.checkedInAt).toLocaleDateString()\n : '—'}\n </td>\n <td className=\"p-3\">\n <Badge variant={statusColors[rec.status]}>\n {statusLabels[rec.status]}\n </Badge>\n </td>\n <td className=\"p-3 text-right\">\n {rec.status !== 'RETURNED' ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={checkingIn}\n onClick={() =>\n void checkInAsset({\n variables: { input: { assignmentId: rec.id } },\n })\n }\n >\n <ArrowDownToLine className=\"mr-1.5 h-3.5 w-3.5\" />\n Check In\n </Button>\n ) : null}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n {records.length === 0 && !loading && (\n <IllustratedEmptyState\n illustration={searchQuery ? 'empty-search' : 'empty-people'}\n title={searchQuery ? 'No matching records' : 'No check-in / out records'}\n description={\n searchQuery\n ? 'No records match your search — try a different name.'\n : 'Check an asset out to someone and the activity will appear here.'\n }\n />\n )}\n {loading && records.length === 0 ? (\n <div className=\"space-y-2 p-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={i}\n className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\"\n />\n ))}\n </div>\n ) : null}\n </div>\n </CardContent>\n </Card>\n </TabsContent>\n );\n })}\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -111,6 +111,7 @@ function A() {
|
|
|
111
111
|
children: [/* @__PURE__ */ l(d, {
|
|
112
112
|
variant: "ghost",
|
|
113
113
|
size: "icon",
|
|
114
|
+
"aria-label": "Go back",
|
|
114
115
|
onClick: () => s("/assets/asset-list"),
|
|
115
116
|
children: /* @__PURE__ */ l(r, { className: "h-4 w-4" })
|
|
116
117
|
}), /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h1", {
|