@burdenoff/microfe-movethewheels 2026.830.5 → 2026.918.1

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.
@@ -0,0 +1,135 @@
1
+ import { Sparkles as e } from "../node_modules/lucide-react/dist/esm/icons/sparkles.js";
2
+ import { Badge as t, Button as n, Card as r, CardContent as i, CardHeader as a, CardTitle as o, Skeleton as s } from "./ui.js";
3
+ import { FormError as c } from "./FormError.js";
4
+ import { useState as l } from "react";
5
+ import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
6
+ //#region src/movethewheels/components/AssignmentSuggestions.tsx
7
+ function p({ id: e, label: r, reasons: i, isBest: a, isAssigned: o, isPending: s, disabled: c, onAssign: l, t: u }) {
8
+ return /* @__PURE__ */ f("li", {
9
+ className: "flex items-start justify-between gap-3 py-3",
10
+ "data-suggestion-id": e,
11
+ children: [/* @__PURE__ */ f("div", {
12
+ className: "flex-1 min-w-0 space-y-1",
13
+ children: [/* @__PURE__ */ f("div", {
14
+ className: "flex flex-wrap items-center gap-x-2 gap-y-1",
15
+ children: [/* @__PURE__ */ d("span", {
16
+ className: "text-sm font-medium break-words",
17
+ children: r
18
+ }), a && /* @__PURE__ */ d(t, {
19
+ variant: "secondary",
20
+ children: u("movethewheels.routes.suggest.best", "Best match")
21
+ })]
22
+ }), /* @__PURE__ */ d("ul", {
23
+ className: "space-y-0.5",
24
+ children: i.map((e) => /* @__PURE__ */ d("li", {
25
+ className: "text-xs text-text-secondary",
26
+ children: e
27
+ }, e))
28
+ })]
29
+ }), /* @__PURE__ */ d(n, {
30
+ size: "sm",
31
+ className: "shrink-0",
32
+ variant: o ? "ghost" : "outline",
33
+ disabled: c || o,
34
+ onClick: l,
35
+ children: o ? u("movethewheels.routes.suggest.assigned", "Assigned") : s ? u("movethewheels.routes.suggest.assigning", "Assigning…") : u("movethewheels.routes.suggest.assign", "Assign")
36
+ })]
37
+ });
38
+ }
39
+ function m({ suggestions: t, isLoading: m, error: h, onRetry: g, assignedVehicleId: _, assignedDriverId: v, onAssign: y, t: b }) {
40
+ let [x, S] = l(null), [C, w] = l(null), T = b("movethewheels.routes.suggest.title", "Suggested assignment"), E = async (e, t) => {
41
+ S(e), w(null);
42
+ try {
43
+ await y(t);
44
+ } catch (e) {
45
+ w(e instanceof Error ? e.message : "Could not assign.");
46
+ } finally {
47
+ S(null);
48
+ }
49
+ };
50
+ if (h && !m) return /* @__PURE__ */ d(r, {
51
+ className: "border-status-error-border",
52
+ children: /* @__PURE__ */ f(i, {
53
+ className: "p-6 text-center space-y-3",
54
+ children: [
55
+ /* @__PURE__ */ d("p", {
56
+ className: "text-status-error-text font-medium",
57
+ children: b("movethewheels.routes.suggest.error", "Couldn't load suggestions")
58
+ }),
59
+ /* @__PURE__ */ d("p", {
60
+ className: "text-sm text-text-secondary",
61
+ children: h.message
62
+ }),
63
+ /* @__PURE__ */ d(n, {
64
+ variant: "outline",
65
+ onClick: g,
66
+ children: b("movethewheels.fleet.error.retry", "Retry")
67
+ })
68
+ ]
69
+ })
70
+ });
71
+ if (m) return /* @__PURE__ */ f(r, { children: [/* @__PURE__ */ d(a, { children: /* @__PURE__ */ d(o, { children: T }) }), /* @__PURE__ */ f(i, {
72
+ className: "space-y-3",
73
+ children: [/* @__PURE__ */ d(s, { className: "h-14 w-full" }), /* @__PURE__ */ d(s, { className: "h-14 w-full" })]
74
+ })] });
75
+ let { vehicles: D, drivers: O } = t, k = D.length === 0 && O.length === 0;
76
+ return /* @__PURE__ */ f(r, { children: [/* @__PURE__ */ d(a, { children: /* @__PURE__ */ f(o, {
77
+ className: "flex items-center gap-2",
78
+ children: [/* @__PURE__ */ d(e, {
79
+ size: 18,
80
+ "aria-hidden": "true"
81
+ }), T]
82
+ }) }), /* @__PURE__ */ f(i, {
83
+ className: "space-y-4",
84
+ children: [/* @__PURE__ */ d(c, { message: C }), k ? /* @__PURE__ */ d("p", {
85
+ className: "text-sm text-text-secondary",
86
+ children: b("movethewheels.routes.suggest.none", "No vehicle or driver is free right now. Vehicles must be in service and drivers must hold a valid licence and not be on another live route.")
87
+ }) : /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ f("section", {
88
+ "aria-label": b("movethewheels.routes.suggest.vehicles", "Vehicles"),
89
+ children: [/* @__PURE__ */ d("h4", {
90
+ className: "text-xs font-semibold uppercase tracking-wide text-text-secondary",
91
+ children: b("movethewheels.routes.suggest.vehicles", "Vehicles")
92
+ }), D.length === 0 ? /* @__PURE__ */ d("p", {
93
+ className: "py-3 text-sm text-text-secondary",
94
+ children: b("movethewheels.routes.suggest.noVehicles", "No vehicle is free right now.")
95
+ }) : /* @__PURE__ */ d("ul", {
96
+ className: "divide-y",
97
+ children: D.map((e, t) => /* @__PURE__ */ d(p, {
98
+ id: e.vehicle.id,
99
+ label: [e.vehicle.vehicleNumber, [e.vehicle.make, e.vehicle.model].filter(Boolean).join(" ")].filter(Boolean).join(" — "),
100
+ reasons: e.reasons,
101
+ isBest: t === 0,
102
+ isAssigned: _ === e.vehicle.id,
103
+ isPending: x === e.vehicle.id,
104
+ disabled: x !== null,
105
+ onAssign: () => E(e.vehicle.id, { vehicleId: e.vehicle.id }),
106
+ t: b
107
+ }, e.vehicle.id))
108
+ })]
109
+ }), /* @__PURE__ */ f("section", {
110
+ "aria-label": b("movethewheels.routes.suggest.drivers", "Drivers"),
111
+ children: [/* @__PURE__ */ d("h4", {
112
+ className: "text-xs font-semibold uppercase tracking-wide text-text-secondary",
113
+ children: b("movethewheels.routes.suggest.drivers", "Drivers")
114
+ }), O.length === 0 ? /* @__PURE__ */ d("p", {
115
+ className: "py-3 text-sm text-text-secondary",
116
+ children: b("movethewheels.routes.suggest.noDrivers", "No driver is free right now.")
117
+ }) : /* @__PURE__ */ d("ul", {
118
+ className: "divide-y",
119
+ children: O.map((e, t) => /* @__PURE__ */ d(p, {
120
+ id: e.driver.id,
121
+ label: `${e.driver.firstName} ${e.driver.lastName}`.trim(),
122
+ reasons: e.reasons,
123
+ isBest: t === 0,
124
+ isAssigned: v === e.driver.id,
125
+ isPending: x === e.driver.id,
126
+ disabled: x !== null,
127
+ onAssign: () => E(e.driver.id, { driverId: e.driver.id }),
128
+ t: b
129
+ }, e.driver.id))
130
+ })]
131
+ })] })]
132
+ })] });
133
+ }
134
+ //#endregion
135
+ export { m as AssignmentSuggestions };
@@ -0,0 +1,114 @@
1
+ import { ChevronRight as e } from "../node_modules/lucide-react/dist/esm/icons/chevron-right.js";
2
+ import { CircleCheckBig as t } from "../node_modules/lucide-react/dist/esm/icons/circle-check-big.js";
3
+ import { TriangleAlert as n } from "../node_modules/lucide-react/dist/esm/icons/triangle-alert.js";
4
+ import { Button as r, Card as i, CardContent as a, CardHeader as o, CardTitle as s, Skeleton as c } from "./ui.js";
5
+ import { StatusBadge as l } from "./StatusBadge.js";
6
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
7
+ //#region src/movethewheels/components/FleetAlertsPanel.tsx
8
+ var f = {
9
+ CRITICAL: {
10
+ bg: "bg-status-error-bg-subtle",
11
+ text: "text-status-error-text"
12
+ },
13
+ WARNING: {
14
+ bg: "bg-status-warning-bg-subtle",
15
+ text: "text-status-warning-text"
16
+ }
17
+ }, p = (e, t) => `${e} ${t}${e === 1 ? "" : "s"}`;
18
+ function m(e) {
19
+ let t = [], { daysUntilDue: n, kmUntilDue: r } = e;
20
+ if (n !== null && (n < 0 ? t.push(`${p(-n, "day")} overdue`) : n === 0 ? t.push("due today") : t.push(`${p(n, "day")} left`)), r !== null) {
21
+ let e = Math.round(Math.abs(r));
22
+ t.push(r < 0 ? `${e} km overdue` : `${e} km left`);
23
+ }
24
+ return t.join(" · ");
25
+ }
26
+ function h({ alerts: p, isLoading: h, error: g, onRetry: _, onOpen: v, t: y }) {
27
+ let b = y("movethewheels.fleet.alerts.title", "Needs attention");
28
+ return g && !h ? /* @__PURE__ */ u(i, {
29
+ className: "border-status-error-border",
30
+ children: /* @__PURE__ */ d(a, {
31
+ className: "p-6 text-center space-y-3",
32
+ children: [
33
+ /* @__PURE__ */ u("p", {
34
+ className: "text-status-error-text font-medium",
35
+ children: y("movethewheels.fleet.alerts.error", "Couldn't load fleet alerts")
36
+ }),
37
+ /* @__PURE__ */ u("p", {
38
+ className: "text-sm text-text-secondary",
39
+ children: g.message
40
+ }),
41
+ /* @__PURE__ */ u(r, {
42
+ variant: "outline",
43
+ onClick: _,
44
+ children: y("movethewheels.fleet.error.retry", "Retry")
45
+ })
46
+ ]
47
+ })
48
+ }) : h ? /* @__PURE__ */ d(i, { children: [/* @__PURE__ */ u(o, { children: /* @__PURE__ */ u(s, { children: b }) }), /* @__PURE__ */ d(a, {
49
+ className: "space-y-3",
50
+ children: [/* @__PURE__ */ u(c, { className: "h-12 w-full" }), /* @__PURE__ */ u(c, { className: "h-12 w-full" })]
51
+ })] }) : p.length === 0 ? /* @__PURE__ */ d(i, { children: [/* @__PURE__ */ u(o, { children: /* @__PURE__ */ d(s, {
52
+ className: "flex items-center gap-2",
53
+ children: [/* @__PURE__ */ u(t, {
54
+ size: 18,
55
+ "aria-hidden": "true",
56
+ className: "text-status-success-text"
57
+ }), b]
58
+ }) }), /* @__PURE__ */ u(a, { children: /* @__PURE__ */ u("p", {
59
+ className: "text-sm text-text-secondary",
60
+ children: y("movethewheels.fleet.alerts.allClear", "All clear — every licence is valid and no vehicle is due for service.")
61
+ }) })] }) : /* @__PURE__ */ d(i, { children: [/* @__PURE__ */ u(o, { children: /* @__PURE__ */ d(s, {
62
+ className: "flex items-center gap-2",
63
+ children: [
64
+ /* @__PURE__ */ u(n, {
65
+ size: 18,
66
+ "aria-hidden": "true",
67
+ className: "text-status-warning-text"
68
+ }),
69
+ b,
70
+ " (",
71
+ p.length,
72
+ ")"
73
+ ]
74
+ }) }), /* @__PURE__ */ u(a, {
75
+ className: "p-0",
76
+ children: /* @__PURE__ */ u("ul", {
77
+ className: "divide-y",
78
+ "aria-label": b,
79
+ children: p.map((t) => {
80
+ let n = m(t);
81
+ return /* @__PURE__ */ u("li", { children: /* @__PURE__ */ d("button", {
82
+ type: "button",
83
+ onClick: () => v(t),
84
+ className: "flex w-full items-center gap-3 px-6 py-3 text-left hover:bg-bg-sunken/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg",
85
+ children: [
86
+ /* @__PURE__ */ u(l, {
87
+ status: t.severity === "CRITICAL" ? "Critical" : "Warning",
88
+ customColors: f[t.severity],
89
+ size: "sm",
90
+ showDot: !1
91
+ }),
92
+ /* @__PURE__ */ d("span", {
93
+ className: "flex-1 min-w-0",
94
+ children: [/* @__PURE__ */ u("span", {
95
+ className: "block text-sm font-medium truncate",
96
+ children: t.message
97
+ }), n && /* @__PURE__ */ u("span", {
98
+ className: "block text-xs text-text-secondary",
99
+ children: n
100
+ })]
101
+ }),
102
+ /* @__PURE__ */ u(e, {
103
+ size: 16,
104
+ "aria-hidden": "true",
105
+ className: "shrink-0"
106
+ })
107
+ ]
108
+ }) }, t.id);
109
+ })
110
+ })
111
+ })] });
112
+ }
113
+ //#endregion
114
+ export { h as FleetAlertsPanel };
@@ -0,0 +1,176 @@
1
+ import { Wrench as e } from "../node_modules/lucide-react/dist/esm/icons/wrench.js";
2
+ import { Button as t, Card as n, CardContent as r, CardHeader as i, CardTitle as a, Input as o, Label as s } from "./ui.js";
3
+ import { FormError as c } from "./FormError.js";
4
+ import { useState as l } from "react";
5
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
6
+ //#region src/movethewheels/components/VehicleServiceCard.tsx
7
+ var f = 90, p = 1e4, m = (e) => e ? e.toLocaleDateString(void 0, {
8
+ year: "numeric",
9
+ month: "short",
10
+ day: "numeric"
11
+ }) : null, h = (e) => e.toISOString().slice(0, 10), g = (e) => {
12
+ if (e.trim() === "") return;
13
+ let t = Number(e);
14
+ return Number.isFinite(t) && t >= 0 ? t : NaN;
15
+ };
16
+ function _({ vehicle: _, onRecord: v, t: y }) {
17
+ let [b, x] = l(!1), [S, C] = l(!1), [w, T] = l(null), [E, D] = l({
18
+ serviceDate: h(/* @__PURE__ */ new Date()),
19
+ mileage: _.mileage === void 0 ? "" : String(_.mileage),
20
+ intervalDays: String(f),
21
+ intervalKm: String(p)
22
+ }), O = m(_.lastMaintenance), k = m(_.nextMaintenance), A = _.nextMaintenanceMileage, j = k !== null || A !== void 0;
23
+ return /* @__PURE__ */ d(n, { children: [/* @__PURE__ */ u(i, { children: /* @__PURE__ */ d(a, {
24
+ className: "flex items-center gap-2",
25
+ children: [/* @__PURE__ */ u(e, {
26
+ size: 18,
27
+ "aria-hidden": "true"
28
+ }), y("movethewheels.vehicles.service.title", "Service schedule")]
29
+ }) }), /* @__PURE__ */ d(r, {
30
+ className: "space-y-4",
31
+ children: [/* @__PURE__ */ d("dl", {
32
+ className: "grid grid-cols-2 gap-4 text-sm",
33
+ children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("dt", {
34
+ className: "text-text-secondary",
35
+ children: y("movethewheels.vehicles.service.last", "Last service")
36
+ }), /* @__PURE__ */ u("dd", {
37
+ className: "font-medium",
38
+ children: O ?? y("movethewheels.vehicles.service.none", "Not recorded")
39
+ })] }), /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("dt", {
40
+ className: "text-text-secondary",
41
+ children: y("movethewheels.vehicles.service.next", "Next service due")
42
+ }), /* @__PURE__ */ u("dd", {
43
+ className: "font-medium",
44
+ children: j ? [k, A === void 0 ? null : `${A.toLocaleString()} km`].filter(Boolean).join(" / ") : y("movethewheels.vehicles.service.noSchedule", "No schedule set")
45
+ })] })]
46
+ }), b ? /* @__PURE__ */ d("form", {
47
+ onSubmit: async (e) => {
48
+ e.preventDefault();
49
+ let t = g(E.mileage), n = g(E.intervalDays), r = g(E.intervalKm);
50
+ if ([
51
+ t,
52
+ n,
53
+ r
54
+ ].some((e) => e !== void 0 && Number.isNaN(e))) {
55
+ T(y("movethewheels.vehicles.service.invalid", "Enter valid, non-negative numbers."));
56
+ return;
57
+ }
58
+ if (r !== void 0 && t === void 0 && _.mileage === void 0) {
59
+ T(y("movethewheels.vehicles.service.needsMileage", "Enter the odometer reading to set a service-by-distance interval."));
60
+ return;
61
+ }
62
+ C(!0), T(null);
63
+ try {
64
+ await v({
65
+ serviceDate: `${E.serviceDate}T12:00:00.000Z`,
66
+ ...t === void 0 ? {} : { mileage: t },
67
+ ...n === void 0 ? {} : { intervalDays: Math.round(n) },
68
+ ...r === void 0 ? {} : { intervalKm: r }
69
+ }), x(!1);
70
+ } catch (e) {
71
+ T(e instanceof Error ? e.message : "Could not record the service.");
72
+ } finally {
73
+ C(!1);
74
+ }
75
+ },
76
+ className: "space-y-3",
77
+ children: [
78
+ /* @__PURE__ */ u(c, { message: w }),
79
+ /* @__PURE__ */ d("div", {
80
+ className: "grid grid-cols-2 gap-3",
81
+ children: [
82
+ /* @__PURE__ */ d("div", {
83
+ className: "space-y-1",
84
+ children: [/* @__PURE__ */ u(s, {
85
+ htmlFor: "service-date",
86
+ children: y("movethewheels.vehicles.service.date", "Service date")
87
+ }), /* @__PURE__ */ u(o, {
88
+ id: "service-date",
89
+ type: "date",
90
+ required: !0,
91
+ value: E.serviceDate,
92
+ onChange: (e) => D((t) => ({
93
+ ...t,
94
+ serviceDate: e.target.value
95
+ }))
96
+ })]
97
+ }),
98
+ /* @__PURE__ */ d("div", {
99
+ className: "space-y-1",
100
+ children: [/* @__PURE__ */ u(s, {
101
+ htmlFor: "service-mileage",
102
+ children: y("movethewheels.vehicles.service.mileage", "Odometer (km)")
103
+ }), /* @__PURE__ */ u(o, {
104
+ id: "service-mileage",
105
+ type: "number",
106
+ min: 0,
107
+ value: E.mileage,
108
+ onChange: (e) => D((t) => ({
109
+ ...t,
110
+ mileage: e.target.value
111
+ }))
112
+ })]
113
+ }),
114
+ /* @__PURE__ */ d("div", {
115
+ className: "space-y-1",
116
+ children: [/* @__PURE__ */ u(s, {
117
+ htmlFor: "service-interval-days",
118
+ children: y("movethewheels.vehicles.service.intervalDays", "Next service in (days)")
119
+ }), /* @__PURE__ */ u(o, {
120
+ id: "service-interval-days",
121
+ type: "number",
122
+ min: 0,
123
+ value: E.intervalDays,
124
+ onChange: (e) => D((t) => ({
125
+ ...t,
126
+ intervalDays: e.target.value
127
+ }))
128
+ })]
129
+ }),
130
+ /* @__PURE__ */ d("div", {
131
+ className: "space-y-1",
132
+ children: [/* @__PURE__ */ u(s, {
133
+ htmlFor: "service-interval-km",
134
+ children: y("movethewheels.vehicles.service.intervalKm", "Next service in (km)")
135
+ }), /* @__PURE__ */ u(o, {
136
+ id: "service-interval-km",
137
+ type: "number",
138
+ min: 0,
139
+ value: E.intervalKm,
140
+ onChange: (e) => D((t) => ({
141
+ ...t,
142
+ intervalKm: e.target.value
143
+ }))
144
+ })]
145
+ })
146
+ ]
147
+ }),
148
+ /* @__PURE__ */ d("div", {
149
+ className: "flex gap-2",
150
+ children: [/* @__PURE__ */ u(t, {
151
+ type: "submit",
152
+ size: "sm",
153
+ disabled: S,
154
+ children: S ? y("movethewheels.vehicles.service.saving", "Saving…") : y("movethewheels.vehicles.service.save", "Save service")
155
+ }), /* @__PURE__ */ u(t, {
156
+ type: "button",
157
+ variant: "ghost",
158
+ size: "sm",
159
+ disabled: S,
160
+ onClick: () => {
161
+ x(!1), T(null);
162
+ },
163
+ children: y("movethewheels.vehicles.service.cancel", "Cancel")
164
+ })]
165
+ })
166
+ ]
167
+ }) : /* @__PURE__ */ u(t, {
168
+ variant: "outline",
169
+ size: "sm",
170
+ onClick: () => x(!0),
171
+ children: y("movethewheels.vehicles.service.record", "Record service")
172
+ })]
173
+ })] });
174
+ }
175
+ //#endregion
176
+ export { _ as VehicleServiceCard };
@@ -2,7 +2,7 @@ import { useMoveTheWheels as e } from "../providers/MoveTheWheelsProvider.js";
2
2
  import { executeGraphQL as t } from "../utils/graphqlClient.js";
