@burdenoff/microfe-theglobalfuel 2026.716.1 → 2026.720.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.
- package/dist/theglobalfuel/TheGlobalFuelRoutes.d.ts.map +1 -1
- package/dist/theglobalfuel/TheGlobalFuelRoutes.js +34 -22
- package/dist/theglobalfuel/TheGlobalFuelRoutes.js.map +1 -1
- package/dist/theglobalfuel/components/DataTable.js +1 -1
- package/dist/theglobalfuel/components/DataTable.js.map +1 -1
- package/dist/theglobalfuel/components/MiniBar.d.ts.map +1 -1
- package/dist/theglobalfuel/components/MiniBar.js +1 -1
- package/dist/theglobalfuel/components/MiniBar.js.map +1 -1
- package/dist/theglobalfuel/components/Tabs.js +1 -1
- package/dist/theglobalfuel/components/Tabs.js.map +1 -1
- package/dist/theglobalfuel/components/index.js +2 -0
- package/dist/theglobalfuel/components/viz/BarChart.js +83 -4
- package/dist/theglobalfuel/components/viz/BarChart.js.map +1 -0
- package/dist/theglobalfuel/components/viz/StatCard.js +60 -3
- package/dist/theglobalfuel/components/viz/StatCard.js.map +1 -0
- package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.d.ts +10 -1
- package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.d.ts.map +1 -1
- package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.js +1 -1
- package/dist/theglobalfuel/hooks/useTheGlobalFuelApi.js.map +1 -1
- package/dist/theglobalfuel/pages/alerts/AlertsPage.d.ts +16 -0
- package/dist/theglobalfuel/pages/alerts/AlertsPage.d.ts.map +1 -0
- package/dist/theglobalfuel/pages/alerts/AlertsPage.js +295 -0
- package/dist/theglobalfuel/pages/alerts/AlertsPage.js.map +1 -0
- package/dist/theglobalfuel/pages/analytics/AnalyticsPage.d.ts +13 -0
- package/dist/theglobalfuel/pages/analytics/AnalyticsPage.d.ts.map +1 -0
- package/dist/theglobalfuel/pages/analytics/AnalyticsPage.js +358 -0
- package/dist/theglobalfuel/pages/analytics/AnalyticsPage.js.map +1 -0
- package/dist/theglobalfuel/pages/assets/AssetDetailPage.d.ts.map +1 -1
- package/dist/theglobalfuel/pages/assets/AssetDetailPage.js +386 -342
- package/dist/theglobalfuel/pages/assets/AssetDetailPage.js.map +1 -1
- package/dist/theglobalfuel/pages/assets/AssetsPage.d.ts.map +1 -1
- package/dist/theglobalfuel/pages/assets/AssetsPage.js +277 -199
- package/dist/theglobalfuel/pages/assets/AssetsPage.js.map +1 -1
- package/dist/theglobalfuel/pages/contracts/ContractsPage.js +1 -1
- package/dist/theglobalfuel/pages/contracts/ContractsPage.js.map +1 -1
- package/dist/theglobalfuel/pages/dashboard/DashboardPage.js +6 -6
- package/dist/theglobalfuel/pages/dashboard/DashboardPage.js.map +1 -1
- package/dist/theglobalfuel/pages/emissions/EmissionsPage.js +1 -1
- package/dist/theglobalfuel/pages/emissions/EmissionsPage.js.map +1 -1
- package/dist/theglobalfuel/pages/inventory/InventoryPage.js +1 -1
- package/dist/theglobalfuel/pages/inventory/InventoryPage.js.map +1 -1
- package/dist/theglobalfuel/pages/provenance/ProvenancePage.d.ts.map +1 -1
- package/dist/theglobalfuel/pages/provenance/ProvenancePage.js +372 -216
- package/dist/theglobalfuel/pages/provenance/ProvenancePage.js.map +1 -1
- package/dist/theglobalfuel/pages/reports/ReportsPage.d.ts +16 -0
- package/dist/theglobalfuel/pages/reports/ReportsPage.d.ts.map +1 -0
- package/dist/theglobalfuel/pages/reports/ReportsPage.js +578 -0
- package/dist/theglobalfuel/pages/reports/ReportsPage.js.map +1 -0
- package/dist/theglobalfuel/pages/shipments/ShipmentsPage.d.ts.map +1 -1
- package/dist/theglobalfuel/pages/shipments/ShipmentsPage.js +43 -12
- package/dist/theglobalfuel/pages/shipments/ShipmentsPage.js.map +1 -1
- package/dist/theglobalfuel/types/index.d.ts +37 -8
- package/dist/theglobalfuel/types/index.d.ts.map +1 -1
- package/dist/theglobalfuel/utils/csv.d.ts +16 -0
- package/dist/theglobalfuel/utils/csv.d.ts.map +1 -0
- package/dist/theglobalfuel/utils/csv.js +14 -0
- package/dist/theglobalfuel/utils/csv.js.map +1 -0
- package/dist/theglobalfuel/utils/index.d.ts +2 -0
- package/dist/theglobalfuel/utils/index.d.ts.map +1 -1
- package/dist/theglobalfuel/utils/pageContext.d.ts.map +1 -1
- package/dist/theglobalfuel/utils/pageContext.js +32 -0
- package/dist/theglobalfuel/utils/pageContext.js.map +1 -1
- package/dist/theglobalfuel/utils/provenance.d.ts +135 -0
- package/dist/theglobalfuel/utils/provenance.d.ts.map +1 -0
- package/dist/theglobalfuel/utils/provenance.js +57 -0
- package/dist/theglobalfuel/utils/provenance.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { PageLayout as e } from "../../components/PageLayout.js";
|
|
2
|
+
import { formatEnumLabel as t, formatNumber as n, formatPercent as r, formatVolume as i } from "../../utils/formatters.js";
|
|
3
|
+
import { ErrorView as a, LoadingView as o } from "../../components/StateViews.js";
|
|
4
|
+
import { EmptyState as ee } from "../../components/EmptyState.js";
|
|
5
|
+
import { AreaChart as te } from "../../components/viz/AreaChart.js";
|
|
6
|
+
import { BarChart as s } from "../../components/viz/BarChart.js";
|
|
7
|
+
import { DonutChart as c } from "../../components/viz/DonutChart.js";
|
|
8
|
+
import { StatCard as l } from "../../components/viz/StatCard.js";
|
|
9
|
+
import { useTheAssets as ne, useTheContracts as re, useTheInventoryLots as ie, useTheProductionRecords as u, useTheShipments as ae } from "../../hooks/useTheGlobalFuelApi.js";
|
|
10
|
+
import { useThePageView as d } from "../../hooks/useThePageView.js";
|
|
11
|
+
import { useTheTranslation as oe } from "../../hooks/useTheTranslation.js";
|
|
12
|
+
import "../../hooks/index.js";
|
|
13
|
+
import { FilterTabs as se } from "../../components/FilterTabs.js";
|
|
14
|
+
import "../../components/index.js";
|
|
15
|
+
import { useMemo as f, useState as p } from "react";
|
|
16
|
+
import { Activity as m, Gauge as h, Package as g, Ship as _, Timer as v, TrendingUp as y } from "lucide-react";
|
|
17
|
+
import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
18
|
+
import { GlassCard as C } from "@burdenoff/fe-libs/ui";
|
|
19
|
+
//#region src/theglobalfuel/pages/analytics/AnalyticsPage.tsx
|
|
20
|
+
var w = { limit: 200 }, ce = [
|
|
21
|
+
{
|
|
22
|
+
value: "30",
|
|
23
|
+
label: "Last 30 days"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
value: "90",
|
|
27
|
+
label: "Last 90 days"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
value: "365",
|
|
31
|
+
label: "Last 12 months"
|
|
32
|
+
}
|
|
33
|
+
], T = 1440 * 60 * 1e3;
|
|
34
|
+
function E(e) {
|
|
35
|
+
let t = new Date(e);
|
|
36
|
+
return Number.isNaN(t.getTime()) ? "" : `${t.getUTCFullYear()}-${String(t.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
37
|
+
}
|
|
38
|
+
function le(e) {
|
|
39
|
+
let [t, n] = e.split("-"), r = new Date(Date.UTC(Number(t), Number(n) - 1, 1));
|
|
40
|
+
return Number.isNaN(r.getTime()) ? e : r.toLocaleString(void 0, {
|
|
41
|
+
month: "short",
|
|
42
|
+
year: "2-digit",
|
|
43
|
+
timeZone: "UTC"
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function ue(e) {
|
|
47
|
+
let t = /* @__PURE__ */ new Map();
|
|
48
|
+
for (let n of e) t.set(n.unit, (t.get(n.unit) ?? 0) + (n.volume ?? 0));
|
|
49
|
+
return t;
|
|
50
|
+
}
|
|
51
|
+
function de(e) {
|
|
52
|
+
let t = null, n = -1;
|
|
53
|
+
for (let [r, i] of e) i > n && (t = r, n = i);
|
|
54
|
+
return t;
|
|
55
|
+
}
|
|
56
|
+
function D() {
|
|
57
|
+
d("analytics");
|
|
58
|
+
let D = oe(), [O, fe] = p("90"), k = Number(O), [A] = p(() => Date.now()), j = f(() => A - k * T, [A, k]), M = ne({ pagination: w }), N = u({ pagination: w }), P = ae(void 0, void 0, w), F = re({ pagination: w }), I = ie({ pagination: w }), L = M.isLoading || N.isLoading || P.isLoading || F.isLoading || I.isLoading, R = M.isError || N.isError || P.isError || F.isError || I.isError, z = f(() => M.data?.items ?? [], [M.data]), B = f(() => F.data?.items ?? [], [F.data]), V = f(() => I.data?.items ?? [], [I.data]), H = f(() => (N.data?.items ?? []).filter((e) => {
|
|
59
|
+
let t = new Date(e.periodEnd ?? e.createdAt).getTime();
|
|
60
|
+
return !Number.isNaN(t) && t >= j;
|
|
61
|
+
}), [N.data, j]), U = f(() => (P.data?.items ?? []).filter((e) => {
|
|
62
|
+
let t = new Date(e.deliveredAt ?? e.departedAt ?? e.scheduledAt ?? e.createdAt).getTime();
|
|
63
|
+
return !Number.isNaN(t) && t >= j;
|
|
64
|
+
}), [P.data, j]), W = f(() => {
|
|
65
|
+
let e = /* @__PURE__ */ new Map();
|
|
66
|
+
for (let t of H) {
|
|
67
|
+
let n = e.get(t.assetId);
|
|
68
|
+
n && n.unit !== t.unit || e.set(t.assetId, {
|
|
69
|
+
produced: (n?.produced ?? 0) + (t.volume ?? 0),
|
|
70
|
+
unit: t.unit
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
let t = [], n = 0, r = 0;
|
|
74
|
+
for (let i of z) {
|
|
75
|
+
let a = e.get(i.id);
|
|
76
|
+
if (!a || i.capacityPerDay <= 0 || i.capacityUnit !== a.unit) continue;
|
|
77
|
+
let o = i.capacityPerDay * k;
|
|
78
|
+
o <= 0 || (n += a.produced, r += o, t.push({
|
|
79
|
+
name: i.name,
|
|
80
|
+
pct: a.produced / o * 100
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
overallPct: r > 0 ? n / r * 100 : null,
|
|
85
|
+
measuredAssets: t.length,
|
|
86
|
+
top: t.sort((e, t) => t.pct - e.pct).slice(0, 8)
|
|
87
|
+
};
|
|
88
|
+
}, [
|
|
89
|
+
H,
|
|
90
|
+
z,
|
|
91
|
+
k
|
|
92
|
+
]), pe = f(() => H.reduce((e, t) => e + (t.downtimeHours ?? 0), 0), [H]), G = f(() => ue(H), [H]), K = f(() => de(G), [G]), q = f(() => {
|
|
93
|
+
if (!K) return [];
|
|
94
|
+
let e = /* @__PURE__ */ new Map();
|
|
95
|
+
for (let t of H) {
|
|
96
|
+
if (t.unit !== K) continue;
|
|
97
|
+
let n = E(t.periodEnd ?? t.createdAt);
|
|
98
|
+
n && e.set(n, (e.get(n) ?? 0) + (t.volume ?? 0));
|
|
99
|
+
}
|
|
100
|
+
return [...e.entries()].sort(([e], [t]) => e.localeCompare(t)).map(([e, t]) => ({
|
|
101
|
+
label: le(e),
|
|
102
|
+
value: t
|
|
103
|
+
}));
|
|
104
|
+
}, [H, K]), J = f(() => {
|
|
105
|
+
let e = B.filter((e) => e.contractedVolume > 0 && (e.status === "ACTIVE" || e.status === "SIGNED")), t = e.reduce((e, t) => e + t.contractedVolume, 0), n = e.reduce((e, t) => e + t.deliveredVolume, 0), r = e.filter((e) => {
|
|
106
|
+
if (!e.endDate) return !1;
|
|
107
|
+
let t = (new Date(e.endDate).getTime() - A) / T, n = e.deliveredVolume / e.contractedVolume;
|
|
108
|
+
return t > 0 && t <= 60 && n < .75;
|
|
109
|
+
}).length;
|
|
110
|
+
return {
|
|
111
|
+
pct: t > 0 ? n / t * 100 : null,
|
|
112
|
+
liveCount: e.length,
|
|
113
|
+
atRisk: r,
|
|
114
|
+
top: e.map((e) => ({
|
|
115
|
+
label: e.reference,
|
|
116
|
+
value: Math.min(100, e.deliveredVolume / e.contractedVolume * 100)
|
|
117
|
+
})).sort((e, t) => e.value - t.value).slice(0, 8)
|
|
118
|
+
};
|
|
119
|
+
}, [B, A]), Y = f(() => {
|
|
120
|
+
let e = /* @__PURE__ */ new Map();
|
|
121
|
+
for (let t of U) e.set(t.mode, (e.get(t.mode) ?? 0) + 1);
|
|
122
|
+
return [...e.entries()].sort((e, t) => t[1] - e[1]).map(([e, n]) => ({
|
|
123
|
+
label: t(e),
|
|
124
|
+
value: n
|
|
125
|
+
}));
|
|
126
|
+
}, [U]), X = f(() => {
|
|
127
|
+
let e = U.filter((e) => e.status !== "CANCELLED");
|
|
128
|
+
return e.length === 0 ? null : e.filter((e) => e.status === "DELIVERED").length / e.length * 100;
|
|
129
|
+
}, [U]), me = f(() => U.filter((e) => e.status === "IN_TRANSIT" || e.status === "LOADING").length, [U]), Z = f(() => {
|
|
130
|
+
let e = /* @__PURE__ */ new Map();
|
|
131
|
+
for (let t of V) e.set(t.status, (e.get(t.status) ?? 0) + 1);
|
|
132
|
+
return [...e.entries()].sort((e, t) => t[1] - e[1]).map(([e, n]) => ({
|
|
133
|
+
label: t(e),
|
|
134
|
+
value: n
|
|
135
|
+
}));
|
|
136
|
+
}, [V]), Q = D("theglobalfuel.pages.analytics.description", "Utilization, fulfilment and movement performance across the portfolio."), $ = D("theglobalfuel.pages.analytics.title", "Operational Analytics");
|
|
137
|
+
if (L) return /* @__PURE__ */ x(e, {
|
|
138
|
+
title: $,
|
|
139
|
+
description: Q,
|
|
140
|
+
dataTour: "theglobalfuel-analytics-page",
|
|
141
|
+
children: /* @__PURE__ */ x(o, { label: "Aggregating operational data…" })
|
|
142
|
+
});
|
|
143
|
+
if (R) return /* @__PURE__ */ x(e, {
|
|
144
|
+
title: $,
|
|
145
|
+
description: Q,
|
|
146
|
+
dataTour: "theglobalfuel-analytics-page",
|
|
147
|
+
children: /* @__PURE__ */ x(a, {
|
|
148
|
+
message: "Couldn't load the operational data for analytics.",
|
|
149
|
+
onRetry: () => {
|
|
150
|
+
M.refetch(), N.refetch(), P.refetch(), F.refetch(), I.refetch();
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
});
|
|
154
|
+
let he = z.length > 0 || H.length > 0 || B.length > 0 || U.length > 0;
|
|
155
|
+
return /* @__PURE__ */ x(e, {
|
|
156
|
+
title: $,
|
|
157
|
+
description: Q,
|
|
158
|
+
dataTour: "theglobalfuel-analytics-page",
|
|
159
|
+
actions: /* @__PURE__ */ x(se, {
|
|
160
|
+
options: ce,
|
|
161
|
+
value: O,
|
|
162
|
+
onChange: fe
|
|
163
|
+
}),
|
|
164
|
+
children: he ? /* @__PURE__ */ S(b, { children: [
|
|
165
|
+
/* @__PURE__ */ S("div", {
|
|
166
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4",
|
|
167
|
+
children: [
|
|
168
|
+
/* @__PURE__ */ x(l, {
|
|
169
|
+
label: "Capacity utilization",
|
|
170
|
+
value: W.overallPct == null ? "—" : r(W.overallPct),
|
|
171
|
+
icon: /* @__PURE__ */ x(h, { className: "h-4 w-4" }),
|
|
172
|
+
deltaLabel: W.overallPct == null ? "No asset has both capacity and matching-unit production" : `${n(W.measuredAssets)} assets with capacity on record`
|
|
173
|
+
}),
|
|
174
|
+
/* @__PURE__ */ x(l, {
|
|
175
|
+
label: "Contract fulfilment",
|
|
176
|
+
value: J.pct == null ? "—" : r(J.pct),
|
|
177
|
+
icon: /* @__PURE__ */ x(y, { className: "h-4 w-4" }),
|
|
178
|
+
deltaLabel: `${n(J.liveCount)} live · ${n(J.atRisk)} at risk`
|
|
179
|
+
}),
|
|
180
|
+
/* @__PURE__ */ x(l, {
|
|
181
|
+
label: "Delivery rate",
|
|
182
|
+
value: X == null ? "—" : r(X),
|
|
183
|
+
icon: /* @__PURE__ */ x(_, { className: "h-4 w-4" }),
|
|
184
|
+
deltaLabel: `${n(me)} currently moving`
|
|
185
|
+
}),
|
|
186
|
+
/* @__PURE__ */ x(l, {
|
|
187
|
+
label: "Recorded downtime",
|
|
188
|
+
value: `${n(pe)} h`,
|
|
189
|
+
icon: /* @__PURE__ */ x(v, { className: "h-4 w-4" }),
|
|
190
|
+
invertDelta: !0,
|
|
191
|
+
deltaLabel: `Across ${n(H.length)} production records`
|
|
192
|
+
})
|
|
193
|
+
]
|
|
194
|
+
}),
|
|
195
|
+
/* @__PURE__ */ S("div", {
|
|
196
|
+
className: "mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3",
|
|
197
|
+
children: [/* @__PURE__ */ S(C, {
|
|
198
|
+
treatment: "glass",
|
|
199
|
+
className: "flex flex-col p-5 lg:col-span-2",
|
|
200
|
+
children: [/* @__PURE__ */ S("div", {
|
|
201
|
+
className: "mb-4",
|
|
202
|
+
children: [/* @__PURE__ */ x("h2", {
|
|
203
|
+
className: "text-base font-semibold text-text-primary",
|
|
204
|
+
children: "Production trend"
|
|
205
|
+
}), /* @__PURE__ */ x("p", {
|
|
206
|
+
className: "text-xs text-text-tertiary",
|
|
207
|
+
children: K ? `Monthly volume in ${t(K)} — the unit carrying most of the portfolio. Other units are charted on their own assets.` : "No production recorded in this window."
|
|
208
|
+
})]
|
|
209
|
+
}), q.length > 0 ? /* @__PURE__ */ x(te, {
|
|
210
|
+
series: q,
|
|
211
|
+
height: 220,
|
|
212
|
+
title: "Monthly production volume"
|
|
213
|
+
}) : /* @__PURE__ */ x("div", {
|
|
214
|
+
className: "rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary",
|
|
215
|
+
children: "No production records in this window."
|
|
216
|
+
})]
|
|
217
|
+
}), /* @__PURE__ */ S(C, {
|
|
218
|
+
treatment: "glass",
|
|
219
|
+
className: "flex flex-col p-5",
|
|
220
|
+
children: [/* @__PURE__ */ S("div", {
|
|
221
|
+
className: "mb-4",
|
|
222
|
+
children: [/* @__PURE__ */ x("h2", {
|
|
223
|
+
className: "text-base font-semibold text-text-primary",
|
|
224
|
+
children: "Movement mix"
|
|
225
|
+
}), /* @__PURE__ */ x("p", {
|
|
226
|
+
className: "text-xs text-text-tertiary",
|
|
227
|
+
children: "Shipments by transport mode."
|
|
228
|
+
})]
|
|
229
|
+
}), Y.length > 0 ? /* @__PURE__ */ x(c, {
|
|
230
|
+
segments: Y,
|
|
231
|
+
centerLabel: n(U.length),
|
|
232
|
+
centerCaption: "shipments",
|
|
233
|
+
showLegend: !0,
|
|
234
|
+
title: "Shipments by transport mode"
|
|
235
|
+
}) : /* @__PURE__ */ x("div", {
|
|
236
|
+
className: "rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary",
|
|
237
|
+
children: "No shipments in this window."
|
|
238
|
+
})]
|
|
239
|
+
})]
|
|
240
|
+
}),
|
|
241
|
+
/* @__PURE__ */ S("div", {
|
|
242
|
+
className: "mt-6 grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
243
|
+
children: [/* @__PURE__ */ S(C, {
|
|
244
|
+
treatment: "glass",
|
|
245
|
+
className: "flex flex-col p-5",
|
|
246
|
+
children: [/* @__PURE__ */ S("div", {
|
|
247
|
+
className: "mb-4",
|
|
248
|
+
children: [/* @__PURE__ */ x("h2", {
|
|
249
|
+
className: "text-base font-semibold text-text-primary",
|
|
250
|
+
children: "Utilization by asset"
|
|
251
|
+
}), /* @__PURE__ */ x("p", {
|
|
252
|
+
className: "text-xs text-text-tertiary",
|
|
253
|
+
children: "Produced volume against nameplate capacity for the window. Assets without a capacity figure, or whose production is recorded in a different unit, are omitted."
|
|
254
|
+
})]
|
|
255
|
+
}), W.top.length > 0 ? /* @__PURE__ */ x(s, {
|
|
256
|
+
series: W.top.map((e) => ({
|
|
257
|
+
label: e.name,
|
|
258
|
+
value: Math.round(e.pct)
|
|
259
|
+
})),
|
|
260
|
+
height: 220,
|
|
261
|
+
colorful: !0,
|
|
262
|
+
valueFormatter: (e) => `${e}%`,
|
|
263
|
+
title: "Capacity utilization by asset"
|
|
264
|
+
}) : /* @__PURE__ */ x("div", {
|
|
265
|
+
className: "rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary",
|
|
266
|
+
children: "No asset has both a capacity figure and matching-unit production in this window."
|
|
267
|
+
})]
|
|
268
|
+
}), /* @__PURE__ */ S(C, {
|
|
269
|
+
treatment: "glass",
|
|
270
|
+
className: "flex flex-col p-5",
|
|
271
|
+
children: [/* @__PURE__ */ S("div", {
|
|
272
|
+
className: "mb-4",
|
|
273
|
+
children: [/* @__PURE__ */ x("h2", {
|
|
274
|
+
className: "text-base font-semibold text-text-primary",
|
|
275
|
+
children: "Least-fulfilled contracts"
|
|
276
|
+
}), /* @__PURE__ */ x("p", {
|
|
277
|
+
className: "text-xs text-text-tertiary",
|
|
278
|
+
children: "Delivered against contracted volume for live (signed/active) contracts."
|
|
279
|
+
})]
|
|
280
|
+
}), J.top.length > 0 ? /* @__PURE__ */ x(s, {
|
|
281
|
+
series: J.top.map((e) => ({
|
|
282
|
+
label: e.label,
|
|
283
|
+
value: Math.round(e.value)
|
|
284
|
+
})),
|
|
285
|
+
height: 220,
|
|
286
|
+
colorful: !0,
|
|
287
|
+
valueFormatter: (e) => `${e}%`,
|
|
288
|
+
title: "Contract fulfilment"
|
|
289
|
+
}) : /* @__PURE__ */ x("div", {
|
|
290
|
+
className: "rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary",
|
|
291
|
+
children: "No live contracts with a contracted volume."
|
|
292
|
+
})]
|
|
293
|
+
})]
|
|
294
|
+
}),
|
|
295
|
+
/* @__PURE__ */ S("div", {
|
|
296
|
+
className: "mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3",
|
|
297
|
+
children: [/* @__PURE__ */ S(C, {
|
|
298
|
+
treatment: "glass",
|
|
299
|
+
className: "flex flex-col p-5",
|
|
300
|
+
children: [/* @__PURE__ */ S("div", {
|
|
301
|
+
className: "mb-4 flex items-center gap-2",
|
|
302
|
+
children: [/* @__PURE__ */ x(g, { className: "h-4 w-4 text-text-secondary" }), /* @__PURE__ */ x("h2", {
|
|
303
|
+
className: "text-base font-semibold text-text-primary",
|
|
304
|
+
children: "Inventory position"
|
|
305
|
+
})]
|
|
306
|
+
}), Z.length > 0 ? /* @__PURE__ */ x(c, {
|
|
307
|
+
segments: Z,
|
|
308
|
+
centerLabel: n(V.length),
|
|
309
|
+
centerCaption: "lots",
|
|
310
|
+
showLegend: !0,
|
|
311
|
+
title: "Inventory lots by status"
|
|
312
|
+
}) : /* @__PURE__ */ x("div", {
|
|
313
|
+
className: "rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary",
|
|
314
|
+
children: "No inventory lots recorded."
|
|
315
|
+
})]
|
|
316
|
+
}), /* @__PURE__ */ S(C, {
|
|
317
|
+
treatment: "glass",
|
|
318
|
+
className: "p-5 lg:col-span-2",
|
|
319
|
+
children: [
|
|
320
|
+
/* @__PURE__ */ x("h2", {
|
|
321
|
+
className: "mb-4 text-base font-semibold text-text-primary",
|
|
322
|
+
children: "Production volume by unit"
|
|
323
|
+
}),
|
|
324
|
+
/* @__PURE__ */ x("p", {
|
|
325
|
+
className: "mb-4 text-xs text-text-tertiary",
|
|
326
|
+
children: "Volumes are never summed across units of measure. Each row is an independent total."
|
|
327
|
+
}),
|
|
328
|
+
G.size > 0 ? /* @__PURE__ */ x("ul", {
|
|
329
|
+
className: "divide-y divide-border-subtle",
|
|
330
|
+
children: [...G.entries()].sort((e, t) => t[1] - e[1]).map(([e, n]) => /* @__PURE__ */ S("li", {
|
|
331
|
+
className: "flex items-center justify-between py-2.5",
|
|
332
|
+
children: [/* @__PURE__ */ x("span", {
|
|
333
|
+
className: "text-sm text-text-secondary",
|
|
334
|
+
children: t(e)
|
|
335
|
+
}), /* @__PURE__ */ x("span", {
|
|
336
|
+
className: "text-sm font-medium tabular-nums text-text-primary",
|
|
337
|
+
children: i(n, e)
|
|
338
|
+
})]
|
|
339
|
+
}, e))
|
|
340
|
+
}) : /* @__PURE__ */ x("div", {
|
|
341
|
+
className: "rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary",
|
|
342
|
+
children: "No production records in this window."
|
|
343
|
+
})
|
|
344
|
+
]
|
|
345
|
+
})]
|
|
346
|
+
})
|
|
347
|
+
] }) : /* @__PURE__ */ x(ee, {
|
|
348
|
+
illustration: "empty-data",
|
|
349
|
+
icon: /* @__PURE__ */ x(m, { className: "h-10 w-10" }),
|
|
350
|
+
title: "Nothing to analyse yet",
|
|
351
|
+
description: "Register assets and record production, contracts and shipments — analytics is computed from those records."
|
|
352
|
+
})
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
//#endregion
|
|
356
|
+
export { D as AnalyticsPage };
|
|
357
|
+
|
|
358
|
+
//# sourceMappingURL=AnalyticsPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../../src/theglobalfuel/pages/analytics/AnalyticsPage.tsx"],"sourcesContent":["/**\n * Operational Analytics — utilization, fulfilment and movement performance.\n * @module theglobalfuel/pages/analytics\n *\n * Where the dashboard answers \"what is my portfolio\", this page answers \"how\n * well is it running\". Every figure is aggregated client-side from the same\n * real `the*` list endpoints the rest of the product uses — there is no\n * separate analytics store and no mock data. The window selector re-slices the\n * already-fetched production/shipment rows rather than refetching, so switching\n * periods is instant and costs no extra backend calls.\n */\n\nimport { useMemo, useState } from 'react';\nimport { Activity, Gauge, Ship, Package, TrendingUp, Timer } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n LoadingView,\n ErrorView,\n EmptyState,\n FilterTabs,\n type FilterOption,\n AreaChart,\n BarChart,\n DonutChart,\n StatCard,\n type Segment,\n type LabeledValue,\n} from '../../components';\nimport {\n useTheAssets,\n useTheProductionRecords,\n useTheShipments,\n useTheContracts,\n useTheInventoryLots,\n useThePageView,\n useTheTranslation,\n} from '../../hooks';\nimport { formatNumber, formatPercent, formatEnumLabel, formatVolume } from '../../utils';\nimport type { TheProductionRecord, TheUnit } from '../../types';\n\nconst PAGE: { limit: number } = { limit: 200 };\n\n/** Analysis window, in days back from now. */\ntype WindowKey = '30' | '90' | '365';\n\nconst WINDOWS: FilterOption<WindowKey>[] = [\n { value: '30', label: 'Last 30 days' },\n { value: '90', label: 'Last 90 days' },\n { value: '365', label: 'Last 12 months' },\n];\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/** Month bucket key (YYYY-MM) for a date string; '' when unparseable. */\nfunction monthKey(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return '';\n return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}`;\n}\n\n/** Short month label (e.g. \"Mar 26\") for a YYYY-MM key. */\nfunction monthLabel(key: string): string {\n const [year, month] = key.split('-');\n const d = new Date(Date.UTC(Number(year), Number(month) - 1, 1));\n if (Number.isNaN(d.getTime())) return key;\n return d.toLocaleString(undefined, { month: 'short', year: '2-digit', timeZone: 'UTC' });\n}\n\n/**\n * Group volumes by unit. Assets report in BARRELS / TONNES / MCF / MWH …, and\n * summing across them would produce a meaningless number, so every total on\n * this page is either unit-scoped or unitless (hours, counts, percentages).\n */\nfunction volumeByUnit(rows: readonly { volume: number; unit: TheUnit }[]): Map<TheUnit, number> {\n const out = new Map<TheUnit, number>();\n for (const row of rows) {\n out.set(row.unit, (out.get(row.unit) ?? 0) + (row.volume ?? 0));\n }\n return out;\n}\n\n/** The unit carrying the largest share of production — the page's lead unit. */\nfunction dominantUnit(byUnit: Map<TheUnit, number>): TheUnit | null {\n let best: TheUnit | null = null;\n let bestValue = -1;\n for (const [unit, value] of byUnit) {\n if (value > bestValue) {\n best = unit;\n bestValue = value;\n }\n }\n return best;\n}\n\nexport function AnalyticsPage() {\n useThePageView('analytics');\n const t = useTheTranslation();\n\n const [windowKey, setWindowKey] = useState<WindowKey>('90');\n const windowDays = Number(windowKey);\n // Sample the clock exactly once, when the page mounts. Reading Date.now()\n // during render would make every aggregate below re-derive against a moving\n // reference point, so a record could silently drop out of the window on an\n // unrelated re-render. A lazy useState initializer pins it for the visit.\n const [evaluatedAt] = useState(() => Date.now());\n const cutoff = useMemo(() => evaluatedAt - windowDays * DAY_MS, [evaluatedAt, windowDays]);\n\n const assets = useTheAssets({ pagination: PAGE });\n const production = useTheProductionRecords({ pagination: PAGE });\n const shipments = useTheShipments(undefined, undefined, PAGE);\n const contracts = useTheContracts({ pagination: PAGE });\n const inventory = useTheInventoryLots({ pagination: PAGE });\n\n const isLoading =\n assets.isLoading ||\n production.isLoading ||\n shipments.isLoading ||\n contracts.isLoading ||\n inventory.isLoading;\n const isError =\n assets.isError ||\n production.isError ||\n shipments.isError ||\n contracts.isError ||\n inventory.isError;\n\n const assetRows = useMemo(() => assets.data?.items ?? [], [assets.data]);\n const contractRows = useMemo(() => contracts.data?.items ?? [], [contracts.data]);\n const lotRows = useMemo(() => inventory.data?.items ?? [], [inventory.data]);\n\n // ── Window-scoped slices ───────────────────────────────────────────────────\n const productionRows = useMemo<TheProductionRecord[]>(\n () =>\n (production.data?.items ?? []).filter((r) => {\n const ts = new Date(r.periodEnd ?? r.createdAt).getTime();\n return !Number.isNaN(ts) && ts >= cutoff;\n }),\n [production.data, cutoff]\n );\n\n const shipmentRows = useMemo(\n () =>\n (shipments.data?.items ?? []).filter((s) => {\n const ts = new Date(\n s.deliveredAt ?? s.departedAt ?? s.scheduledAt ?? s.createdAt\n ).getTime();\n return !Number.isNaN(ts) && ts >= cutoff;\n }),\n [shipments.data, cutoff]\n );\n\n // ── Utilization: produced volume vs nameplate capacity over the window ─────\n // Only assets whose reporting unit matches the production record's unit are\n // counted, so utilization never mixes units. Assets with no capacity on\n // record are excluded rather than counted as 0% (which would be a lie).\n const utilization = useMemo(() => {\n const byAsset = new Map<string, { produced: number; unit: TheUnit }>();\n for (const r of productionRows) {\n const prev = byAsset.get(r.assetId);\n if (prev && prev.unit !== r.unit) continue;\n byAsset.set(r.assetId, { produced: (prev?.produced ?? 0) + (r.volume ?? 0), unit: r.unit });\n }\n\n const rows: { name: string; pct: number }[] = [];\n let totalProduced = 0;\n let totalCapacity = 0;\n for (const asset of assetRows) {\n const agg = byAsset.get(asset.id);\n if (!agg || asset.capacityPerDay <= 0 || asset.capacityUnit !== agg.unit) continue;\n const capacity = asset.capacityPerDay * windowDays;\n if (capacity <= 0) continue;\n totalProduced += agg.produced;\n totalCapacity += capacity;\n rows.push({ name: asset.name, pct: (agg.produced / capacity) * 100 });\n }\n return {\n overallPct: totalCapacity > 0 ? (totalProduced / totalCapacity) * 100 : null,\n measuredAssets: rows.length,\n top: rows.sort((a, b) => b.pct - a.pct).slice(0, 8),\n };\n }, [productionRows, assetRows, windowDays]);\n\n // ── Downtime ───────────────────────────────────────────────────────────────\n const downtimeHours = useMemo(\n () => productionRows.reduce((sum, r) => sum + (r.downtimeHours ?? 0), 0),\n [productionRows]\n );\n\n // ── Production trend (monthly, dominant unit only) ─────────────────────────\n const productionByUnit = useMemo(() => volumeByUnit(productionRows), [productionRows]);\n const leadUnit = useMemo(() => dominantUnit(productionByUnit), [productionByUnit]);\n\n const trendSeries = useMemo<LabeledValue[]>(() => {\n if (!leadUnit) return [];\n const buckets = new Map<string, number>();\n for (const r of productionRows) {\n if (r.unit !== leadUnit) continue;\n const key = monthKey(r.periodEnd ?? r.createdAt);\n if (!key) continue;\n buckets.set(key, (buckets.get(key) ?? 0) + (r.volume ?? 0));\n }\n return [...buckets.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, value]) => ({ label: monthLabel(key), value }));\n }, [productionRows, leadUnit]);\n\n // ── Contract fulfilment ────────────────────────────────────────────────────\n const fulfilment = useMemo(() => {\n const live = contractRows.filter(\n (c) => c.contractedVolume > 0 && (c.status === 'ACTIVE' || c.status === 'SIGNED')\n );\n const contracted = live.reduce((s, c) => s + c.contractedVolume, 0);\n const delivered = live.reduce((s, c) => s + c.deliveredVolume, 0);\n const atRisk = live.filter((c) => {\n if (!c.endDate) return false;\n const daysLeft = (new Date(c.endDate).getTime() - evaluatedAt) / DAY_MS;\n const pct = c.deliveredVolume / c.contractedVolume;\n return daysLeft > 0 && daysLeft <= 60 && pct < 0.75;\n }).length;\n return {\n pct: contracted > 0 ? (delivered / contracted) * 100 : null,\n liveCount: live.length,\n atRisk,\n top: live\n .map((c) => ({\n label: c.reference,\n value: Math.min(100, (c.deliveredVolume / c.contractedVolume) * 100),\n }))\n .sort((a, b) => a.value - b.value)\n .slice(0, 8),\n };\n }, [contractRows, evaluatedAt]);\n\n // ── Shipment mode mix + delivery rate ──────────────────────────────────────\n const shipmentMix = useMemo<Segment[]>(() => {\n const buckets = new Map<string, number>();\n for (const s of shipmentRows) {\n buckets.set(s.mode, (buckets.get(s.mode) ?? 0) + 1);\n }\n return [...buckets.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([mode, value]) => ({ label: formatEnumLabel(mode), value }));\n }, [shipmentRows]);\n\n const deliveryRate = useMemo(() => {\n const terminalOrDone = shipmentRows.filter((s) => s.status !== 'CANCELLED');\n if (terminalOrDone.length === 0) return null;\n const delivered = terminalOrDone.filter((s) => s.status === 'DELIVERED').length;\n return (delivered / terminalOrDone.length) * 100;\n }, [shipmentRows]);\n\n const inTransit = useMemo(\n () => shipmentRows.filter((s) => s.status === 'IN_TRANSIT' || s.status === 'LOADING').length,\n [shipmentRows]\n );\n\n // ── Inventory position ─────────────────────────────────────────────────────\n const inventoryMix = useMemo<Segment[]>(() => {\n const buckets = new Map<string, number>();\n for (const l of lotRows) {\n buckets.set(l.status, (buckets.get(l.status) ?? 0) + 1);\n }\n return [...buckets.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([status, value]) => ({ label: formatEnumLabel(status), value }));\n }, [lotRows]);\n\n const description = t(\n 'theglobalfuel.pages.analytics.description',\n 'Utilization, fulfilment and movement performance across the portfolio.'\n );\n const pageTitle = t('theglobalfuel.pages.analytics.title', 'Operational Analytics');\n\n if (isLoading) {\n return (\n <PageLayout\n title={pageTitle}\n description={description}\n dataTour=\"theglobalfuel-analytics-page\"\n >\n <LoadingView label=\"Aggregating operational data…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout\n title={pageTitle}\n description={description}\n dataTour=\"theglobalfuel-analytics-page\"\n >\n <ErrorView\n message=\"Couldn't load the operational data for analytics.\"\n onRetry={() => {\n void assets.refetch();\n void production.refetch();\n void shipments.refetch();\n void contracts.refetch();\n void inventory.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n const hasAnything =\n assetRows.length > 0 ||\n productionRows.length > 0 ||\n contractRows.length > 0 ||\n shipmentRows.length > 0;\n\n return (\n <PageLayout\n title={pageTitle}\n description={description}\n dataTour=\"theglobalfuel-analytics-page\"\n actions={<FilterTabs options={WINDOWS} value={windowKey} onChange={setWindowKey} />}\n >\n {!hasAnything ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Activity className=\"h-10 w-10\" />}\n title=\"Nothing to analyse yet\"\n description=\"Register assets and record production, contracts and shipments — analytics is computed from those records.\"\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <StatCard\n label=\"Capacity utilization\"\n value={utilization.overallPct != null ? formatPercent(utilization.overallPct) : '—'}\n icon={<Gauge className=\"h-4 w-4\" />}\n deltaLabel={\n utilization.overallPct != null\n ? `${formatNumber(utilization.measuredAssets)} assets with capacity on record`\n : 'No asset has both capacity and matching-unit production'\n }\n />\n <StatCard\n label=\"Contract fulfilment\"\n value={fulfilment.pct != null ? formatPercent(fulfilment.pct) : '—'}\n icon={<TrendingUp className=\"h-4 w-4\" />}\n deltaLabel={`${formatNumber(fulfilment.liveCount)} live · ${formatNumber(fulfilment.atRisk)} at risk`}\n />\n <StatCard\n label=\"Delivery rate\"\n value={deliveryRate != null ? formatPercent(deliveryRate) : '—'}\n icon={<Ship className=\"h-4 w-4\" />}\n deltaLabel={`${formatNumber(inTransit)} currently moving`}\n />\n <StatCard\n label=\"Recorded downtime\"\n value={`${formatNumber(downtimeHours)} h`}\n icon={<Timer className=\"h-4 w-4\" />}\n invertDelta\n deltaLabel={`Across ${formatNumber(productionRows.length)} production records`}\n />\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5 lg:col-span-2\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Production trend</h2>\n <p className=\"text-xs text-text-tertiary\">\n {leadUnit\n ? `Monthly volume in ${formatEnumLabel(leadUnit)} — the unit carrying most of the portfolio. Other units are charted on their own assets.`\n : 'No production recorded in this window.'}\n </p>\n </div>\n {trendSeries.length > 0 ? (\n <AreaChart series={trendSeries} height={220} title=\"Monthly production volume\" />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No production records in this window.\n </div>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Movement mix</h2>\n <p className=\"text-xs text-text-tertiary\">Shipments by transport mode.</p>\n </div>\n {shipmentMix.length > 0 ? (\n <DonutChart\n segments={shipmentMix}\n centerLabel={formatNumber(shipmentRows.length)}\n centerCaption=\"shipments\"\n showLegend\n title=\"Shipments by transport mode\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No shipments in this window.\n </div>\n )}\n </GlassCard>\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">Utilization by asset</h2>\n <p className=\"text-xs text-text-tertiary\">\n Produced volume against nameplate capacity for the window. Assets without a\n capacity figure, or whose production is recorded in a different unit, are omitted.\n </p>\n </div>\n {utilization.top.length > 0 ? (\n <BarChart\n series={utilization.top.map((r) => ({ label: r.name, value: Math.round(r.pct) }))}\n height={220}\n colorful\n valueFormatter={(v) => `${v}%`}\n title=\"Capacity utilization by asset\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No asset has both a capacity figure and matching-unit production in this window.\n </div>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Least-fulfilled contracts\n </h2>\n <p className=\"text-xs text-text-tertiary\">\n Delivered against contracted volume for live (signed/active) contracts.\n </p>\n </div>\n {fulfilment.top.length > 0 ? (\n <BarChart\n series={fulfilment.top.map((r) => ({\n label: r.label,\n value: Math.round(r.value),\n }))}\n height={220}\n colorful\n valueFormatter={(v) => `${v}%`}\n title=\"Contract fulfilment\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No live contracts with a contracted volume.\n </div>\n )}\n </GlassCard>\n </div>\n\n <div className=\"mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <GlassCard treatment=\"glass\" className=\"flex flex-col p-5\">\n <div className=\"mb-4 flex items-center gap-2\">\n <Package className=\"h-4 w-4 text-text-secondary\" />\n <h2 className=\"text-base font-semibold text-text-primary\">Inventory position</h2>\n </div>\n {inventoryMix.length > 0 ? (\n <DonutChart\n segments={inventoryMix}\n centerLabel={formatNumber(lotRows.length)}\n centerCaption=\"lots\"\n showLegend\n title=\"Inventory lots by status\"\n />\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No inventory lots recorded.\n </div>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"glass\" className=\"p-5 lg:col-span-2\">\n <h2 className=\"mb-4 text-base font-semibold text-text-primary\">\n Production volume by unit\n </h2>\n <p className=\"mb-4 text-xs text-text-tertiary\">\n Volumes are never summed across units of measure. Each row is an independent total.\n </p>\n {productionByUnit.size > 0 ? (\n <ul className=\"divide-y divide-border-subtle\">\n {[...productionByUnit.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([unit, value]) => (\n <li key={unit} className=\"flex items-center justify-between py-2.5\">\n <span className=\"text-sm text-text-secondary\">{formatEnumLabel(unit)}</span>\n <span className=\"text-sm font-medium tabular-nums text-text-primary\">\n {formatVolume(value, unit)}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <div className=\"rounded-xl border border-dashed border-border-seam py-10 text-center text-sm text-text-secondary\">\n No production records in this window.\n </div>\n )}\n </GlassCard>\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/theglobalfuel/pages/assets/AssetDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"AssetDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/theglobalfuel/pages/assets/AssetDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA2IH,wBAAgB,eAAe,gCA2hC9B"}
|