@burdenoff/microfe-movethewheels 2026.1017.16 → 2026.1017.17

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.
@@ -214,6 +214,11 @@ var E = ({ registerNavItems: v, basePath: b = "/" }) => {
214
214
  label: C("movethewheels.root.nav.tripUtilization", "Reported trip utilization"),
215
215
  path: E(b, "routes/trip-utilization"),
216
216
  icon: /* @__PURE__ */ S(m, { size: 16 })
217
+ }, {
218
+ id: "mtw-fuel-preview",
219
+ label: C("movethewheels.root.nav.fuelPreview", "Consumed fuel preview"),
220
+ path: E(b, "fuel-preview"),
221
+ icon: /* @__PURE__ */ S(m, { size: 16 })
217
222
  }]
218
223
  },
219
224
  {
@@ -28,8 +28,8 @@ function d() {
28
28
  })
29
29
  });
30
30
  }
31
- var f = r(() => import("./pages/DashboardPage.js")), p = r(() => import("./pages/OrdersListPage.js")), m = r(() => import("./pages/OrderDetailsPage.js")), h = r(() => import("./pages/CreateOrderPage.js")), g = r(() => import("./pages/TrackingPage.js")), _ = r(() => import("./pages/TrackingEventQualityPage.js")), v = r(() => import("./pages/ShipmentStatusAuthorityPage.js")), y = r(() => import("./pages/LiveTrackingPage.js")), b = r(() => import("./pages/FleetPage.js")), x = r(() => import("./pages/VehiclesPage.js")), S = r(() => import("./pages/VehicleDetailsPage.js")), C = r(() => import("./pages/DriversPage.js")), w = r(() => import("./pages/DriverDetailsPage.js")), T = r(() => import("./pages/InventoryPage.js")), E = r(() => import("./pages/ProductsPage.js")), D = r(() => import("./pages/ProductDetailsPage.js")), O = r(() => import("./pages/PalletPreviewPage.js")), k = r(() => import("./pages/WarehousesPage.js")), A = r(() => import("./pages/WarehouseDetailsPage.js")), j = r(() => import("./pages/CustomersPage.js")), M = r(() => import("./pages/CustomerDetailsPage.js")), N = r(() => import("./pages/RoutesPage.js")), P = r(() => import("./pages/RouteDetailsPage.js")), ee = r(() => import("./pages/AnalyticsPage.js")), F = r(() => import("./pages/FinancePage.js")), I = r(() => import("./pages/FreightEstimatePage.js")), L = r(() => import("./pages/FreightInvoicePage.js")), R = r(() => import("./pages/ConsolidationPage.js")), z = r(() => import("./pages/LogisticsProceduresPage.js")), B = r(() => import("./pages/MarketplacePage.js")), V = r(() => import("./pages/ImportExportPage.js")), H = r(() => import("./pages/ReportsPage.js")), U = r(() => import("./pages/SettingsPage.js")), W = r(() => import("./pages/AIAssistantPage.js")), G = r(() => import("./pages/DeliveryCapturePage.js")), K = r(() => import("./pages/MultiModalShipmentPage.js")), q = r(() => import("./pages/ShippingRatesPage.js")), J = r(() => import("./pages/ContainersPage.js")), Y = r(() => import("./pages/ContainerConditionPage.js")), X = r(() => import("./pages/FreeTimePage.js")), Z = r(() => import("./pages/PendingPickPage.js")), Q = r(() => import("./pages/StockObservationsPage.js")), $ = r(() => import("./pages/StockBindingPage.js")), te = r(() => import("./pages/TripUtilizationPage.js")), ne = r(() => import("./pages/EmptyReusePage.js"));
32
- function re() {
31
+ var f = r(() => import("./pages/DashboardPage.js")), p = r(() => import("./pages/OrdersListPage.js")), m = r(() => import("./pages/OrderDetailsPage.js")), h = r(() => import("./pages/CreateOrderPage.js")), g = r(() => import("./pages/TrackingPage.js")), _ = r(() => import("./pages/TrackingEventQualityPage.js")), v = r(() => import("./pages/ShipmentStatusAuthorityPage.js")), y = r(() => import("./pages/LiveTrackingPage.js")), b = r(() => import("./pages/FleetPage.js")), x = r(() => import("./pages/VehiclesPage.js")), S = r(() => import("./pages/VehicleDetailsPage.js")), C = r(() => import("./pages/DriversPage.js")), w = r(() => import("./pages/DriverDetailsPage.js")), T = r(() => import("./pages/InventoryPage.js")), E = r(() => import("./pages/ProductsPage.js")), D = r(() => import("./pages/ProductDetailsPage.js")), O = r(() => import("./pages/PalletPreviewPage.js")), k = r(() => import("./pages/WarehousesPage.js")), A = r(() => import("./pages/WarehouseDetailsPage.js")), j = r(() => import("./pages/CustomersPage.js")), M = r(() => import("./pages/CustomerDetailsPage.js")), N = r(() => import("./pages/RoutesPage.js")), P = r(() => import("./pages/RouteDetailsPage.js")), ee = r(() => import("./pages/AnalyticsPage.js")), F = r(() => import("./pages/FinancePage.js")), I = r(() => import("./pages/FreightEstimatePage.js")), L = r(() => import("./pages/FreightInvoicePage.js")), R = r(() => import("./pages/ConsolidationPage.js")), z = r(() => import("./pages/LogisticsProceduresPage.js")), B = r(() => import("./pages/MarketplacePage.js")), V = r(() => import("./pages/ImportExportPage.js")), H = r(() => import("./pages/ReportsPage.js")), U = r(() => import("./pages/SettingsPage.js")), W = r(() => import("./pages/AIAssistantPage.js")), G = r(() => import("./pages/DeliveryCapturePage.js")), K = r(() => import("./pages/MultiModalShipmentPage.js")), q = r(() => import("./pages/ShippingRatesPage.js")), J = r(() => import("./pages/ContainersPage.js")), Y = r(() => import("./pages/ContainerConditionPage.js")), X = r(() => import("./pages/FreeTimePage.js")), Z = r(() => import("./pages/PendingPickPage.js")), Q = r(() => import("./pages/StockObservationsPage.js")), $ = r(() => import("./pages/StockBindingPage.js")), te = r(() => import("./pages/TripUtilizationPage.js")), ne = r(() => import("./pages/ConsumedFuelPreviewPage.js")), re = r(() => import("./pages/EmptyReusePage.js"));
32
+ function ie() {
33
33
  return /* @__PURE__ */ s(n, {
34
34
  fallback: /* @__PURE__ */ s(d, {}),
35
35
  children: /* @__PURE__ */ c(a, { children: [
@@ -95,7 +95,7 @@ function re() {
95
95
  }),
96
96
  /* @__PURE__ */ s(i, {
97
97
  path: "containers/empty-reuse",
98
- element: /* @__PURE__ */ s(ne, {})
98
+ element: /* @__PURE__ */ s(re, {})
99
99
  }),
100
100
  /* @__PURE__ */ s(i, {
101
101
  path: "containers/consolidation",
@@ -213,6 +213,10 @@ function re() {
213
213
  path: "routes/trip-utilization",
214
214
  element: /* @__PURE__ */ s(te, {})
215
215
  }),
216
+ /* @__PURE__ */ s(i, {
217
+ path: "fuel-preview",
218
+ element: /* @__PURE__ */ s(ne, {})
219
+ }),
216
220
  /* @__PURE__ */ s(i, {
217
221
  path: "routes/new",
218
222
  element: /* @__PURE__ */ s(P, {})
@@ -269,4 +273,4 @@ function re() {
269
273
  });
270
274
  }
271
275
  //#endregion
272
- export { re as MoveTheWheelsRoutes };
276
+ export { ie as MoveTheWheelsRoutes };
@@ -0,0 +1,132 @@
1
+ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
2
+ /** Internal type. DO NOT USE DIRECTLY. */
3
+ type Exact<T extends {
4
+ [key: string]: unknown;
5
+ }> = {
6
+ [K in keyof T]: T[K];
7
+ };
8
+ /** Internal type. DO NOT USE DIRECTLY. */
9
+ export type Incremental<T> = T | {
10
+ [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never;
11
+ };
12
+ import type * as Types from './consumed-fuel-preview-operations';
13
+ export type Maybe<T> = T | null;
14
+ export type InputMaybe<T> = Maybe<T>;
15
+ /** All built-in and custom scalars, mapped to their actual values */
16
+ export type Scalars = {
17
+ ID: {
18
+ input: string;
19
+ output: string;
20
+ };
21
+ String: {
22
+ input: string;
23
+ output: string;
24
+ };
25
+ Boolean: {
26
+ input: boolean;
27
+ output: boolean;
28
+ };
29
+ Int: {
30
+ input: number;
31
+ output: number;
32
+ };
33
+ Float: {
34
+ input: number;
35
+ output: number;
36
+ };
37
+ };
38
+ export type MtwConsumedFuelBasis = 'ESTIMATED_CONSUMPTION' | 'MEASURED_CONSUMPTION' | 'PURCHASED_OR_REFUELLED' | 'UNKNOWN';
39
+ export type MtwConsumedFuelPreview = {
40
+ __typename?: 'MtwConsumedFuelPreview';
41
+ distance?: Maybe<MtwFuelDistanceObservation>;
42
+ fuel?: Maybe<MtwFuelVolumeObservation>;
43
+ litresPer100Km?: Maybe<Scalars['Float']['output']>;
44
+ qualification: MtwConsumedFuelQualification;
45
+ reasons: Array<MtwConsumedFuelReason>;
46
+ status: MtwUtilizationMetricStatus;
47
+ };
48
+ export type MtwConsumedFuelPreviewInput = {
49
+ distance?: InputMaybe<MtwFuelDistanceObservationInput>;
50
+ fuel?: InputMaybe<MtwFuelVolumeObservationInput>;
51
+ };
52
+ export type MtwConsumedFuelQualification = 'OPERATOR_REPORTED_UNVERIFIED';
53
+ export type MtwConsumedFuelReason = 'DISTANCE_UNKNOWN' | 'FUEL_UNKNOWN' | 'INTERVAL_MISMATCH' | 'NON_MEASURED_DISTANCE' | 'PARTIAL_COVERAGE' | 'SOURCE_UNKNOWN' | 'UNSUPPORTED_FUEL_BASIS' | 'ZERO_DISTANCE';
54
+ export type MtwFuelDistanceObservation = {
55
+ __typename?: 'MtwFuelDistanceObservation';
56
+ basis: MtwUtilizationDistanceBasis;
57
+ coverage: MtwTripUtilizationCoverage;
58
+ intervalFrom: Scalars['String']['output'];
59
+ intervalUntil: Scalars['String']['output'];
60
+ metres?: Maybe<Scalars['Int']['output']>;
61
+ sourceReference?: Maybe<Scalars['String']['output']>;
62
+ sourceRevision?: Maybe<Scalars['String']['output']>;
63
+ };
64
+ export type MtwFuelDistanceObservationInput = {
65
+ basis: MtwUtilizationDistanceBasis;
66
+ coverage: MtwTripUtilizationCoverage;
67
+ intervalFrom: Scalars['String']['input'];
68
+ intervalUntil: Scalars['String']['input'];
69
+ metres?: InputMaybe<Scalars['Int']['input']>;
70
+ sourceReference?: InputMaybe<Scalars['String']['input']>;
71
+ sourceRevision?: InputMaybe<Scalars['String']['input']>;
72
+ };
73
+ export type MtwFuelVolumeObservation = {
74
+ __typename?: 'MtwFuelVolumeObservation';
75
+ basis: MtwConsumedFuelBasis;
76
+ coverage: MtwTripUtilizationCoverage;
77
+ intervalFrom: Scalars['String']['output'];
78
+ intervalUntil: Scalars['String']['output'];
79
+ millilitres?: Maybe<Scalars['Int']['output']>;
80
+ sourceReference?: Maybe<Scalars['String']['output']>;
81
+ sourceRevision?: Maybe<Scalars['String']['output']>;
82
+ };
83
+ export type MtwFuelVolumeObservationInput = {
84
+ basis: MtwConsumedFuelBasis;
85
+ coverage: MtwTripUtilizationCoverage;
86
+ intervalFrom: Scalars['String']['input'];
87
+ intervalUntil: Scalars['String']['input'];
88
+ millilitres?: InputMaybe<Scalars['Int']['input']>;
89
+ sourceReference?: InputMaybe<Scalars['String']['input']>;
90
+ sourceRevision?: InputMaybe<Scalars['String']['input']>;
91
+ };
92
+ export type MtwTripUtilizationCoverage = 'COMPLETE' | 'PARTIAL';
93
+ export type MtwUtilizationDistanceBasis = 'MEASURED' | 'OPERATOR_ESTIMATE' | 'PROVIDER_ESTIMATE';
94
+ export type MtwUtilizationMetricStatus = 'KNOWN' | 'UNKNOWN';
95
+ export type Query = {
96
+ __typename?: 'Query';
97
+ mtwConsumedFuelPreview: MtwConsumedFuelPreview;
98
+ };
99
+ export type QueryMtwConsumedFuelPreviewArgs = {
100
+ input: MtwConsumedFuelPreviewInput;
101
+ };
102
+ export type MtwConsumedFuelPreviewQueryVariables = Exact<{
103
+ input: Types.MtwConsumedFuelPreviewInput;
104
+ }>;
105
+ export type MtwConsumedFuelPreviewQuery = {
106
+ mtwConsumedFuelPreview: {
107
+ status: Types.MtwUtilizationMetricStatus;
108
+ litresPer100Km: number | null;
109
+ reasons: Array<Types.MtwConsumedFuelReason>;
110
+ qualification: Types.MtwConsumedFuelQualification;
111
+ distance: {
112
+ metres: number | null;
113
+ intervalFrom: string;
114
+ intervalUntil: string;
115
+ basis: Types.MtwUtilizationDistanceBasis;
116
+ coverage: Types.MtwTripUtilizationCoverage;
117
+ sourceReference: string | null;
118
+ sourceRevision: string | null;
119
+ } | null;
120
+ fuel: {
121
+ millilitres: number | null;
122
+ intervalFrom: string;
123
+ intervalUntil: string;
124
+ basis: Types.MtwConsumedFuelBasis;
125
+ coverage: Types.MtwTripUtilizationCoverage;
126
+ sourceReference: string | null;
127
+ sourceRevision: string | null;
128
+ } | null;
129
+ };
130
+ };
131
+ export declare const MtwConsumedFuelPreviewDocument: DocumentNode<MtwConsumedFuelPreviewQuery, MtwConsumedFuelPreviewQueryVariables>;
132
+ export {};
@@ -0,0 +1,90 @@
1
+ import { useMoveTheWheels as e } from "../providers/MoveTheWheelsProvider.js";
2
+ import { executeGraphQL as t } from "../utils/graphqlClient.js";
3
+ import { MtwConsumedFuelPreviewDocument as n } from "../src/generated/consumed-fuel-preview-operations.js";
4
+ import { parseConsumedFuelPreview as r, validateConsumedFuelEnvelope as i } from "../utils/consumedFuelResponse.js";
5
+ import { useCallback as a, useEffect as o, useLayoutEffect as s, useMemo as c, useRef as l, useState as u } from "react";
6
+ import { print as d } from "graphql";
7
+ //#region src/movethewheels/hooks/useConsumedFuelPreview.ts
8
+ var f = d(n);
9
+ function p() {
10
+ let { workspaceId: n, tenantId: d, currentUser: p, apiGatewayUrl: m, authToken: h, workspaceToken: g } = e(), _ = c(() => ({
11
+ workspaceId: n,
12
+ tenantId: d,
13
+ actorId: p?.id,
14
+ apiGatewayUrl: m,
15
+ authToken: h,
16
+ workspaceToken: g
17
+ }), [
18
+ n,
19
+ d,
20
+ p?.id,
21
+ m,
22
+ h,
23
+ g
24
+ ]), v = l(_);
25
+ s(() => {
26
+ v.current = _;
27
+ }, [_]);
28
+ let y = l(0), b = l(null), [x, S] = u(null), C = a(() => {
29
+ y.current++, b.current?.abort(), b.current = null;
30
+ }, []), w = a(() => {
31
+ C(), S(null);
32
+ }, [C]);
33
+ o(() => (w(), C), [
34
+ _,
35
+ C,
36
+ w
37
+ ]);
38
+ let T = a(async (e) => {
39
+ if (v.current !== _ || b.current || !_.workspaceId || !_.actorId) return;
40
+ e = structuredClone(e);
41
+ let n = new AbortController();
42
+ b.current = n;
43
+ let a = ++y.current;
44
+ S({
45
+ context: _,
46
+ generation: a,
47
+ loading: !0,
48
+ result: null,
49
+ input: e,
50
+ failed: !1
51
+ });
52
+ try {
53
+ let o = r(await t({
54
+ ..._,
55
+ signal: n.signal,
56
+ validateResponse: i
57
+ }, f, { input: e }, "SINGLE_ATTEMPT"), e);
58
+ !n.signal.aborted && v.current === _ && y.current === a && S({
59
+ context: _,
60
+ generation: a,
61
+ loading: !1,
62
+ result: o,
63
+ input: e,
64
+ failed: !1
65
+ });
66
+ } catch {
67
+ !n.signal.aborted && v.current === _ && y.current === a && S({
68
+ context: _,
69
+ generation: a,
70
+ loading: !1,
71
+ result: null,
72
+ input: e,
73
+ failed: !0
74
+ });
75
+ } finally {
76
+ b.current === n && (b.current = null);
77
+ }
78
+ }, [_]), E = x?.context === _;
79
+ return {
80
+ run: T,
81
+ invalidate: w,
82
+ ready: !!_.workspaceId && !!_.actorId,
83
+ loading: E ? x.loading : !1,
84
+ result: E ? x.result : null,
85
+ input: E ? x.input : null,
86
+ failed: E ? x.failed : !1
87
+ };
88
+ }
89
+ //#endregion
90
+ export { p as useConsumedFuelPreview };
@@ -0,0 +1,33 @@
1
+ import { MtwConsumedFuelPreviewInput } from '../../generated/consumed-fuel-preview-operations';
2
+ export declare function useConsumedFuelPreview(): {
3
+ run: (input: MtwConsumedFuelPreviewInput) => Promise<void>;
4
+ invalidate: () => void;
5
+ ready: boolean;
6
+ loading: boolean;
7
+ result: {
8
+ status: import('../../generated/consumed-fuel-preview-operations').MtwUtilizationMetricStatus;
9
+ litresPer100Km: number | null;
10
+ reasons: Array<import('../../generated/consumed-fuel-preview-operations').MtwConsumedFuelReason>;
11
+ qualification: import('../../generated/consumed-fuel-preview-operations').MtwConsumedFuelQualification;
12
+ distance: {
13
+ metres: number | null;
14
+ intervalFrom: string;
15
+ intervalUntil: string;
16
+ basis: import('../../generated/consumed-fuel-preview-operations').MtwUtilizationDistanceBasis;
17
+ coverage: import('../../generated/consumed-fuel-preview-operations').MtwTripUtilizationCoverage;
18
+ sourceReference: string | null;
19
+ sourceRevision: string | null;
20
+ } | null;
21
+ fuel: {
22
+ millilitres: number | null;
23
+ intervalFrom: string;
24
+ intervalUntil: string;
25
+ basis: import('../../generated/consumed-fuel-preview-operations').MtwConsumedFuelBasis;
26
+ coverage: import('../../generated/consumed-fuel-preview-operations').MtwTripUtilizationCoverage;
27
+ sourceReference: string | null;
28
+ sourceRevision: string | null;
29
+ } | null;
30
+ } | null;
31
+ input: MtwConsumedFuelPreviewInput | null;
32
+ failed: boolean;
33
+ };
@@ -0,0 +1 @@
1
+ export default function ConsumedFuelPreviewPage(): import('react').JSX.Element;
@@ -0,0 +1,27 @@
1
+ import { MtwConsumedFuelPreviewInput } from '../../generated/consumed-fuel-preview-operations';
2
+ export interface FuelObservationDraft {
3
+ included: boolean;
4
+ amount: string;
5
+ intervalFrom: string;
6
+ intervalUntil: string;
7
+ basis: string;
8
+ coverage: string;
9
+ sourceReference: string;
10
+ sourceRevision: string;
11
+ }
12
+ export interface ConsumedFuelDraft {
13
+ distance: FuelObservationDraft;
14
+ fuel: FuelObservationDraft;
15
+ }
16
+ export declare function newConsumedFuelDraft(): ConsumedFuelDraft;
17
+ export declare class ConsumedFuelInputError extends Error {
18
+ readonly field: string;
19
+ readonly kind: 'QUANTITY' | 'INSTANT' | 'INTERVAL' | 'SOURCE' | 'CHOICE';
20
+ constructor(field: string, kind: 'QUANTITY' | 'INSTANT' | 'INTERVAL' | 'SOURCE' | 'CHOICE');
21
+ }
22
+ /** Parse decimal km/L text directly into whole m/mL; no floating multiplication or rounding. */
23
+ export declare function exactMilliUnits(value: string, field: string): number | null;
24
+ export declare function strictFuelInstant(value: string, field: string): string;
25
+ export declare function consumedFuelInput(draft: ConsumedFuelDraft, now?: number): MtwConsumedFuelPreviewInput;
26
+ /** Display precision is not sensor precision; positive values must never appear as zero. */
27
+ export declare function formatFuelRatio(value: number, locale?: string): string;
@@ -0,0 +1,4 @@
1
+ import { MtwConsumedFuelPreviewInput, MtwConsumedFuelPreviewQuery } from '../../generated/consumed-fuel-preview-operations';
2
+ export type ConsumedFuelPreview = MtwConsumedFuelPreviewQuery['mtwConsumedFuelPreview'];
3
+ export declare function validateConsumedFuelEnvelope(value: unknown): void;
4
+ export declare function parseConsumedFuelPreview(data: unknown, input: MtwConsumedFuelPreviewInput): ConsumedFuelPreview;
@@ -0,0 +1,267 @@
1
+ import { Gauge as e } from "../node_modules/lucide-react/dist/esm/icons/gauge.js";
2
+ import { useMoveTheWheels as t } from "../providers/MoveTheWheelsProvider.js";
3
+ import { useTr as n } from "../shared/hooks/useTr.js";
4
+ import "../hooks/index.js";
5
+ import { Button as r, Input as i, Label as a } from "../components/ui.js";
6
+ import { PageLayout as o } from "../components/PageLayout.js";
7
+ import { ConsumedFuelInputError as s, consumedFuelInput as c, formatFuelRatio as l, newConsumedFuelDraft as u } from "../utils/consumedFuelInput.js";
8
+ import { useConsumedFuelPreview as d } from "../hooks/useConsumedFuelPreview.js";
9
+ import { useEffect as f, useId as p, useRef as m, useState as h } from "react";
10
+ import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
11
+ //#region src/movethewheels/pages/ConsumedFuelPreviewPage.tsx
12
+ var y = {
13
+ DISTANCE_UNKNOWN: "Distance is missing.",
14
+ FUEL_UNKNOWN: "Consumed volume is missing.",
15
+ SOURCE_UNKNOWN: "Source reference or revision is missing.",
16
+ NON_MEASURED_DISTANCE: "Distance is not declared measured.",
17
+ UNSUPPORTED_FUEL_BASIS: "Fuel basis is estimated, refuelled or unknown.",
18
+ INTERVAL_MISMATCH: "The observation intervals differ.",
19
+ PARTIAL_COVERAGE: "Coverage is declared partial.",
20
+ ZERO_DISTANCE: "L/100 km is undefined for zero distance."
21
+ };
22
+ function b({ kind: e, value: t, prefix: r, error: o, onChange: s }) {
23
+ let c = n(), l = [
24
+ ["amount", e === "distance" ? "Distance (km)" : "Reported fuel volume (L)"],
25
+ ["intervalFrom", "Interval start (UTC)"],
26
+ ["intervalUntil", "Interval end (UTC)"],
27
+ ["sourceReference", "Source reference"],
28
+ ["sourceRevision", "Source revision"]
29
+ ], u = e === "distance" ? [
30
+ ["MEASURED", "Measured"],
31
+ ["PROVIDER_ESTIMATE", "Provider estimate"],
32
+ ["OPERATOR_ESTIMATE", "Operator estimate"]
33
+ ] : [
34
+ ["MEASURED_CONSUMPTION", "Measured consumption"],
35
+ ["ESTIMATED_CONSUMPTION", "Estimated consumption"],
36
+ ["PURCHASED_OR_REFUELLED", "Purchased or refuelled"],
37
+ ["UNKNOWN", "Unknown"]
38
+ ];
39
+ return /* @__PURE__ */ v("fieldset", {
40
+ className: "min-w-0 rounded-xl border border-outline p-4 space-y-4",
41
+ children: [
42
+ /* @__PURE__ */ _("legend", {
43
+ className: "px-1 font-semibold",
44
+ children: e === "distance" ? c("movethewheels.fuelPreview.distance", "Distance observation") : c("movethewheels.fuelPreview.fuel", "Fuel observation")
45
+ }),
46
+ /* @__PURE__ */ v(a, {
47
+ htmlFor: `${r}-${e}-included`,
48
+ className: "flex items-center gap-2 min-h-11",
49
+ children: [/* @__PURE__ */ _("input", {
50
+ id: `${r}-${e}-included`,
51
+ type: "checkbox",
52
+ checked: t.included,
53
+ onChange: (e) => s({ included: e.target.checked })
54
+ }), c("movethewheels.fuelPreview.include", "Include this observation")]
55
+ }),
56
+ t.included ? /* @__PURE__ */ v("div", {
57
+ className: "grid gap-3 sm:grid-cols-2",
58
+ children: [l.map(([n, l]) => /* @__PURE__ */ v("div", {
59
+ className: "min-w-0 space-y-1",
60
+ children: [/* @__PURE__ */ _(a, {
61
+ htmlFor: `${r}-${e}-${n}`,
62
+ children: c(`movethewheels.fuelPreview.${e}.${n}`, l)
63
+ }), /* @__PURE__ */ _(i, {
64
+ id: `${r}-${e}-${n}`,
65
+ type: "text",
66
+ inputMode: n === "amount" ? "decimal" : "text",
67
+ autoComplete: "off",
68
+ value: t[n],
69
+ onChange: (e) => s({ [n]: e.target.value }),
70
+ required: n === "intervalFrom" || n === "intervalUntil",
71
+ maxLength: n === "amount" || n.startsWith("interval") ? 32 : 255,
72
+ "aria-invalid": o?.field === `${e}-${n}`,
73
+ "aria-describedby": o?.field === `${e}-${n}` ? `${r}-error` : void 0,
74
+ className: "min-h-11"
75
+ })]
76
+ }, n)), [[
77
+ "basis",
78
+ c("movethewheels.fuelPreview.basis", "Observation basis"),
79
+ u
80
+ ], [
81
+ "coverage",
82
+ c("movethewheels.fuelPreview.coverage", "Declared interval coverage"),
83
+ [["COMPLETE", c("movethewheels.fuelPreview.complete", "Complete")], ["PARTIAL", c("movethewheels.fuelPreview.partial", "Partial")]]
84
+ ]].map(([n, i, l]) => /* @__PURE__ */ v("div", {
85
+ className: "space-y-1",
86
+ children: [/* @__PURE__ */ _(a, {
87
+ htmlFor: `${r}-${e}-${n}`,
88
+ children: i
89
+ }), /* @__PURE__ */ v("select", {
90
+ id: `${r}-${e}-${n}`,
91
+ required: !0,
92
+ value: t[n],
93
+ onChange: (e) => s({ [n]: e.target.value }),
94
+ "aria-invalid": o?.field === `${e}-${n}`,
95
+ "aria-describedby": o?.field === `${e}-${n}` ? `${r}-error` : void 0,
96
+ className: "min-h-11 w-full rounded-md border border-outline bg-surface px-3",
97
+ children: [/* @__PURE__ */ _("option", {
98
+ value: "",
99
+ children: c("movethewheels.fuelPreview.choose", "Choose a declared basis or coverage")
100
+ }), l.map(([e, t]) => /* @__PURE__ */ _("option", {
101
+ value: e,
102
+ children: c(`movethewheels.fuelPreview.choices.${e}`, t)
103
+ }, e))]
104
+ })]
105
+ }, n))]
106
+ }) : /* @__PURE__ */ _("p", {
107
+ className: "text-sm text-text-secondary",
108
+ children: c("movethewheels.fuelPreview.absent", "Not supplied. This observation will remain unknown.")
109
+ })
110
+ ]
111
+ });
112
+ }
113
+ function x({ result: e }) {
114
+ let t = n();
115
+ return /* @__PURE__ */ v("section", {
116
+ "aria-label": t("movethewheels.fuelPreview.result", "Consumption preview result"),
117
+ className: "rounded-xl border border-outline p-4 space-y-3",
118
+ "data-testid": "fuel-preview-result",
119
+ children: [
120
+ /* @__PURE__ */ _("h2", {
121
+ className: "text-xl font-semibold",
122
+ children: e.status === "KNOWN" && e.litresPer100Km !== null ? l(e.litresPer100Km) : t("movethewheels.fuelPreview.unknown", "Unknown — no ratio available")
123
+ }),
124
+ /* @__PURE__ */ _("p", { children: t("movethewheels.fuelPreview.unverified", "Operator reported · unverified. Source labels do not authenticate measurements.") }),
125
+ e.reasons.length > 0 && /* @__PURE__ */ _("ul", {
126
+ className: "list-disc ps-5",
127
+ children: e.reasons.map((e) => /* @__PURE__ */ _("li", { children: t(`movethewheels.fuelPreview.reasons.${e}`, y[e]) }, e))
128
+ }),
129
+ /* @__PURE__ */ _("div", {
130
+ className: "grid gap-3 sm:grid-cols-2",
131
+ children: ["distance", "fuel"].map((n) => {
132
+ let r = e[n];
133
+ return /* @__PURE__ */ v("div", {
134
+ className: "min-w-0 break-words text-sm",
135
+ children: [/* @__PURE__ */ _("h3", {
136
+ className: "font-semibold",
137
+ children: n === "distance" ? t("movethewheels.fuelPreview.distance", "Distance observation") : t("movethewheels.fuelPreview.fuel", "Fuel observation")
138
+ }), r ? /* @__PURE__ */ v(g, { children: [
139
+ /* @__PURE__ */ _("p", { children: n === "distance" ? `${e.distance?.metres ?? t("movethewheels.fuelPreview.missing", "Unknown")} m` : `${e.fuel?.millilitres ?? t("movethewheels.fuelPreview.missing", "Unknown")} mL` }),
140
+ /* @__PURE__ */ v("p", { children: [
141
+ r.intervalFrom,
142
+ " → ",
143
+ r.intervalUntil
144
+ ] }),
145
+ /* @__PURE__ */ v("p", { children: [
146
+ t(`movethewheels.fuelPreview.choices.${r.basis}`, r.basis),
147
+ " ",
148
+ "·",
149
+ " ",
150
+ t(`movethewheels.fuelPreview.choices.${r.coverage}`, r.coverage)
151
+ ] }),
152
+ /* @__PURE__ */ v("p", { children: [
153
+ r.sourceReference ?? t("movethewheels.fuelPreview.noSource", "No source reference"),
154
+ " ",
155
+ "/",
156
+ " ",
157
+ r.sourceRevision ?? t("movethewheels.fuelPreview.noRevision", "No source revision")
158
+ ] })
159
+ ] }) : /* @__PURE__ */ _("p", { children: t("movethewheels.fuelPreview.notSupplied", "Not supplied") })]
160
+ }, n);
161
+ })
162
+ })
163
+ ]
164
+ });
165
+ }
166
+ function S() {
167
+ let { workspaceId: e, tenantId: n, currentUser: r, apiGatewayUrl: i } = t();
168
+ return /* @__PURE__ */ _(w, {}, JSON.stringify([
169
+ e,
170
+ n,
171
+ r?.id,
172
+ i
173
+ ]));
174
+ }
175
+ function C(e, t) {
176
+ return e?.kind === "QUANTITY" ? t("movethewheels.fuelPreview.quantityError", "Use a nonnegative decimal with at most three fractional digits, up to 2147483.647 km or L. No separators, signs or exponents.") : e?.kind === "INSTANT" ? t("movethewheels.fuelPreview.instantError", "Use a real UTC timestamp such as YYYY-MM-DDTHH:mm:ssZ, with at most three fractional digits.") : e?.kind === "INTERVAL" ? t("movethewheels.fuelPreview.intervalError", "Start must precede end; end must not be in the future.") : e?.kind === "SOURCE" ? t("movethewheels.fuelPreview.sourceError", "Leave a source label blank, or shorten it and include at least one non-space character.") : t("movethewheels.fuelPreview.choiceError", "Choose the declared basis and coverage.");
177
+ }
178
+ function w() {
179
+ let t = n(), i = p(), [a, l] = h(u), [g, y] = h(null), [S, w] = h(!1), [T, E] = h(!1), D = d(), O = m(null);
180
+ f(() => {
181
+ if (g) {
182
+ let e = document.getElementById(`${i}-${g.field}`);
183
+ e instanceof HTMLElement ? e.focus() : O.current?.focus();
184
+ } else (D.failed || T) && O.current?.focus();
185
+ }, [
186
+ g,
187
+ i,
188
+ D.failed,
189
+ T
190
+ ]);
191
+ let k = (e, t) => {
192
+ D.invalidate(), w(!0), y(null), E(!1), l((n) => ({
193
+ ...n,
194
+ [e]: {
195
+ ...n[e],
196
+ ...t
197
+ }
198
+ }));
199
+ }, A = C(g, t);
200
+ return /* @__PURE__ */ v(o, {
201
+ title: t("movethewheels.fuelPreview.title", "Consumed fuel preview"),
202
+ subtitle: t("movethewheels.fuelPreview.subtitle", "Compare sourced consumption and distance for the same interval."),
203
+ icon: /* @__PURE__ */ _(e, { "aria-hidden": "true" }),
204
+ children: [
205
+ /* @__PURE__ */ _("p", {
206
+ className: "mb-4 text-sm",
207
+ children: t("movethewheels.fuelPreview.limits", "Reported interval arithmetic only. No measurement verification, savings, emissions, backhaul suitability or safety approval. Nothing is saved.")
208
+ }),
209
+ /* @__PURE__ */ v("form", {
210
+ noValidate: !0,
211
+ onSubmit: (e) => {
212
+ if (e.preventDefault(), !(D.loading || !D.ready)) {
213
+ E(!1);
214
+ try {
215
+ let e = c(a);
216
+ y(null), w(!1), D.run(e);
217
+ } catch (e) {
218
+ e instanceof s ? y(e) : (D.invalidate(), y(null), E(!0));
219
+ }
220
+ }
221
+ },
222
+ className: "space-y-4",
223
+ children: [
224
+ /* @__PURE__ */ _("p", {
225
+ className: "text-sm text-text-secondary",
226
+ children: t("movethewheels.fuelPreview.precision", "Blank quantities stay unknown; explicit zero stays zero. Enter km and litres with at most three decimal places. COMPLETE is your declaration for that interval, not a trip certificate.")
227
+ }),
228
+ /* @__PURE__ */ _("div", {
229
+ className: "grid gap-4 lg:grid-cols-2",
230
+ children: ["distance", "fuel"].map((e) => /* @__PURE__ */ _(b, {
231
+ kind: e,
232
+ value: a[e],
233
+ prefix: i,
234
+ error: g,
235
+ onChange: (t) => k(e, t)
236
+ }, e))
237
+ }),
238
+ (g || D.failed || T) && /* @__PURE__ */ _("div", {
239
+ id: `${i}-error`,
240
+ ref: O,
241
+ tabIndex: -1,
242
+ role: "alert",
243
+ className: "rounded-md border border-outline p-3",
244
+ children: g ? A : T ? t("movethewheels.fuelPreview.inputUnavailable", "The observations could not be prepared. Review the form before trying this read again.") : t("movethewheels.fuelPreview.unavailable", "Preview unavailable. No result is accepted; check your connection and access before trying this read again.")
245
+ }),
246
+ !D.ready && /* @__PURE__ */ _("p", {
247
+ role: "status",
248
+ children: t("movethewheels.fuelPreview.contextRequired", "Choose an active workspace and sign in before previewing.")
249
+ }),
250
+ /* @__PURE__ */ _(r, {
251
+ type: "submit",
252
+ disabled: D.loading || !D.ready,
253
+ className: "min-h-11",
254
+ children: D.loading ? t("movethewheels.fuelPreview.loading", "Previewing…") : t("movethewheels.fuelPreview.submit", "Preview consumption")
255
+ })
256
+ ]
257
+ }),
258
+ /* @__PURE__ */ _("div", {
259
+ "aria-live": "polite",
260
+ className: "mt-5",
261
+ children: D.result ? /* @__PURE__ */ _(x, { result: D.result }) : S ? /* @__PURE__ */ _("p", { children: t("movethewheels.fuelPreview.stale", "Inputs changed. Preview again for these observations; the previous result is hidden.") }) : null
262
+ })
263
+ ]
264
+ });
265
+ }
266
+ //#endregion
267
+ export { S as default };