3
3
  import { useCallback as n, useEffect as r, useMemo as i, useState as a } from "react";
4
4
  //#region src/movethewheels/hooks/useFleet.ts
5
- var o = "\n query ListVehicles($filter: VehicleFilterInput, $pagination: PaginationInput) {\n vehicles(filter: $filter, pagination: $pagination) {\n id\n vehicleNumber\n type\n make\n model\n licensePlate\n ownershipType\n year\n capacity\n status\n currentLocation\n fuelLevel\n mileage\n createdAt\n updatedAt\n }\n }\n", s = "\n query ListDrivers($filter: DriverFilterInput, $pagination: PaginationInput) {\n drivers(filter: $filter, pagination: $pagination) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", c = "\n query GetVehicle($id: ID!) {\n vehicle(id: $id) {\n id\n vehicleNumber\n type\n make\n model\n licensePlate\n ownershipType\n year\n capacity\n status\n currentLocation\n fuelLevel\n mileage\n createdAt\n updatedAt\n }\n }\n", l = "\n query GetDriver($id: ID!) {\n driver(id: $id) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", u = "\n mutation UpdateVehicleStatus($id: ID!, $status: VehicleStatus!) {\n updateVehicleStatus(id: $id, status: $status) {\n id\n vehicleNumber\n type\n make\n model\n licensePlate\n ownershipType\n year\n capacity\n status\n currentLocation\n fuelLevel\n mileage\n createdAt\n updatedAt\n }\n }\n", d = "\n mutation AssignDriverVehicle($id: ID!, $vehicleId: ID) {\n assignDriverVehicle(id: $id, vehicleId: $vehicleId) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", f = "\n mutation UpdateDriverStatus($id: ID!, $status: DriverStatus!) {\n updateDriverStatus(id: $id, status: $status) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", p = "\n mutation CreateVehicle($input: CreateVehicleInput!) {\n createVehicle(input: $input) {\n id\n vehicleNumber\n type\n make\n model\n licensePlate\n ownershipType\n year\n capacity\n status\n currentLocation\n fuelLevel\n mileage\n createdAt\n updatedAt\n }\n }\n", m = "\n mutation CreateDriver($input: CreateDriverInput!) {\n createDriver(input: $input) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", h = "\n mutation UpdateVehicle($id: ID!, $input: UpdateVehicleInput!) {\n updateVehicle(id: $id, input: $input) {\n id\n vehicleNumber\n type\n make\n model\n licensePlate\n ownershipType\n year\n capacity\n status\n currentLocation\n fuelLevel\n mileage\n createdAt\n updatedAt\n }\n }\n", g = "\n mutation UpdateDriver($id: ID!, $input: UpdateDriverInput!) {\n updateDriver(id: $id, input: $input) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", _ = "\n query GetDriverStatusCounts {\n onBreak: driversCount(filter: { status: ON_BREAK })\n offDuty: driversCount(filter: { status: OFF_DUTY })\n inactive: driversCount(filter: { status: INACTIVE })\n }\n", v = "\n query GetFleetStats {\n fleetStats {\n totalVehicles\n activeVehicles\n inTransitVehicles\n maintenanceVehicles\n totalDrivers\n availableDrivers\n onDeliveryDrivers\n avgDriverRating\n }\n }\n";
5
+ var o = "\n query ListVehicles($filter: VehicleFilterInput, $pagination: PaginationInput) {\n vehicles(filter: $filter, pagination: $pagination) {\n id\n vehicleNumber\n type\n make\n model\n licensePlate\n ownershipType\n year\n capacity\n status\n currentLocation\n fuelLevel\n mileage\n lastServiceDate\n nextServiceDueDate\n nextServiceDueMileage\n createdAt\n updatedAt\n }\n }\n", s = "\n query ListDrivers($filter: DriverFilterInput, $pagination: PaginationInput) {\n drivers(filter: $filter, pagination: $pagination) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", c = "\n query GetVehicle($id: ID!) {\n vehicle(id: $id) {\n id\n vehicleNumber\n type\n make\n model\n licensePlate\n ownershipType\n year\n capacity\n status\n currentLocation\n fuelLevel\n mileage\n lastServiceDate\n nextServiceDueDate\n nextServiceDueMileage\n createdAt\n updatedAt\n }\n }\n", l = "\n query GetDriver($id: ID!) {\n driver(id: $id) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", u = "\n mutation UpdateVehicleStatus($id: ID!, $status: VehicleStatus!) {\n updateVehicleStatus(id: $id, status: $status) {\n id\n vehicleNumber\n type\n make\n model\n licensePlate\n ownershipType\n year\n capacity\n status\n currentLocation\n fuelLevel\n mileage\n createdAt\n updatedAt\n }\n }\n", d = "\n mutation AssignDriverVehicle($id: ID!, $vehicleId: ID) {\n assignDriverVehicle(id: $id, vehicleId: $vehicleId) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", f = "\n mutation UpdateDriverStatus($id: ID!, $status: DriverStatus!) {\n updateDriverStatus(id: $id, status: $status) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", p = "\n mutation CreateVehicle($input: CreateVehicleInput!) {\n createVehicle(input: $input) {\n id\n vehicleNumber\n type\n make\n model\n licensePlate\n ownershipType\n year\n capacity\n status\n currentLocation\n fuelLevel\n mileage\n createdAt\n updatedAt\n }\n }\n", m = "\n mutation CreateDriver($input: CreateDriverInput!) {\n createDriver(input: $input) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", h = "\n mutation UpdateVehicle($id: ID!, $input: UpdateVehicleInput!) {\n updateVehicle(id: $id, input: $input) {\n id\n vehicleNumber\n type\n make\n model\n licensePlate\n ownershipType\n year\n capacity\n status\n currentLocation\n fuelLevel\n mileage\n createdAt\n updatedAt\n }\n }\n", g = "\n mutation UpdateDriver($id: ID!, $input: UpdateDriverInput!) {\n updateDriver(id: $id, input: $input) {\n id\n userId\n firstName\n lastName\n email\n phone\n employeeId\n licenseNumber\n licenseClass\n licenseExpiry\n status\n currentLocation\n rating\n vehicleId\n createdAt\n }\n }\n", _ = "\n query GetDriverStatusCounts {\n onBreak: driversCount(filter: { status: ON_BREAK })\n offDuty: driversCount(filter: { status: OFF_DUTY })\n inactive: driversCount(filter: { status: INACTIVE })\n }\n", v = "\n query GetFleetStats {\n fleetStats {\n totalVehicles\n activeVehicles\n inTransitVehicles\n maintenanceVehicles\n totalDrivers\n availableDrivers\n onDeliveryDrivers\n avgDriverRating\n }\n }\n";
6
6
  function y(e) {
7
7
  switch (e) {
8
8
  case "ACTIVE": return "available";
@@ -58,8 +58,9 @@ function C(e) {
58
58
  address: e.currentLocation.address ?? void 0,
59
59
  timestamp: /* @__PURE__ */ new Date()
60
60
  } : void 0,
61
- lastMaintenance: void 0,
62
- nextMaintenance: void 0,
61
+ lastMaintenance: e.lastServiceDate ? new Date(e.lastServiceDate) : void 0,
62
+ nextMaintenance: e.nextServiceDueDate ? new Date(e.nextServiceDueDate) : void 0,
63
+ nextMaintenanceMileage: e.nextServiceDueMileage ?? void 0,
63
64
  fuelLevel: e.fuelLevel ?? void 0,
64
65
  mileage: e.mileage ?? void 0,
65
66
  documents: [],
@@ -0,0 +1,91 @@
1
+ import { useMoveTheWheels as e } from "../providers/MoveTheWheelsProvider.js";
2
+ import { executeGraphQL as t } from "../utils/graphqlClient.js";
3
+ import { useCallback as n, useEffect as r, useMemo as i, useState as a } from "react";
4
+ //#region src/movethewheels/hooks/useFleetIntelligence.ts
5
+ var o = "\n query FleetAlerts {\n fleetAlerts {\n id\n type\n severity\n subjectType\n subjectId\n subjectName\n message\n dueDate\n daysUntilDue\n kmUntilDue\n }\n }\n", s = "\n query AssignmentSuggestions($routeId: ID!, $limit: Int) {\n assignmentSuggestions(routeId: $routeId, limit: $limit) {\n routeId\n vehicles {\n score\n reasons\n vehicle {\n id\n vehicleNumber\n make\n model\n }\n }\n drivers {\n score\n reasons\n driver {\n id\n firstName\n lastName\n rating\n }\n }\n }\n }\n", c = "\n mutation RecordVehicleService($id: ID!, $input: RecordVehicleServiceInput!) {\n recordVehicleService(id: $id, input: $input) {\n id\n status\n mileage\n lastServiceDate\n nextServiceDueDate\n nextServiceDueMileage\n }\n }\n";
6
+ function l() {
7
+ let { apiGatewayUrl: t, authToken: n, workspaceToken: r, workspaceId: a, tenantId: o, currentUser: s } = e();
8
+ return i(() => ({
9
+ apiGatewayUrl: t,
10
+ authToken: n,
11
+ workspaceToken: r,
12
+ workspaceId: a,
13
+ tenantId: o,
14
+ actorId: s?.id
15
+ }), [
16
+ t,
17
+ n,
18
+ r,
19
+ a,
20
+ o,
21
+ s?.id
22
+ ]);
23
+ }
24
+ function u() {
25
+ let e = l(), [i, s] = a([]), [c, u] = a(!0), [d, f] = a(null), [p, m] = a(0);
26
+ return r(() => {
27
+ let n = !0;
28
+ return u(!0), f(null), t(e, o).then((e) => {
29
+ n && s(e.fleetAlerts);
30
+ }).catch((e) => {
31
+ n && f(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to load fleet alerts"));
32
+ }).finally(() => {
33
+ n && u(!1);
34
+ }), () => {
35
+ n = !1;
36
+ };
37
+ }, [e, p]), {
38
+ data: i,
39
+ isLoading: c,
40
+ error: d,
41
+ refetch: n(() => m((e) => e + 1), [])
42
+ };
43
+ }
44
+ var d = {
45
+ routeId: "",
46
+ vehicles: [],
47
+ drivers: []
48
+ };
49
+ function f(e, i = 3) {
50
+ let o = l(), [c, u] = a(d), [f, p] = a(!!e), [m, h] = a(null), [g, _] = a(0);
51
+ return r(() => {
52
+ if (!e) {
53
+ u(d), p(!1);
54
+ return;
55
+ }
56
+ let n = !0;
57
+ return p(!0), h(null), t(o, s, {
58
+ routeId: e,
59
+ limit: i
60
+ }).then((e) => {
61
+ n && u(e.assignmentSuggestions);
62
+ }).catch((e) => {
63
+ n && h(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to load suggestions"));
64
+ }).finally(() => {
65
+ n && p(!1);
66
+ }), () => {
67
+ n = !1;
68
+ };
69
+ }, [
70
+ o,
71
+ e,
72
+ i,
73
+ g
74
+ ]), {
75
+ data: c,
76
+ isLoading: f,
77
+ error: m,
78
+ refetch: n(() => _((e) => e + 1), [])
79
+ };
80
+ }
81
+ function p() {
82
+ let e = l();
83
+ return n(async (n, r) => {
84
+ await t(e, c, {
85
+ id: n,
86
+ input: r
87
+ });
88
+ }, [e]);
89
+ }
90
+ //#endregion
91
+ export { f as useAssignmentSuggestions, u as useFleetAlerts, p as useRecordVehicleService };
@@ -0,0 +1,17 @@
1
+ import { AssignmentSuggestions as Suggestions } from '../hooks/useFleetIntelligence';
2
+
3
+ interface AssignmentSuggestionsProps {
4
+ suggestions: Suggestions;
5
+ isLoading: boolean;
6
+ error: Error | null;
7
+ onRetry: () => void;
8
+ assignedVehicleId?: string;
9
+ assignedDriverId?: string;
10
+ onAssign: (patch: {
11
+ vehicleId?: string;
12
+ driverId?: string;
13
+ }) => Promise<void>;
14
+ t: (key: string, fallback: string) => string;
15
+ }
16
+ export declare function AssignmentSuggestions({ suggestions, isLoading, error, onRetry, assignedVehicleId, assignedDriverId, onAssign, t, }: AssignmentSuggestionsProps): import("react").JSX.Element;
17
+ export {};
@@ -0,0 +1,14 @@
1
+ import { FleetAlert } from '../hooks/useFleetIntelligence';
2
+
3
+ interface FleetAlertsPanelProps {
4
+ alerts: FleetAlert[];
5
+ isLoading: boolean;
6
+ error: Error | null;
7
+ onRetry: () => void;
8
+ onOpen: (alert: FleetAlert) => void;
9
+ t: (key: string, fallback: string) => string;
10
+ }
11
+ /** Human-readable "how close / how late" for an alert, from its structured due data. */
12
+ export declare function describeDue(alert: Pick<FleetAlert, 'daysUntilDue' | 'kmUntilDue'>): string;
13
+ export declare function FleetAlertsPanel({ alerts, isLoading, error, onRetry, onOpen, t, }: FleetAlertsPanelProps): import("react").JSX.Element;
14
+ export {};
@@ -0,0 +1,10 @@
1
+ import { RecordVehicleServiceInput } from '../hooks/useFleetIntelligence';
2
+ import { Vehicle } from '../types';
3
+
4
+ interface VehicleServiceCardProps {
5
+ vehicle: Vehicle;
6
+ onRecord: (input: RecordVehicleServiceInput) => Promise<void>;
7
+ t: (key: string, fallback: string) => string;
8
+ }
9
+ export declare function VehicleServiceCard({ vehicle, onRecord, t }: VehicleServiceCardProps): import("react").JSX.Element;
10
+ export {};
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Fleet intelligence hooks — compliance/maintenance alerts, dispatch
3
+ * suggestions for a route, and logging a completed vehicle service.
4
+ *
5
+ * All three sit on top of wspace-movethewheels-svc's fleet module; the ranking
6
+ * and alert rules live server-side so every surface (web, CLI, MCP) agrees.
7
+ */
8
+ export type FleetAlertType = 'LICENSE_EXPIRED' | 'LICENSE_EXPIRING' | 'MAINTENANCE_OVERDUE' | 'MAINTENANCE_DUE_SOON';
9
+ export type FleetAlertSeverity = 'CRITICAL' | 'WARNING';
10
+ export type FleetAlertSubjectType = 'VEHICLE' | 'DRIVER';
11
+ export interface FleetAlert {
12
+ id: string;
13
+ type: FleetAlertType;
14
+ severity: FleetAlertSeverity;
15
+ subjectType: FleetAlertSubjectType;
16
+ subjectId: string;
17
+ subjectName: string;
18
+ message: string;
19
+ dueDate: string | null;
20
+ daysUntilDue: number | null;
21
+ kmUntilDue: number | null;
22
+ }
23
+ export interface VehicleSuggestion {
24
+ score: number;
25
+ reasons: string[];
26
+ vehicle: {
27
+ id: string;
28
+ vehicleNumber: string;
29
+ make: string | null;
30
+ model: string | null;
31
+ };
32
+ }
33
+ export interface DriverSuggestion {
34
+ score: number;
35
+ reasons: string[];
36
+ driver: {
37
+ id: string;
38
+ firstName: string;
39
+ lastName: string;
40
+ rating: number | null;
41
+ };
42
+ }
43
+ export interface AssignmentSuggestions {
44
+ routeId: string;
45
+ vehicles: VehicleSuggestion[];
46
+ drivers: DriverSuggestion[];
47
+ }
48
+ export interface RecordVehicleServiceInput {
49
+ /** ISO date-time. Defaults to now on the server. */
50
+ serviceDate?: string;
51
+ mileage?: number;
52
+ intervalDays?: number;
53
+ intervalKm?: number;
54
+ }
55
+ interface AsyncResource<T> {
56
+ data: T;
57
+ isLoading: boolean;
58
+ error: Error | null;
59
+ refetch: () => void;
60
+ }
61
+ export declare function useFleetAlerts(): AsyncResource<FleetAlert[]>;
62
+ export declare function useAssignmentSuggestions(routeId: string | undefined, limit?: number): AsyncResource<AssignmentSuggestions>;
63
+ export declare function useRecordVehicleService(): (vehicleId: string, input: RecordVehicleServiceInput) => Promise<void>;
64
+ export {};
@@ -277,6 +277,8 @@ export interface Vehicle {
277
277
  currentLocation?: Location;
278
278
  lastMaintenance?: Date;
279
279
  nextMaintenance?: Date;
280
+ /** Odometer reading (km) at which the next service is due. */
281
+ nextMaintenanceMileage?: number;
280
282
  fuelLevel?: number;
281
283
  mileage?: number;
282
284
  documents: VehicleDocument[];