@burdenoff/microfe-assethandler 2026.703.1 → 2026.703.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.
Files changed (27) hide show
  1. package/dist/assethandler/pages/AiPredictionsPage.js.map +1 -1
  2. package/dist/assethandler/pages/AnalyticsPage.js.map +1 -1
  3. package/dist/assethandler/pages/AssetsAssetCategoriesPage.js.map +1 -1
  4. package/dist/assethandler/pages/AssetsAssetCheckInOutPage.js.map +1 -1
  5. package/dist/assethandler/pages/AssetsAssetTrackingPage.js.map +1 -1
  6. package/dist/assethandler/pages/DigitalTwinPage.js.map +1 -1
  7. package/dist/assethandler/pages/DiscoveryJobsPage.js.map +1 -1
  8. package/dist/assethandler/pages/EsgDashboardPage.js.map +1 -1
  9. package/dist/assethandler/pages/FinanceBudgetDetailPage.js.map +1 -1
  10. package/dist/assethandler/pages/FinanceFinanceHubPage.js.map +1 -1
  11. package/dist/assethandler/pages/ItilChangeManagementPage.js.map +1 -1
  12. package/dist/assethandler/pages/ItilIncidentManagementPage.js.map +1 -1
  13. package/dist/assethandler/pages/ItilItilhubPage.js +1 -2
  14. package/dist/assethandler/pages/ItilItilhubPage.js.map +1 -1
  15. package/dist/assethandler/pages/ItilProblemManagementPage.js.map +1 -1
  16. package/dist/assethandler/pages/KnowledgeBasePage.js.map +1 -1
  17. package/dist/assethandler/pages/MaintenanceMaintenanceCalendarPage.js.map +1 -1
  18. package/dist/assethandler/pages/MaintenanceMaintenanceSchedulerPage.js.map +1 -1
  19. package/dist/assethandler/pages/OperationsMaintenanceDetailPage.js.map +1 -1
  20. package/dist/assethandler/pages/OperationsOperationsHubPage.js.map +1 -1
  21. package/dist/assethandler/pages/OperationsWorkOrderDetailPage.js.map +1 -1
  22. package/dist/assethandler/pages/PortfolioPortfolioOverviewPage.js.map +1 -1
  23. package/dist/assethandler/pages/ProvenancePage.js.map +1 -1
  24. package/dist/assethandler/pages/SecuritySecurityHubPage.js.map +1 -1
  25. package/dist/assethandler/pages/TransactionsTransactionDetailPage.js.map +1 -1
  26. package/dist/assethandler/store/assethandlerStore.js.map +1 -1
  27. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AiPredictionsPage.js","names":[],"sources":["../../../src/assethandler/pages/AiPredictionsPage.tsx"],"sourcesContent":["import { BrainCircuit } from 'lucide-react';\nimport { GradientText, IllustratedEmptyState, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\n\nconst AI_PREDICTIONS_QUERY = gql`\n query AssetHandlerAiPredictions {\n aiPredictions {\n id\n assetId\n predictionType\n score\n confidence\n recommendation\n generatedAt\n modelVersion\n }\n }\n`;\n\nconst REQUEST_AI_PREDICTION_MUTATION = gql`\n mutation AssetHandlerRequestAiPrediction($input: RequestAIPredictionInput!) {\n requestAIPrediction(input: $input) {\n id\n }\n }\n`;\n\nexport function AiPredictionsPage() {\n const [assetId, setAssetId] = useState('AST-001');\n\n const { data, loading, refetch } = useQuery<{\n aiPredictions: Array<{\n id: string;\n assetId: string;\n predictionType: string;\n score: number;\n confidence: number;\n recommendation: string;\n generatedAt: string;\n modelVersion: string;\n }>;\n }>(AI_PREDICTIONS_QUERY, { fetchPolicy: 'cache-and-network' });\n\n const [requestPrediction, { loading: requesting }] = useMutation(REQUEST_AI_PREDICTION_MUTATION, {\n onCompleted: () => {\n void refetch();\n },\n });\n\n const predictions = data?.aiPredictions ?? [];\n\n return (\n <div className=\"space-y-4 p-4\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <BrainCircuit className=\"h-6 w-6\" /> <GradientText>AI Predictions</GradientText>\n </h1>\n <PagePurpose>\n Forecast failures before they happen. Enter an asset and run a model to get a risk score,\n confidence level and a concrete recommendation — so you can schedule preventive maintenance\n instead of reacting to breakdowns. Use it to prioritise which high-value or aging assets to\n service next.\n </PagePurpose>\n <EmphasisPanel>\n <h2 className=\"text-sm font-semibold mb-3\">Run a prediction</h2>\n <form\n className=\"grid gap-3 md:grid-cols-3\"\n onSubmit={(event) => {\n event.preventDefault();\n void requestPrediction({\n variables: {\n input: {\n assetId,\n predictionType: 'maintenance-risk',\n featureVector: {\n runtimeHours: 10240,\n lastIncidentDaysAgo: 9,\n temperatureVariance: 0.23,\n },\n },\n },\n });\n }}\n >\n <input\n className=\"rounded-md border px-3 py-2\"\n value={assetId}\n onChange={(event) => setAssetId(event.target.value)}\n aria-label=\"Asset ID\"\n />\n <div className=\"text-sm text-text-secondary\">Prediction Type: maintenance-risk</div>\n <button\n className=\"rounded-md bg-action-primary-bg px-3 py-2 text-action-primary-text\"\n disabled={requesting}\n type=\"submit\"\n >\n {requesting ? 'Running...' : 'Run Prediction'}\n </button>\n </form>\n </EmphasisPanel>\n <div className=\"grid gap-3 md:grid-cols-2\">\n {loading && predictions.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && predictions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No AI predictions available yet\"\n description=\"AI-generated predictions for your assets will appear here.\"\n />\n ) : null}\n {predictions.map((prediction) => (\n <article key={prediction.id} className=\"rounded-lg border p-4\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">\n {prediction.predictionType} • {prediction.assetId}\n </p>\n <h2 className=\"mt-1 text-base font-semibold\">\n Risk score {(prediction.score * 100).toFixed(1)}%\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Confidence {(prediction.confidence * 100).toFixed(1)}%\n </p>\n <p className=\"mt-2 text-sm\">{prediction.recommendation}</p>\n <p className=\"mt-2 text-xs text-text-secondary\">\n {prediction.modelVersion} • {new Date(prediction.generatedAt).toLocaleString()}\n </p>\n </article>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAMA,IAAM,IAAuB,CAAG;;;;;;;;;;;;;GAe1B,IAAiC,CAAG;;;;;;;AAQ1C,SAAgB,IAAoB;CAClC,IAAM,CAAC,GAAS,KAAc,EAAS,UAAU,EAE3C,EAAE,SAAM,YAAS,eAAY,EAWhC,GAAsB,EAAE,aAAa,qBAAqB,CAAC,EAExD,CAAC,GAAmB,EAAE,SAAS,OAAgB,EAAY,GAAgC,EAC/F,mBAAmB;AACZ,KAAS;IAEjB,CAAC,EAEI,IAAc,GAAM,iBAAiB,EAAE;AAE7C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,MAAD;IAAI,WAAU;cAAd;KACE,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;;KAAC,kBAAC,GAAD,EAAA,UAAc,kBAA6B,CAAA;KAC7E;;GACL,kBAAC,GAAD,EAAA,UAAa,mSAKC,CAAA;GACd,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAA6B;IAAqB,CAAA,EAChE,kBAAC,QAAD;IACE,WAAU;IACV,WAAW,MAAU;AAEhB,KADL,EAAM,gBAAgB,EACjB,EAAkB,EACrB,WAAW,EACT,OAAO;MACL;MACA,gBAAgB;MAChB,eAAe;OACb,cAAc;OACd,qBAAqB;OACrB,qBAAqB;OACtB;MACF,EACF,EACF,CAAC;;cAhBJ;KAmBE,kBAAC,SAAD;MACE,WAAU;MACV,OAAO;MACP,WAAW,MAAU,EAAW,EAAM,OAAO,MAAM;MACnD,cAAW;MACX,CAAA;KACF,kBAAC,OAAD;MAAK,WAAU;gBAA8B;MAAuC,CAAA;KACpF,kBAAC,UAAD;MACE,WAAU;MACV,UAAU;MACV,MAAK;gBAEJ,IAAa,eAAe;MACtB,CAAA;KACJ;MACO,EAAA,CAAA;GAChB,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,KAAW,EAAY,WAAW,IACjC,kBAAC,OAAD;MAAK,WAAU;gBACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAAa,WAAU,8DAA+D,EAA5E,EAA4E,CACtF;MACE,CAAA,GACJ;KACH,CAAC,KAAW,EAAY,WAAW,IAClC,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GACA;KACH,EAAY,KAAK,MAChB,kBAAC,WAAD;MAA6B,WAAU;gBAAvC;OACE,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAW;SAAe;SAAI,EAAW;SACxC;;OACJ,kBAAC,MAAD;QAAI,WAAU;kBAAd;SAA6C;UAC9B,EAAW,QAAQ,KAAK,QAAQ,EAAE;SAAC;SAC7C;;OACL,kBAAC,KAAD;QAAG,WAAU;kBAAb;SAAgD;UACjC,EAAW,aAAa,KAAK,QAAQ,EAAE;SAAC;SACnD;;OACJ,kBAAC,KAAD;QAAG,WAAU;kBAAgB,EAAW;QAAmB,CAAA;OAC3D,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAW;SAAa;SAAI,IAAI,KAAK,EAAW,YAAY,CAAC,gBAAgB;SAC5E;;OACI;QAdI,EAAW,GAcf,CACV;KACE;;GACF"}
1
+ {"version":3,"file":"AiPredictionsPage.js","names":[],"sources":["../../../src/assethandler/pages/AiPredictionsPage.tsx"],"sourcesContent":["import { BrainCircuit } from 'lucide-react';\nimport {\n GradientText,\n IllustratedEmptyState,\n EmphasisPanel,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\n\nconst AI_PREDICTIONS_QUERY = gql`\n query AssetHandlerAiPredictions {\n aiPredictions {\n id\n assetId\n predictionType\n score\n confidence\n recommendation\n generatedAt\n modelVersion\n }\n }\n`;\n\nconst REQUEST_AI_PREDICTION_MUTATION = gql`\n mutation AssetHandlerRequestAiPrediction($input: RequestAIPredictionInput!) {\n requestAIPrediction(input: $input) {\n id\n }\n }\n`;\n\nexport function AiPredictionsPage() {\n const [assetId, setAssetId] = useState('AST-001');\n\n const { data, loading, refetch } = useQuery<{\n aiPredictions: Array<{\n id: string;\n assetId: string;\n predictionType: string;\n score: number;\n confidence: number;\n recommendation: string;\n generatedAt: string;\n modelVersion: string;\n }>;\n }>(AI_PREDICTIONS_QUERY, { fetchPolicy: 'cache-and-network' });\n\n const [requestPrediction, { loading: requesting }] = useMutation(REQUEST_AI_PREDICTION_MUTATION, {\n onCompleted: () => {\n void refetch();\n },\n });\n\n const predictions = data?.aiPredictions ?? [];\n\n return (\n <div className=\"space-y-4 p-4\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <BrainCircuit className=\"h-6 w-6\" /> <GradientText>AI Predictions</GradientText>\n </h1>\n <PagePurpose>\n Forecast failures before they happen. Enter an asset and run a model to get a risk score,\n confidence level and a concrete recommendation — so you can schedule preventive maintenance\n instead of reacting to breakdowns. Use it to prioritise which high-value or aging assets to\n service next.\n </PagePurpose>\n <EmphasisPanel>\n <h2 className=\"text-sm font-semibold mb-3\">Run a prediction</h2>\n <form\n className=\"grid gap-3 md:grid-cols-3\"\n onSubmit={(event) => {\n event.preventDefault();\n void requestPrediction({\n variables: {\n input: {\n assetId,\n predictionType: 'maintenance-risk',\n featureVector: {\n runtimeHours: 10240,\n lastIncidentDaysAgo: 9,\n temperatureVariance: 0.23,\n },\n },\n },\n });\n }}\n >\n <input\n className=\"rounded-md border px-3 py-2\"\n value={assetId}\n onChange={(event) => setAssetId(event.target.value)}\n aria-label=\"Asset ID\"\n />\n <div className=\"text-sm text-text-secondary\">Prediction Type: maintenance-risk</div>\n <button\n className=\"rounded-md bg-action-primary-bg px-3 py-2 text-action-primary-text\"\n disabled={requesting}\n type=\"submit\"\n >\n {requesting ? 'Running...' : 'Run Prediction'}\n </button>\n </form>\n </EmphasisPanel>\n <div className=\"grid gap-3 md:grid-cols-2\">\n {loading && predictions.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && predictions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No AI predictions available yet\"\n description=\"AI-generated predictions for your assets will appear here.\"\n />\n ) : null}\n {predictions.map((prediction) => (\n <article key={prediction.id} className=\"rounded-lg border p-4\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">\n {prediction.predictionType} • {prediction.assetId}\n </p>\n <h2 className=\"mt-1 text-base font-semibold\">\n Risk score {(prediction.score * 100).toFixed(1)}%\n </h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Confidence {(prediction.confidence * 100).toFixed(1)}%\n </p>\n <p className=\"mt-2 text-sm\">{prediction.recommendation}</p>\n <p className=\"mt-2 text-xs text-text-secondary\">\n {prediction.modelVersion} • {new Date(prediction.generatedAt).toLocaleString()}\n </p>\n </article>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAWA,IAAM,IAAuB,CAAG;;;;;;;;;;;;;GAe1B,IAAiC,CAAG;;;;;;;AAQ1C,SAAgB,IAAoB;CAClC,IAAM,CAAC,GAAS,KAAc,EAAS,UAAU,EAE3C,EAAE,SAAM,YAAS,eAAY,EAWhC,GAAsB,EAAE,aAAa,qBAAqB,CAAC,EAExD,CAAC,GAAmB,EAAE,SAAS,OAAgB,EAAY,GAAgC,EAC/F,mBAAmB;AACZ,KAAS;IAEjB,CAAC,EAEI,IAAc,GAAM,iBAAiB,EAAE;AAE7C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,MAAD;IAAI,WAAU;cAAd;KACE,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;;KAAC,kBAAC,GAAD,EAAA,UAAc,kBAA6B,CAAA;KAC7E;;GACL,kBAAC,GAAD,EAAA,UAAa,mSAKC,CAAA;GACd,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAA6B;IAAqB,CAAA,EAChE,kBAAC,QAAD;IACE,WAAU;IACV,WAAW,MAAU;AAEd,KADL,EAAM,gBAAgB,EACjB,EAAkB,EACrB,WAAW,EACT,OAAO;MACL;MACA,gBAAgB;MAChB,eAAe;OACb,cAAc;OACd,qBAAqB;OACrB,qBAAqB;OACtB;MACF,EACF,EACF,CAAC;;cAhBN;KAmBE,kBAAC,SAAD;MACE,WAAU;MACV,OAAO;MACP,WAAW,MAAU,EAAW,EAAM,OAAO,MAAM;MACnD,cAAW;MACX,CAAA;KACF,kBAAC,OAAD;MAAK,WAAU;gBAA8B;MAAuC,CAAA;KACpF,kBAAC,UAAD;MACE,WAAU;MACV,UAAU;MACV,MAAK;gBAEJ,IAAa,eAAe;MACtB,CAAA;KACJ;MACO,EAAA,CAAA;GAChB,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,KAAW,EAAY,WAAW,IACjC,kBAAC,OAAD;MAAK,WAAU;gBACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAAa,WAAU,8DAA+D,EAA5E,EAA4E,CACtF;MACE,CAAA,GACJ;KACH,CAAC,KAAW,EAAY,WAAW,IAClC,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GACA;KACH,EAAY,KAAK,MAChB,kBAAC,WAAD;MAA6B,WAAU;gBAAvC;OACE,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAW;SAAe;SAAI,EAAW;SACxC;;OACJ,kBAAC,MAAD;QAAI,WAAU;kBAAd;SAA6C;UAC9B,EAAW,QAAQ,KAAK,QAAQ,EAAE;SAAC;SAC7C;;OACL,kBAAC,KAAD;QAAG,WAAU;kBAAb;SAAgD;UACjC,EAAW,aAAa,KAAK,QAAQ,EAAE;SAAC;SACnD;;OACJ,kBAAC,KAAD;QAAG,WAAU;kBAAgB,EAAW;QAAmB,CAAA;OAC3D,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAW;SAAa;SAAI,IAAI,KAAK,EAAW,YAAY,CAAC,gBAAgB;SAC5E;;OACI;QAdI,EAAW,GAcf,CACV;KACE;;GACF"}
@@ -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 { Box, PieChart, Activity, Wrench, AlertTriangle, Wallet } from 'lucide-react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\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 // ─── 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((i) => i.status !== 'paid' && i.status !== 'cancelled');\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 &amp; 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} &middot; {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 &amp; 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 <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\">\n ${budgetSpent.toLocaleString()} spent\n </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 <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 { Box, PieChart, Activity, Wrench, AlertTriangle, Wallet } from 'lucide-react';\nimport { useAssetHandlerStore } from '../store/assethandlerStore';\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 // ─── 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 &amp; 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} &middot; {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 &amp; 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 <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 <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 +1 @@
1
- {"version":3,"file":"AssetsAssetCategoriesPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Button,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Separator,\n GradientText,\n GlassCard,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { Box, Monitor, Server, Wifi, Truck, Wrench, Code, Plus } from 'lucide-react';\nimport { useAssetHandlerStore, type Asset } from '../store/assethandlerStore';\n\nconst categoryIcons: Record<string, React.ReactNode> = {\n electronics: <Monitor className=\"h-5 w-5\" />,\n it_hardware: <Monitor className=\"h-5 w-5\" />,\n furniture: <Box className=\"h-5 w-5\" />,\n vehicles: <Truck className=\"h-5 w-5\" />,\n equipment: <Wrench className=\"h-5 w-5\" />,\n software: <Code className=\"h-5 w-5\" />,\n networking: <Wifi className=\"h-5 w-5\" />,\n servers: <Server className=\"h-5 w-5\" />,\n};\n\nconst categoryColors: Record<string, string> = {\n electronics: 'bg-status-info-bg-subtle text-status-info-text',\n it_hardware: 'bg-status-info-bg-subtle text-status-info-text',\n furniture: 'bg-status-warning-bg-subtle text-status-warning-text',\n vehicles: 'bg-status-success-bg-subtle text-status-success-text',\n equipment: 'bg-status-warning-bg-subtle text-status-warning-text',\n software: 'bg-action-primary-bg/10 text-action-primary-bg',\n networking: 'bg-status-info-bg-subtle text-status-info-text',\n servers: 'bg-status-error-bg-subtle text-status-error-text',\n};\n\nexport function AssetsAssetCategoriesPage() {\n const { assets } = useAssetHandlerStore();\n\n const categories = useMemo(() => {\n const catMap: Record<\n string,\n {\n count: number;\n statuses: Record<string, number>;\n conditions: Record<string, number>;\n topAssets: Asset[];\n }\n > = {};\n for (const asset of assets) {\n if (!catMap[asset.category])\n catMap[asset.category] = {\n count: 0,\n statuses: {},\n conditions: {},\n topAssets: [],\n };\n const cat = catMap[asset.category];\n cat.count++;\n cat.statuses[asset.status] = (cat.statuses[asset.status] || 0) + 1;\n cat.conditions[asset.condition] = (cat.conditions[asset.condition] || 0) + 1;\n cat.topAssets.push(asset);\n }\n for (const cat of Object.values(catMap)) {\n cat.topAssets.sort((a, b) => a.name.localeCompare(b.name));\n cat.topAssets = cat.topAssets.slice(0, 3);\n }\n return catMap;\n }, [assets]);\n\n const totalAssets = assets.length;\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 Categories</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {Object.keys(categories).length} categories &middot; {totalAssets} total assets\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"mr-2 h-4 w-4\" />\n Add Category\n </Button>\n </div>\n\n <PagePurpose>\n Group and compare your assets by category to see where they are concentrated. Each card\n rolls up how many assets you have in a category, their status and condition mix, and a few\n example items — so you can plan procurement and replacements by category, not one asset at a\n time.\n </PagePurpose>\n\n {Object.keys(categories).length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No categories yet\"\n description=\"Add assets and they will be grouped into categories here automatically.\"\n />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {Object.entries(categories).map(([key, data], idx) => {\n const pct = totalAssets > 0 ? Math.round((data.count / totalAssets) * 100) : 0;\n return (\n <GlassCard\n key={key}\n treatment={idx === 0 ? 'glass' : 'plain'}\n glow={idx === 0}\n className=\"border-border-seam\"\n >\n <CardHeader className=\"pb-3\">\n <div className=\"flex items-center justify-between\">\n <div className={`p-2 rounded-lg ${categoryColors[key] || 'bg-bg-sunken'}`}>\n {categoryIcons[key] || <Box className=\"h-5 w-5\" />}\n </div>\n <Badge variant=\"outline\">{pct}%</Badge>\n </div>\n <CardTitle className=\"capitalize mt-3\">{key.replace('_', ' ')}</CardTitle>\n <CardDescription>{data.count} assets</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Share of estate</span>\n <span className=\"font-medium\">{pct}%</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 <Separator />\n <div>\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Status breakdown</p>\n <div className=\"flex flex-wrap gap-1\">\n {Object.entries(data.statuses).map(([status, count]) => (\n <Badge key={status} variant=\"secondary\" className=\"text-[10px]\">\n {status.replace('_', ' ')}: {count}\n </Badge>\n ))}\n </div>\n </div>\n <Separator />\n <div>\n <p className=\"text-xs font-medium text-text-secondary mb-2\">\n Condition breakdown\n </p>\n <div className=\"flex flex-wrap gap-1\">\n {Object.entries(data.conditions).map(([condition, count]) => (\n <Badge key={condition} variant=\"outline\" className=\"text-[10px]\">\n {condition}: {count}\n </Badge>\n ))}\n </div>\n </div>\n <Separator />\n <div>\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Example assets</p>\n <div className=\"space-y-1.5\">\n {data.topAssets.map((a) => (\n <div key={a.id} className=\"flex items-center justify-between text-xs\">\n <span className=\"truncate flex-1\">{a.name}</span>\n <span className=\"font-medium ml-2 capitalize text-text-secondary\">\n {a.condition}\n </span>\n </div>\n ))}\n </div>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AssetsAssetCategoriesPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetCategoriesPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Button,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Separator,\n GradientText,\n GlassCard,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { Box, Monitor, Server, Wifi, Truck, Wrench, Code, Plus } from 'lucide-react';\nimport { useAssetHandlerStore, type Asset } from '../store/assethandlerStore';\n\nconst categoryIcons: Record<string, React.ReactNode> = {\n electronics: <Monitor className=\"h-5 w-5\" />,\n it_hardware: <Monitor className=\"h-5 w-5\" />,\n furniture: <Box className=\"h-5 w-5\" />,\n vehicles: <Truck className=\"h-5 w-5\" />,\n equipment: <Wrench className=\"h-5 w-5\" />,\n software: <Code className=\"h-5 w-5\" />,\n networking: <Wifi className=\"h-5 w-5\" />,\n servers: <Server className=\"h-5 w-5\" />,\n};\n\nconst categoryColors: Record<string, string> = {\n electronics: 'bg-status-info-bg-subtle text-status-info-text',\n it_hardware: 'bg-status-info-bg-subtle text-status-info-text',\n furniture: 'bg-status-warning-bg-subtle text-status-warning-text',\n vehicles: 'bg-status-success-bg-subtle text-status-success-text',\n equipment: 'bg-status-warning-bg-subtle text-status-warning-text',\n software: 'bg-action-primary-bg/10 text-action-primary-bg',\n networking: 'bg-status-info-bg-subtle text-status-info-text',\n servers: 'bg-status-error-bg-subtle text-status-error-text',\n};\n\nexport function AssetsAssetCategoriesPage() {\n const { assets } = useAssetHandlerStore();\n\n const categories = useMemo(() => {\n const catMap: Record<\n string,\n {\n count: number;\n statuses: Record<string, number>;\n conditions: Record<string, number>;\n topAssets: Asset[];\n }\n > = {};\n for (const asset of assets) {\n if (!catMap[asset.category])\n catMap[asset.category] = {\n count: 0,\n statuses: {},\n conditions: {},\n topAssets: [],\n };\n const cat = catMap[asset.category];\n cat.count++;\n cat.statuses[asset.status] = (cat.statuses[asset.status] || 0) + 1;\n cat.conditions[asset.condition] = (cat.conditions[asset.condition] || 0) + 1;\n cat.topAssets.push(asset);\n }\n for (const cat of Object.values(catMap)) {\n cat.topAssets.sort((a, b) => a.name.localeCompare(b.name));\n cat.topAssets = cat.topAssets.slice(0, 3);\n }\n return catMap;\n }, [assets]);\n\n const totalAssets = assets.length;\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 Categories</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {Object.keys(categories).length} categories &middot; {totalAssets} total assets\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"mr-2 h-4 w-4\" />\n Add Category\n </Button>\n </div>\n\n <PagePurpose>\n Group and compare your assets by category to see where they are concentrated. Each card\n rolls up how many assets you have in a category, their status and condition mix, and a few\n example items — so you can plan procurement and replacements by category, not one asset at a\n time.\n </PagePurpose>\n\n {Object.keys(categories).length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No categories yet\"\n description=\"Add assets and they will be grouped into categories here automatically.\"\n />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {Object.entries(categories).map(([key, data], idx) => {\n const pct = totalAssets > 0 ? Math.round((data.count / totalAssets) * 100) : 0;\n return (\n <GlassCard\n key={key}\n treatment={idx === 0 ? 'glass' : 'plain'}\n glow={idx === 0}\n className=\"border-border-seam\"\n >\n <CardHeader className=\"pb-3\">\n <div className=\"flex items-center justify-between\">\n <div className={`p-2 rounded-lg ${categoryColors[key] || 'bg-bg-sunken'}`}>\n {categoryIcons[key] || <Box className=\"h-5 w-5\" />}\n </div>\n <Badge variant=\"outline\">{pct}%</Badge>\n </div>\n <CardTitle className=\"capitalize mt-3\">{key.replace('_', ' ')}</CardTitle>\n <CardDescription>{data.count} assets</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Share of estate</span>\n <span className=\"font-medium\">{pct}%</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 <Separator />\n <div>\n <p className=\"text-xs font-medium text-text-secondary mb-2\">\n Status breakdown\n </p>\n <div className=\"flex flex-wrap gap-1\">\n {Object.entries(data.statuses).map(([status, count]) => (\n <Badge key={status} variant=\"secondary\" className=\"text-[10px]\">\n {status.replace('_', ' ')}: {count}\n </Badge>\n ))}\n </div>\n </div>\n <Separator />\n <div>\n <p className=\"text-xs font-medium text-text-secondary mb-2\">\n Condition breakdown\n </p>\n <div className=\"flex flex-wrap gap-1\">\n {Object.entries(data.conditions).map(([condition, count]) => (\n <Badge key={condition} variant=\"outline\" className=\"text-[10px]\">\n {condition}: {count}\n </Badge>\n ))}\n </div>\n </div>\n <Separator />\n <div>\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Example assets</p>\n <div className=\"space-y-1.5\">\n {data.topAssets.map((a) => (\n <div key={a.id} className=\"flex items-center justify-between text-xs\">\n <span className=\"truncate flex-1\">{a.name}</span>\n <span className=\"font-medium ml-2 capitalize text-text-secondary\">\n {a.condition}\n </span>\n </div>\n ))}\n </div>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -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 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>\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 +1 @@
1
- {"version":3,"file":"AssetsAssetTrackingPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetTrackingPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Input,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { MapPin, Search, RefreshCw, Radio, Building, HelpCircle } from 'lucide-react';\nimport { useAssetHandlerStore, type Asset } from '../store/assethandlerStore';\n\nconst statusColors: Record<string, 'default' | 'secondary' | 'destructive' | 'outline'> = {\n active: 'default',\n inactive: 'outline',\n maintenance: 'secondary',\n retired: 'outline',\n disposed: 'destructive',\n};\n\nconst UNLOCATED = 'Unlocated';\n\nexport function AssetsAssetTrackingPage() {\n const { assets } = useAssetHandlerStore();\n\n const locationGroups = useMemo(\n () =>\n assets.reduce<Record<string, Asset[]>>((acc, asset) => {\n const key = asset.location.trim() || UNLOCATED;\n if (!acc[key]) acc[key] = [];\n acc[key].push(asset);\n return acc;\n }, {}),\n [assets]\n );\n\n const locatedCount = assets.filter((a) => a.location.trim()).length;\n const unlocatedCount = assets.length - locatedCount;\n const locationCount = Object.keys(locationGroups).filter((k) => k !== UNLOCATED).length;\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 Tracking</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Location overview for {assets.length} assets\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <RefreshCw className=\"mr-2 h-4 w-4\" />\n Refresh\n </Button>\n <Button size=\"sm\">\n <Radio className=\"mr-2 h-4 w-4\" />\n Track New Asset\n </Button>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Radio className=\"h-4 w-4\" />\n Located Assets\n </div>\n <p className=\"text-2xl font-bold mt-1\">{locatedCount}</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 <Building className=\"h-4 w-4\" />\n Locations\n </div>\n <p className=\"text-2xl font-bold mt-1\">{locationCount}</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 <HelpCircle className=\"h-4 w-4\" />\n Unlocated\n </div>\n <p className=\"text-2xl font-bold mt-1\">{unlocatedCount}</p>\n </CardContent>\n </Card>\n </div>\n\n <PagePurpose>\n Know where your assets physically are. See them grouped by location, so you can locate\n equipment fast, spot assets that still have no recorded location, and confirm items are\n where they should be.\n </PagePurpose>\n\n <div className=\"grid gap-6 lg:grid-cols-5\">\n <EmphasisPanel className=\"lg:col-span-3\">\n <CardHeader className=\"px-0 pt-0\">\n <CardTitle>Location Map</CardTitle>\n <CardDescription>Geographic distribution of tracked assets</CardDescription>\n </CardHeader>\n <CardContent className=\"px-0 pb-0\">\n <div className=\"bg-bg-sunken rounded-lg h-80 flex items-center justify-center border-2 border-dashed\">\n <div className=\"text-center\">\n <MapPin className=\"h-12 w-12 text-text-secondary mx-auto mb-2\" />\n <p className=\"text-text-secondary\">Interactive map view</p>\n <p className=\"text-xs text-text-secondary mt-1\">\n Map integration would render here\n </p>\n </div>\n </div>\n </CardContent>\n </EmphasisPanel>\n\n <Card className=\"lg:col-span-2\">\n <CardHeader>\n <CardTitle>Asset Locations</CardTitle>\n <div className=\"relative mt-2\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <Input placeholder=\"Search assets...\" className=\"pl-9\" />\n </div>\n </CardHeader>\n <CardContent>\n {assets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No assets to track\"\n description=\"Add an asset and give it a location to see it grouped here.\"\n />\n ) : (\n <div className=\"space-y-4\">\n {Object.entries(locationGroups).map(([location, groupAssets]) => (\n <div key={location}>\n <h4 className=\"text-sm font-medium flex items-center gap-2 mb-2\">\n <Building className=\"h-3.5 w-3.5\" />\n {location}\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {groupAssets.length}\n </Badge>\n </h4>\n <div className=\"space-y-2 ml-5\">\n {groupAssets.map((asset) => (\n <div\n key={asset.id}\n className=\"flex items-start gap-3 rounded border p-2.5\"\n >\n <MapPin className=\"h-3.5 w-3.5 mt-0.5 text-text-secondary shrink-0\" />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium truncate\">{asset.name}</p>\n <p className=\"text-xs text-text-secondary\">{asset.assetTag}</p>\n <div className=\"flex items-center gap-2 mt-1\">\n <Badge\n variant={statusColors[asset.status] || 'outline'}\n className=\"text-[10px]\"\n >\n {asset.status.replace('_', ' ')}\n </Badge>\n {asset.updatedAt ? (\n <span className=\"text-[10px] text-text-secondary\">\n Updated: {new Date(asset.updatedAt).toLocaleDateString()}\n </span>\n ) : null}\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AssetsAssetTrackingPage.js","names":[],"sources":["../../../src/assethandler/pages/AssetsAssetTrackingPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Input,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { MapPin, Search, RefreshCw, Radio, Building, HelpCircle } from 'lucide-react';\nimport { useAssetHandlerStore, type Asset } from '../store/assethandlerStore';\n\nconst statusColors: Record<string, 'default' | 'secondary' | 'destructive' | 'outline'> = {\n active: 'default',\n inactive: 'outline',\n maintenance: 'secondary',\n retired: 'outline',\n disposed: 'destructive',\n};\n\nconst UNLOCATED = 'Unlocated';\n\nexport function AssetsAssetTrackingPage() {\n const { assets } = useAssetHandlerStore();\n\n const locationGroups = useMemo(\n () =>\n assets.reduce<Record<string, Asset[]>>((acc, asset) => {\n const key = asset.location.trim() || UNLOCATED;\n if (!acc[key]) acc[key] = [];\n acc[key].push(asset);\n return acc;\n }, {}),\n [assets]\n );\n\n const locatedCount = assets.filter((a) => a.location.trim()).length;\n const unlocatedCount = assets.length - locatedCount;\n const locationCount = Object.keys(locationGroups).filter((k) => k !== UNLOCATED).length;\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 Tracking</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Location overview for {assets.length} assets\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <RefreshCw className=\"mr-2 h-4 w-4\" />\n Refresh\n </Button>\n <Button size=\"sm\">\n <Radio className=\"mr-2 h-4 w-4\" />\n Track New Asset\n </Button>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Radio className=\"h-4 w-4\" />\n Located Assets\n </div>\n <p className=\"text-2xl font-bold mt-1\">{locatedCount}</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 <Building className=\"h-4 w-4\" />\n Locations\n </div>\n <p className=\"text-2xl font-bold mt-1\">{locationCount}</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 <HelpCircle className=\"h-4 w-4\" />\n Unlocated\n </div>\n <p className=\"text-2xl font-bold mt-1\">{unlocatedCount}</p>\n </CardContent>\n </Card>\n </div>\n\n <PagePurpose>\n Know where your assets physically are. See them grouped by location, so you can locate\n equipment fast, spot assets that still have no recorded location, and confirm items are\n where they should be.\n </PagePurpose>\n\n <div className=\"grid gap-6 lg:grid-cols-5\">\n <EmphasisPanel className=\"lg:col-span-3\">\n <CardHeader className=\"px-0 pt-0\">\n <CardTitle>Location Map</CardTitle>\n <CardDescription>Geographic distribution of tracked assets</CardDescription>\n </CardHeader>\n <CardContent className=\"px-0 pb-0\">\n <div className=\"bg-bg-sunken rounded-lg h-80 flex items-center justify-center border-2 border-dashed\">\n <div className=\"text-center\">\n <MapPin className=\"h-12 w-12 text-text-secondary mx-auto mb-2\" />\n <p className=\"text-text-secondary\">Interactive map view</p>\n <p className=\"text-xs text-text-secondary mt-1\">\n Map integration would render here\n </p>\n </div>\n </div>\n </CardContent>\n </EmphasisPanel>\n\n <Card className=\"lg:col-span-2\">\n <CardHeader>\n <CardTitle>Asset Locations</CardTitle>\n <div className=\"relative mt-2\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <Input placeholder=\"Search assets...\" className=\"pl-9\" />\n </div>\n </CardHeader>\n <CardContent>\n {assets.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No assets to track\"\n description=\"Add an asset and give it a location to see it grouped here.\"\n />\n ) : (\n <div className=\"space-y-4\">\n {Object.entries(locationGroups).map(([location, groupAssets]) => (\n <div key={location}>\n <h4 className=\"text-sm font-medium flex items-center gap-2 mb-2\">\n <Building className=\"h-3.5 w-3.5\" />\n {location}\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {groupAssets.length}\n </Badge>\n </h4>\n <div className=\"space-y-2 ml-5\">\n {groupAssets.map((asset) => (\n <div key={asset.id} className=\"flex items-start gap-3 rounded border p-2.5\">\n <MapPin className=\"h-3.5 w-3.5 mt-0.5 text-text-secondary shrink-0\" />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium truncate\">{asset.name}</p>\n <p className=\"text-xs text-text-secondary\">{asset.assetTag}</p>\n <div className=\"flex items-center gap-2 mt-1\">\n <Badge\n variant={statusColors[asset.status] || 'outline'}\n className=\"text-[10px]\"\n >\n {asset.status.replace('_', ' ')}\n </Badge>\n {asset.updatedAt ? (\n <span className=\"text-[10px] text-text-secondary\">\n Updated: {new Date(asset.updatedAt).toLocaleDateString()}\n </span>\n ) : null}\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DigitalTwinPage.js","names":[],"sources":["../../../src/assethandler/pages/DigitalTwinPage.tsx"],"sourcesContent":["import { Box } from 'lucide-react';\nimport { GradientText, IllustratedEmptyState, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\n\nconst DIGITAL_TWINS_QUERY = gql`\n query AssetHandlerDigitalTwins {\n digitalTwins {\n id\n assetId\n modelRef\n fidelityLevel\n geometryRef\n updatedAt\n }\n }\n`;\n\nexport function DigitalTwinPage() {\n const { data, loading } = useQuery<{\n digitalTwins: Array<{\n id: string;\n assetId: string;\n modelRef: string;\n fidelityLevel: string;\n geometryRef: string | null;\n updatedAt: string;\n }>;\n }>(DIGITAL_TWINS_QUERY, { fetchPolicy: 'cache-and-network' });\n\n const twins = data?.digitalTwins ?? [];\n\n return (\n <div className=\"space-y-4 p-4\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <Box className=\"h-6 w-6\" /> <GradientText>Digital Twin</GradientText>\n </h1>\n <PagePurpose>\n A live virtual replica of your most critical assets. Use digital twins to simulate\n what-if scenarios, validate maintenance changes, and visualise condition without touching\n the physical equipment — so you can de-risk decisions on high-value or hard-to-access\n assets.\n </PagePurpose>\n <div className=\"grid gap-3 md:grid-cols-2\">\n {loading && twins.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && twins.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No digital twins found\"\n description=\"Digital twins of your assets will appear here.\"\n />\n ) : null}\n {twins.map((twin, i) => (\n <GlassCard\n key={twin.id}\n treatment={i === 0 ? 'glass' : 'plain'}\n glow={i === 0}\n className=\"p-4 border-border-seam\"\n >\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">\n Asset {twin.assetId}\n </p>\n <h2 className=\"mt-1 text-base font-semibold\">{twin.modelRef}</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">Fidelity: {twin.fidelityLevel}</p>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Geometry: {twin.geometryRef ?? 'Not linked'}\n </p>\n </GlassCard>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;AAKA,IAAM,IAAsB,CAAG;;;;;;;;;;;;AAa/B,SAAgB,IAAkB;CAChC,IAAM,EAAE,SAAM,eAAY,EASvB,GAAqB,EAAE,aAAa,qBAAqB,CAAC,EAEvD,IAAQ,GAAM,gBAAgB,EAAE;AAEtC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,MAAD;IAAI,WAAU;cAAd;KACE,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;;KAAC,kBAAC,GAAD,EAAA,UAAc,gBAA2B,CAAA;KAClE;;GACL,kBAAC,GAAD,EAAA,UAAa,8QAKC,CAAA;GACd,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,KAAW,EAAM,WAAW,IAC3B,kBAAC,OAAD;MAAK,WAAU;gBACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAAa,WAAU,8DAA+D,EAA5E,EAA4E,CACtF;MACE,CAAA,GACJ;KACH,CAAC,KAAW,EAAM,WAAW,IAC5B,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GACA;KACH,EAAM,KAAK,GAAM,MAChB,kBAAC,GAAD;MAEE,WAAW,MAAM,IAAI,UAAU;MAC/B,MAAM,MAAM;MACZ,WAAU;gBAJZ;OAME,kBAAC,KAAD;QAAG,WAAU;kBAAb,CAAmE,UAC1D,EAAK,QACV;;OACJ,kBAAC,MAAD;QAAI,WAAU;kBAAgC,EAAK;QAAc,CAAA;OACjE,kBAAC,KAAD;QAAG,WAAU;kBAAb,CAAgD,cAAW,EAAK,cAAkB;;OAClF,kBAAC,KAAD;QAAG,WAAU;kBAAb,CAAgD,cACnC,EAAK,eAAe,aAC7B;;OACM;QAbL,EAAK,GAaA,CACZ;KACE;;GACF"}
1
+ {"version":3,"file":"DigitalTwinPage.js","names":[],"sources":["../../../src/assethandler/pages/DigitalTwinPage.tsx"],"sourcesContent":["import { Box } from 'lucide-react';\nimport { GradientText, IllustratedEmptyState, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\n\nconst DIGITAL_TWINS_QUERY = gql`\n query AssetHandlerDigitalTwins {\n digitalTwins {\n id\n assetId\n modelRef\n fidelityLevel\n geometryRef\n updatedAt\n }\n }\n`;\n\nexport function DigitalTwinPage() {\n const { data, loading } = useQuery<{\n digitalTwins: Array<{\n id: string;\n assetId: string;\n modelRef: string;\n fidelityLevel: string;\n geometryRef: string | null;\n updatedAt: string;\n }>;\n }>(DIGITAL_TWINS_QUERY, { fetchPolicy: 'cache-and-network' });\n\n const twins = data?.digitalTwins ?? [];\n\n return (\n <div className=\"space-y-4 p-4\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <Box className=\"h-6 w-6\" /> <GradientText>Digital Twin</GradientText>\n </h1>\n <PagePurpose>\n A live virtual replica of your most critical assets. Use digital twins to simulate what-if\n scenarios, validate maintenance changes, and visualise condition without touching the\n physical equipment — so you can de-risk decisions on high-value or hard-to-access assets.\n </PagePurpose>\n <div className=\"grid gap-3 md:grid-cols-2\">\n {loading && twins.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && twins.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No digital twins found\"\n description=\"Digital twins of your assets will appear here.\"\n />\n ) : null}\n {twins.map((twin, i) => (\n <GlassCard\n key={twin.id}\n treatment={i === 0 ? 'glass' : 'plain'}\n glow={i === 0}\n className=\"p-4 border-border-seam\"\n >\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">\n Asset {twin.assetId}\n </p>\n <h2 className=\"mt-1 text-base font-semibold\">{twin.modelRef}</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">Fidelity: {twin.fidelityLevel}</p>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Geometry: {twin.geometryRef ?? 'Not linked'}\n </p>\n </GlassCard>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;AAKA,IAAM,IAAsB,CAAG;;;;;;;;;;;;AAa/B,SAAgB,IAAkB;CAChC,IAAM,EAAE,SAAM,eAAY,EASvB,GAAqB,EAAE,aAAa,qBAAqB,CAAC,EAEvD,IAAQ,GAAM,gBAAgB,EAAE;AAEtC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,MAAD;IAAI,WAAU;cAAd;KACE,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;;KAAC,kBAAC,GAAD,EAAA,UAAc,gBAA2B,CAAA;KAClE;;GACL,kBAAC,GAAD,EAAA,UAAa,8QAIC,CAAA;GACd,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,KAAW,EAAM,WAAW,IAC3B,kBAAC,OAAD;MAAK,WAAU;gBACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAAa,WAAU,8DAA+D,EAA5E,EAA4E,CACtF;MACE,CAAA,GACJ;KACH,CAAC,KAAW,EAAM,WAAW,IAC5B,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GACA;KACH,EAAM,KAAK,GAAM,MAChB,kBAAC,GAAD;MAEE,WAAW,MAAM,IAAI,UAAU;MAC/B,MAAM,MAAM;MACZ,WAAU;gBAJZ;OAME,kBAAC,KAAD;QAAG,WAAU;kBAAb,CAAmE,UAC1D,EAAK,QACV;;OACJ,kBAAC,MAAD;QAAI,WAAU;kBAAgC,EAAK;QAAc,CAAA;OACjE,kBAAC,KAAD;QAAG,WAAU;kBAAb,CAAgD,cAAW,EAAK,cAAkB;;OAClF,kBAAC,KAAD;QAAG,WAAU;kBAAb,CAAgD,cACnC,EAAK,eAAe,aAC7B;;OACM;QAbL,EAAK,GAaA,CACZ;KACE;;GACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"DiscoveryJobsPage.js","names":[],"sources":["../../../src/assethandler/pages/DiscoveryJobsPage.tsx"],"sourcesContent":["import { Radar } from 'lucide-react';\nimport { GradientText, IllustratedEmptyState, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\n\nconst DISCOVERY_JOBS_QUERY = gql`\n query AssetHandlerDiscoveryJobs {\n discoveryJobs {\n id\n name\n source\n status\n assetsDiscovered\n startedAt\n completedAt\n }\n }\n`;\n\nconst START_DISCOVERY_JOB_MUTATION = gql`\n mutation AssetHandlerStartDiscoveryJob($input: StartDiscoveryJobInput!) {\n startDiscoveryJob(input: $input) {\n id\n }\n }\n`;\n\nexport function DiscoveryJobsPage() {\n const [name, setName] = useState('Nightly Datacenter Scan');\n const [source, setSource] = useState('aws-ap-south-1');\n\n const { data, loading, refetch } = useQuery<{\n discoveryJobs: Array<{\n id: string;\n name: string;\n source: string;\n status: string;\n assetsDiscovered: number;\n startedAt: string | null;\n completedAt: string | null;\n }>;\n }>(DISCOVERY_JOBS_QUERY, { fetchPolicy: 'cache-and-network' });\n\n const [startDiscoveryJob, { loading: starting }] = useMutation(START_DISCOVERY_JOB_MUTATION, {\n onCompleted: () => {\n void refetch();\n },\n });\n\n const jobs = data?.discoveryJobs ?? [];\n\n return (\n <div className=\"space-y-4 p-4\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <Radar className=\"h-6 w-6\" /> <GradientText>Discovery Jobs</GradientText>\n </h1>\n <PagePurpose>\n Automatically find assets across your cloud and on-prem environments instead of entering\n them by hand. Point a discovery job at a source (e.g. an AWS region) and it scans for new or\n changed assets and feeds them into your inventory — keeping the estate complete and\n up to date.\n </PagePurpose>\n <EmphasisPanel>\n <h2 className=\"text-sm font-semibold mb-3\">Start a discovery scan</h2>\n <form\n className=\"grid gap-3 md:grid-cols-3\"\n onSubmit={(event) => {\n event.preventDefault();\n void startDiscoveryJob({ variables: { input: { name, source } } });\n }}\n >\n <input\n className=\"rounded-md border px-3 py-2\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n placeholder=\"Job name\"\n />\n <input\n className=\"rounded-md border px-3 py-2\"\n value={source}\n onChange={(event) => setSource(event.target.value)}\n placeholder=\"Discovery source\"\n />\n <button\n className=\"rounded-md bg-action-primary-bg px-3 py-2 text-action-primary-text\"\n disabled={starting}\n type=\"submit\"\n >\n {starting ? 'Starting...' : 'Start Job'}\n </button>\n </form>\n </EmphasisPanel>\n <div className=\"rounded-lg border\">\n <div className=\"grid grid-cols-5 gap-3 border-b px-4 py-3 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n <span>Name</span>\n <span>Source</span>\n <span>Status</span>\n <span>Discovered</span>\n <span>Started</span>\n </div>\n {loading && jobs.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && jobs.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No discovery jobs found\"\n description=\"Run a discovery job to populate this list.\"\n />\n ) : null}\n {jobs.map((job) => (\n <div\n key={job.id}\n className=\"grid grid-cols-5 gap-3 border-b px-4 py-3 text-sm last:border-b-0\"\n >\n <span className=\"font-medium\">{job.name}</span>\n <span>{job.source}</span>\n <span>{job.status}</span>\n <span>{job.assetsDiscovered}</span>\n <span className=\"text-text-secondary\">\n {job.startedAt ? new Date(job.startedAt).toLocaleString() : 'n/a'}\n </span>\n </div>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAMA,IAAM,IAAuB,CAAG;;;;;;;;;;;;GAc1B,IAA+B,CAAG;;;;;;;AAQxC,SAAgB,IAAoB;CAClC,IAAM,CAAC,GAAM,KAAW,EAAS,0BAA0B,EACrD,CAAC,GAAQ,KAAa,EAAS,iBAAiB,EAEhD,EAAE,SAAM,YAAS,eAAY,EAUhC,GAAsB,EAAE,aAAa,qBAAqB,CAAC,EAExD,CAAC,GAAmB,EAAE,SAAS,OAAc,EAAY,GAA8B,EAC3F,mBAAmB;AACZ,KAAS;IAEjB,CAAC,EAEI,IAAO,GAAM,iBAAiB,EAAE;AAEtC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,MAAD;IAAI,WAAU;cAAd;KACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;;KAAC,kBAAC,GAAD,EAAA,UAAc,kBAA6B,CAAA;KACtE;;GACL,kBAAC,GAAD,EAAA,UAAa,yRAKC,CAAA;GACd,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAA6B;IAA2B,CAAA,EACtE,kBAAC,QAAD;IACE,WAAU;IACV,WAAW,MAAU;AAEd,KADL,EAAM,gBAAgB,EACjB,EAAkB,EAAE,WAAW,EAAE,OAAO;MAAE;MAAM;MAAQ,EAAE,EAAE,CAAC;;cAJtE;KAOA,kBAAC,SAAD;MACE,WAAU;MACV,OAAO;MACP,WAAW,MAAU,EAAQ,EAAM,OAAO,MAAM;MAChD,aAAY;MACZ,CAAA;KACF,kBAAC,SAAD;MACE,WAAU;MACV,OAAO;MACP,WAAW,MAAU,EAAU,EAAM,OAAO,MAAM;MAClD,aAAY;MACZ,CAAA;KACA,kBAAC,UAAD;MACE,WAAU;MACV,UAAU;MACV,MAAK;gBAEJ,IAAW,gBAAgB;MACrB,CAAA;KACJ;MACO,EAAA,CAAA;GAChB,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,QAAD,EAAA,UAAM,QAAW,CAAA;OACjB,kBAAC,QAAD,EAAA,UAAM,UAAa,CAAA;OACnB,kBAAC,QAAD,EAAA,UAAM,UAAa,CAAA;OACnB,kBAAC,QAAD,EAAA,UAAM,cAAiB,CAAA;OACvB,kBAAC,QAAD,EAAA,UAAM,WAAc,CAAA;OAChB;;KACL,KAAW,EAAK,WAAW,IAC1B,kBAAC,OAAD;MAAK,WAAU;gBACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAAa,WAAU,8DAA+D,EAA5E,EAA4E,CACtF;MACE,CAAA,GACJ;KACH,CAAC,KAAW,EAAK,WAAW,IAC3B,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GACA;KACH,EAAK,KAAK,MACT,kBAAC,OAAD;MAEE,WAAU;gBAFZ;OAIE,kBAAC,QAAD;QAAM,WAAU;kBAAe,EAAI;QAAY,CAAA;OAC/C,kBAAC,QAAD,EAAA,UAAO,EAAI,QAAc,CAAA;OACzB,kBAAC,QAAD,EAAA,UAAO,EAAI,QAAc,CAAA;OACzB,kBAAC,QAAD,EAAA,UAAO,EAAI,kBAAwB,CAAA;OACnC,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAI,YAAY,IAAI,KAAK,EAAI,UAAU,CAAC,gBAAgB,GAAG;QACvD,CAAA;OACH;QAVC,EAAI,GAUL,CACN;KACE;;GACF"}
1
+ {"version":3,"file":"DiscoveryJobsPage.js","names":[],"sources":["../../../src/assethandler/pages/DiscoveryJobsPage.tsx"],"sourcesContent":["import { Radar } from 'lucide-react';\nimport {\n GradientText,\n IllustratedEmptyState,\n EmphasisPanel,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\n\nconst DISCOVERY_JOBS_QUERY = gql`\n query AssetHandlerDiscoveryJobs {\n discoveryJobs {\n id\n name\n source\n status\n assetsDiscovered\n startedAt\n completedAt\n }\n }\n`;\n\nconst START_DISCOVERY_JOB_MUTATION = gql`\n mutation AssetHandlerStartDiscoveryJob($input: StartDiscoveryJobInput!) {\n startDiscoveryJob(input: $input) {\n id\n }\n }\n`;\n\nexport function DiscoveryJobsPage() {\n const [name, setName] = useState('Nightly Datacenter Scan');\n const [source, setSource] = useState('aws-ap-south-1');\n\n const { data, loading, refetch } = useQuery<{\n discoveryJobs: Array<{\n id: string;\n name: string;\n source: string;\n status: string;\n assetsDiscovered: number;\n startedAt: string | null;\n completedAt: string | null;\n }>;\n }>(DISCOVERY_JOBS_QUERY, { fetchPolicy: 'cache-and-network' });\n\n const [startDiscoveryJob, { loading: starting }] = useMutation(START_DISCOVERY_JOB_MUTATION, {\n onCompleted: () => {\n void refetch();\n },\n });\n\n const jobs = data?.discoveryJobs ?? [];\n\n return (\n <div className=\"space-y-4 p-4\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <Radar className=\"h-6 w-6\" /> <GradientText>Discovery Jobs</GradientText>\n </h1>\n <PagePurpose>\n Automatically find assets across your cloud and on-prem environments instead of entering\n them by hand. Point a discovery job at a source (e.g. an AWS region) and it scans for new or\n changed assets and feeds them into your inventory — keeping the estate complete and up to\n date.\n </PagePurpose>\n <EmphasisPanel>\n <h2 className=\"text-sm font-semibold mb-3\">Start a discovery scan</h2>\n <form\n className=\"grid gap-3 md:grid-cols-3\"\n onSubmit={(event) => {\n event.preventDefault();\n void startDiscoveryJob({ variables: { input: { name, source } } });\n }}\n >\n <input\n className=\"rounded-md border px-3 py-2\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n placeholder=\"Job name\"\n />\n <input\n className=\"rounded-md border px-3 py-2\"\n value={source}\n onChange={(event) => setSource(event.target.value)}\n placeholder=\"Discovery source\"\n />\n <button\n className=\"rounded-md bg-action-primary-bg px-3 py-2 text-action-primary-text\"\n disabled={starting}\n type=\"submit\"\n >\n {starting ? 'Starting...' : 'Start Job'}\n </button>\n </form>\n </EmphasisPanel>\n <div className=\"rounded-lg border\">\n <div className=\"grid grid-cols-5 gap-3 border-b px-4 py-3 text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n <span>Name</span>\n <span>Source</span>\n <span>Status</span>\n <span>Discovered</span>\n <span>Started</span>\n </div>\n {loading && jobs.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && jobs.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No discovery jobs found\"\n description=\"Run a discovery job to populate this list.\"\n />\n ) : null}\n {jobs.map((job) => (\n <div\n key={job.id}\n className=\"grid grid-cols-5 gap-3 border-b px-4 py-3 text-sm last:border-b-0\"\n >\n <span className=\"font-medium\">{job.name}</span>\n <span>{job.source}</span>\n <span>{job.status}</span>\n <span>{job.assetsDiscovered}</span>\n <span className=\"text-text-secondary\">\n {job.startedAt ? new Date(job.startedAt).toLocaleString() : 'n/a'}\n </span>\n </div>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAWA,IAAM,IAAuB,CAAG;;;;;;;;;;;;GAc1B,IAA+B,CAAG;;;;;;;AAQxC,SAAgB,IAAoB;CAClC,IAAM,CAAC,GAAM,KAAW,EAAS,0BAA0B,EACrD,CAAC,GAAQ,KAAa,EAAS,iBAAiB,EAEhD,EAAE,SAAM,YAAS,eAAY,EAUhC,GAAsB,EAAE,aAAa,qBAAqB,CAAC,EAExD,CAAC,GAAmB,EAAE,SAAS,OAAc,EAAY,GAA8B,EAC3F,mBAAmB;AACZ,KAAS;IAEjB,CAAC,EAEI,IAAO,GAAM,iBAAiB,EAAE;AAEtC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,MAAD;IAAI,WAAU;cAAd;KACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;;KAAC,kBAAC,GAAD,EAAA,UAAc,kBAA6B,CAAA;KACtE;;GACL,kBAAC,GAAD,EAAA,UAAa,yRAKC,CAAA;GACd,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAA6B;IAA2B,CAAA,EACtE,kBAAC,QAAD;IACE,WAAU;IACV,WAAW,MAAU;AAEd,KADL,EAAM,gBAAgB,EACjB,EAAkB,EAAE,WAAW,EAAE,OAAO;MAAE;MAAM;MAAQ,EAAE,EAAE,CAAC;;cAJtE;KAOE,kBAAC,SAAD;MACE,WAAU;MACV,OAAO;MACP,WAAW,MAAU,EAAQ,EAAM,OAAO,MAAM;MAChD,aAAY;MACZ,CAAA;KACF,kBAAC,SAAD;MACE,WAAU;MACV,OAAO;MACP,WAAW,MAAU,EAAU,EAAM,OAAO,MAAM;MAClD,aAAY;MACZ,CAAA;KACF,kBAAC,UAAD;MACE,WAAU;MACV,UAAU;MACV,MAAK;gBAEJ,IAAW,gBAAgB;MACrB,CAAA;KACJ;MACO,EAAA,CAAA;GAChB,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,QAAD,EAAA,UAAM,QAAW,CAAA;OACjB,kBAAC,QAAD,EAAA,UAAM,UAAa,CAAA;OACnB,kBAAC,QAAD,EAAA,UAAM,UAAa,CAAA;OACnB,kBAAC,QAAD,EAAA,UAAM,cAAiB,CAAA;OACvB,kBAAC,QAAD,EAAA,UAAM,WAAc,CAAA;OAChB;;KACL,KAAW,EAAK,WAAW,IAC1B,kBAAC,OAAD;MAAK,WAAU;gBACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAAa,WAAU,8DAA+D,EAA5E,EAA4E,CACtF;MACE,CAAA,GACJ;KACH,CAAC,KAAW,EAAK,WAAW,IAC3B,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GACA;KACH,EAAK,KAAK,MACT,kBAAC,OAAD;MAEE,WAAU;gBAFZ;OAIE,kBAAC,QAAD;QAAM,WAAU;kBAAe,EAAI;QAAY,CAAA;OAC/C,kBAAC,QAAD,EAAA,UAAO,EAAI,QAAc,CAAA;OACzB,kBAAC,QAAD,EAAA,UAAO,EAAI,QAAc,CAAA;OACzB,kBAAC,QAAD,EAAA,UAAO,EAAI,kBAAwB,CAAA;OACnC,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAI,YAAY,IAAI,KAAK,EAAI,UAAU,CAAC,gBAAgB,GAAG;QACvD,CAAA;OACH;QAVC,EAAI,GAUL,CACN;KACE;;GACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"EsgDashboardPage.js","names":[],"sources":["../../../src/assethandler/pages/EsgDashboardPage.tsx"],"sourcesContent":["import { Leaf } from 'lucide-react';\nimport { GradientText, IllustratedEmptyState, EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\n\nconst ESG_METRICS_QUERY = gql`\n query AssetHandlerEsgMetrics {\n esgMetrics {\n id\n metricKey\n metricName\n category\n unit\n value\n target\n trend\n periodStart\n periodEnd\n }\n }\n`;\n\nconst UPSERT_ESG_METRIC_MUTATION = gql`\n mutation AssetHandlerUpsertEsgMetric($input: UpsertEsgMetricInput!) {\n upsertEsgMetric(input: $input) {\n id\n }\n }\n`;\n\nexport function EsgDashboardPage() {\n const [value, setValue] = useState('42.5');\n\n const { data, loading, refetch } = useQuery<{\n esgMetrics: Array<{\n id: string;\n metricKey: string;\n metricName: string;\n category: string;\n unit: string;\n value: number;\n target: number | null;\n trend: string | null;\n periodStart: string;\n periodEnd: string;\n }>;\n }>(ESG_METRICS_QUERY, { fetchPolicy: 'cache-and-network' });\n\n const [upsertEsgMetric, { loading: saving }] = useMutation(UPSERT_ESG_METRIC_MUTATION, {\n onCompleted: () => {\n void refetch();\n },\n });\n\n const metrics = data?.esgMetrics ?? [];\n\n return (\n <div className=\"space-y-4 p-4\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <Leaf className=\"h-6 w-6\" /> <GradientText>ESG Dashboard</GradientText>\n </h1>\n <PagePurpose>\n Measure the environmental and governance impact of your asset estate against targets —\n energy use, carbon emissions, responsible disposal. Record metrics here to track progress\n toward sustainability goals and produce the evidence ESG reporting and audits require.\n </PagePurpose>\n <EmphasisPanel>\n <h2 className=\"text-sm font-semibold mb-3\">Record an ESG metric</h2>\n <form\n className=\"grid gap-3 md:grid-cols-4\"\n onSubmit={(event) => {\n event.preventDefault();\n const periodStart = new Date(\n new Date().getFullYear(),\n new Date().getMonth(),\n 1\n ).toISOString();\n const periodEnd = new Date().toISOString();\n void upsertEsgMetric({\n variables: {\n input: {\n metricKey: 'carbon-footprint-scope2',\n metricName: 'Scope 2 Carbon Emissions',\n category: 'environment',\n unit: 'tCO2e',\n value: Number(value),\n target: 40,\n trend: 'improving',\n periodStart,\n periodEnd,\n },\n },\n });\n }}\n >\n <input\n className=\"rounded-md border px-3 py-2\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n aria-label=\"Metric value\"\n />\n <div className=\"text-sm text-text-secondary md:col-span-2\">\n Metric: Scope 2 Carbon Emissions (monthly)\n </div>\n <button\n className=\"rounded-md bg-action-primary-bg px-3 py-2 text-action-primary-text\"\n disabled={saving}\n type=\"submit\"\n >\n {saving ? 'Saving...' : 'Save Metric'}\n </button>\n </form>\n </EmphasisPanel>\n <div className=\"grid gap-3 md:grid-cols-2\">\n {loading && metrics.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && metrics.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No ESG metrics recorded yet\"\n description=\"Sustainability and ESG metrics will appear here once recorded.\"\n />\n ) : null}\n {metrics.map((metric) => (\n <article key={metric.id} className=\"rounded-lg border p-4\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">{metric.category}</p>\n <h2 className=\"mt-1 text-base font-semibold\">{metric.metricName}</h2>\n <p className=\"mt-2 text-sm\">\n <span className=\"font-semibold\">{metric.value}</span> {metric.unit}\n {metric.target !== null ? (\n <span className=\"text-text-secondary\"> / target {metric.target}</span>\n ) : null}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">Trend: {metric.trend ?? 'stable'}</p>\n </article>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAMA,IAAM,IAAoB,CAAG;;;;;;;;;;;;;;;GAiBvB,IAA6B,CAAG;;;;;;;AAQtC,SAAgB,IAAmB;CACjC,IAAM,CAAC,GAAO,KAAY,EAAS,OAAO,EAEpC,EAAE,SAAM,YAAS,eAAY,EAahC,GAAmB,EAAE,aAAa,qBAAqB,CAAC,EAErD,CAAC,GAAiB,EAAE,SAAS,OAAY,EAAY,GAA4B,EACrF,mBAAmB;AACZ,KAAS;IAEjB,CAAC,EAEI,IAAU,GAAM,cAAc,EAAE;AAEtC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,MAAD;IAAI,WAAU;cAAd;KACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;;KAAC,kBAAC,GAAD,EAAA,UAAc,iBAA4B,CAAA;KACpE;;GACL,kBAAC,GAAD,EAAA,UAAa,2QAIC,CAAA;GACd,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAA6B;IAAyB,CAAA,EACpE,kBAAC,QAAD;IACE,WAAU;IACV,WAAW,MAAU;AACrB,OAAM,gBAAgB;KACtB,IAAM,IAAc,IAAI,sBACtB,IAAI,MAAM,EAAC,aAAa,mBACxB,IAAI,MAAM,EAAC,UAAU,EACrB,EACD,CAAC,aAAa,EACT,qBAAY,IAAI,MAAM,EAAC,aAAa;AACrC,OAAgB,EACnB,WAAW,EACT,OAAO;MACL,WAAW;MACX,YAAY;MACZ,UAAU;MACV,MAAM;MACN,OAAO,OAAO,EAAM;MACpB,QAAQ;MACR,OAAO;MACP;MACA;MACD,EACF,EACF,CAAC;;cAxBJ;KA2BE,kBAAC,SAAD;MACE,WAAU;MACH;MACP,WAAW,MAAU,EAAS,EAAM,OAAO,MAAM;MACjD,cAAW;MACX,CAAA;KACF,kBAAC,OAAD;MAAK,WAAU;gBAA4C;MAErD,CAAA;KACN,kBAAC,UAAD;MACE,WAAU;MACV,UAAU;MACV,MAAK;gBAEJ,IAAS,cAAc;MACjB,CAAA;KACJ;MACO,EAAA,CAAA;GAChB,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,KAAW,EAAQ,WAAW,IAC7B,kBAAC,OAAD;MAAK,WAAU;gBACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAAa,WAAU,8DAA+D,EAA5E,EAA4E,CACtF;MACE,CAAA,GACJ;KACH,CAAC,KAAW,EAAQ,WAAW,IAC9B,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GACA;KACH,EAAQ,KAAK,MACZ,kBAAC,WAAD;MAAyB,WAAU;gBAAnC;OACE,kBAAC,KAAD;QAAG,WAAU;kBAAuD,EAAO;QAAa,CAAA;OACxF,kBAAC,MAAD;QAAI,WAAU;kBAAgC,EAAO;QAAgB,CAAA;OACrE,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACE,kBAAC,QAAD;UAAM,WAAU;oBAAiB,EAAO;UAAa,CAAA;;SAAE,EAAO;SAC7D,EAAO,WAAW,OAEf,OADF,kBAAC,QAAD;UAAM,WAAU;oBAAhB,CAAsC,cAAW,EAAO,OAAc;;SAEtE;;OACJ,kBAAC,KAAD;QAAG,WAAU;kBAAb,CAAgD,WAAQ,EAAO,SAAS,SAAa;;OAC7E;QAVI,EAAO,GAUX,CACV;KACE;;GACF"}
1
+ {"version":3,"file":"EsgDashboardPage.js","names":[],"sources":["../../../src/assethandler/pages/EsgDashboardPage.tsx"],"sourcesContent":["import { Leaf } from 'lucide-react';\nimport {\n GradientText,\n IllustratedEmptyState,\n EmphasisPanel,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\n\nconst ESG_METRICS_QUERY = gql`\n query AssetHandlerEsgMetrics {\n esgMetrics {\n id\n metricKey\n metricName\n category\n unit\n value\n target\n trend\n periodStart\n periodEnd\n }\n }\n`;\n\nconst UPSERT_ESG_METRIC_MUTATION = gql`\n mutation AssetHandlerUpsertEsgMetric($input: UpsertEsgMetricInput!) {\n upsertEsgMetric(input: $input) {\n id\n }\n }\n`;\n\nexport function EsgDashboardPage() {\n const [value, setValue] = useState('42.5');\n\n const { data, loading, refetch } = useQuery<{\n esgMetrics: Array<{\n id: string;\n metricKey: string;\n metricName: string;\n category: string;\n unit: string;\n value: number;\n target: number | null;\n trend: string | null;\n periodStart: string;\n periodEnd: string;\n }>;\n }>(ESG_METRICS_QUERY, { fetchPolicy: 'cache-and-network' });\n\n const [upsertEsgMetric, { loading: saving }] = useMutation(UPSERT_ESG_METRIC_MUTATION, {\n onCompleted: () => {\n void refetch();\n },\n });\n\n const metrics = data?.esgMetrics ?? [];\n\n return (\n <div className=\"space-y-4 p-4\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <Leaf className=\"h-6 w-6\" /> <GradientText>ESG Dashboard</GradientText>\n </h1>\n <PagePurpose>\n Measure the environmental and governance impact of your asset estate against targets —\n energy use, carbon emissions, responsible disposal. Record metrics here to track progress\n toward sustainability goals and produce the evidence ESG reporting and audits require.\n </PagePurpose>\n <EmphasisPanel>\n <h2 className=\"text-sm font-semibold mb-3\">Record an ESG metric</h2>\n <form\n className=\"grid gap-3 md:grid-cols-4\"\n onSubmit={(event) => {\n event.preventDefault();\n const periodStart = new Date(\n new Date().getFullYear(),\n new Date().getMonth(),\n 1\n ).toISOString();\n const periodEnd = new Date().toISOString();\n void upsertEsgMetric({\n variables: {\n input: {\n metricKey: 'carbon-footprint-scope2',\n metricName: 'Scope 2 Carbon Emissions',\n category: 'environment',\n unit: 'tCO2e',\n value: Number(value),\n target: 40,\n trend: 'improving',\n periodStart,\n periodEnd,\n },\n },\n });\n }}\n >\n <input\n className=\"rounded-md border px-3 py-2\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n aria-label=\"Metric value\"\n />\n <div className=\"text-sm text-text-secondary md:col-span-2\">\n Metric: Scope 2 Carbon Emissions (monthly)\n </div>\n <button\n className=\"rounded-md bg-action-primary-bg px-3 py-2 text-action-primary-text\"\n disabled={saving}\n type=\"submit\"\n >\n {saving ? 'Saving...' : 'Save Metric'}\n </button>\n </form>\n </EmphasisPanel>\n <div className=\"grid gap-3 md:grid-cols-2\">\n {loading && metrics.length === 0 ? (\n <div className=\"space-y-2 px-4 py-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-8 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n ) : null}\n {!loading && metrics.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No ESG metrics recorded yet\"\n description=\"Sustainability and ESG metrics will appear here once recorded.\"\n />\n ) : null}\n {metrics.map((metric) => (\n <article key={metric.id} className=\"rounded-lg border p-4\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">{metric.category}</p>\n <h2 className=\"mt-1 text-base font-semibold\">{metric.metricName}</h2>\n <p className=\"mt-2 text-sm\">\n <span className=\"font-semibold\">{metric.value}</span> {metric.unit}\n {metric.target !== null ? (\n <span className=\"text-text-secondary\"> / target {metric.target}</span>\n ) : null}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">Trend: {metric.trend ?? 'stable'}</p>\n </article>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAWA,IAAM,IAAoB,CAAG;;;;;;;;;;;;;;;GAiBvB,IAA6B,CAAG;;;;;;;AAQtC,SAAgB,IAAmB;CACjC,IAAM,CAAC,GAAO,KAAY,EAAS,OAAO,EAEpC,EAAE,SAAM,YAAS,eAAY,EAahC,GAAmB,EAAE,aAAa,qBAAqB,CAAC,EAErD,CAAC,GAAiB,EAAE,SAAS,OAAY,EAAY,GAA4B,EACrF,mBAAmB;AACZ,KAAS;IAEjB,CAAC,EAEI,IAAU,GAAM,cAAc,EAAE;AAEtC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,MAAD;IAAI,WAAU;cAAd;KACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;;KAAC,kBAAC,GAAD,EAAA,UAAc,iBAA4B,CAAA;KACpE;;GACL,kBAAC,GAAD,EAAA,UAAa,2QAIC,CAAA;GACd,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAA6B;IAAyB,CAAA,EACpE,kBAAC,QAAD;IACE,WAAU;IACV,WAAW,MAAU;AACnB,OAAM,gBAAgB;KACtB,IAAM,IAAc,IAAI,sBACtB,IAAI,MAAM,EAAC,aAAa,mBACxB,IAAI,MAAM,EAAC,UAAU,EACrB,EACD,CAAC,aAAa,EACT,qBAAY,IAAI,MAAM,EAAC,aAAa;AACrC,OAAgB,EACnB,WAAW,EACT,OAAO;MACL,WAAW;MACX,YAAY;MACZ,UAAU;MACV,MAAM;MACN,OAAO,OAAO,EAAM;MACpB,QAAQ;MACR,OAAO;MACP;MACA;MACD,EACF,EACF,CAAC;;cAxBN;KA2BE,kBAAC,SAAD;MACE,WAAU;MACH;MACP,WAAW,MAAU,EAAS,EAAM,OAAO,MAAM;MACjD,cAAW;MACX,CAAA;KACF,kBAAC,OAAD;MAAK,WAAU;gBAA4C;MAErD,CAAA;KACN,kBAAC,UAAD;MACE,WAAU;MACV,UAAU;MACV,MAAK;gBAEJ,IAAS,cAAc;MACjB,CAAA;KACJ;MACO,EAAA,CAAA;GAChB,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,KAAW,EAAQ,WAAW,IAC7B,kBAAC,OAAD;MAAK,WAAU;gBACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAAa,WAAU,8DAA+D,EAA5E,EAA4E,CACtF;MACE,CAAA,GACJ;KACH,CAAC,KAAW,EAAQ,WAAW,IAC9B,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GACA;KACH,EAAQ,KAAK,MACZ,kBAAC,WAAD;MAAyB,WAAU;gBAAnC;OACE,kBAAC,KAAD;QAAG,WAAU;kBAAuD,EAAO;QAAa,CAAA;OACxF,kBAAC,MAAD;QAAI,WAAU;kBAAgC,EAAO;QAAgB,CAAA;OACrE,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACE,kBAAC,QAAD;UAAM,WAAU;oBAAiB,EAAO;UAAa,CAAA;;SAAE,EAAO;SAC7D,EAAO,WAAW,OAEf,OADF,kBAAC,QAAD;UAAM,WAAU;oBAAhB,CAAsC,cAAW,EAAO,OAAc;;SAEtE;;OACJ,kBAAC,KAAD;QAAG,WAAU;kBAAb,CAAgD,WAAQ,EAAO,SAAS,SAAa;;OAC7E;QAVI,EAAO,GAUX,CACV;KACE;;GACF"}