@burdenoff/microfe-movethewheels 2026.929.2 → 2026.929.3
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/hooks/useFleet.js
CHANGED
|
@@ -90,7 +90,7 @@ function E(e) {
|
|
|
90
90
|
phone: e.phone ?? "",
|
|
91
91
|
employeeId: e.employeeId,
|
|
92
92
|
licenseNumber: e.licenseNumber ?? "",
|
|
93
|
-
licenseType: e.licenseClass ?? "
|
|
93
|
+
licenseType: e.licenseClass ?? "",
|
|
94
94
|
licenseExpiry: e.licenseExpiry ? new Date(e.licenseExpiry) : void 0,
|
|
95
95
|
vehicleIds: e.vehicleId ? [e.vehicleId] : [],
|
|
96
96
|
assignedVehicle: e.vehicleId ?? void 0,
|
|
@@ -44,8 +44,8 @@ export interface CreateDriverInput {
|
|
|
44
44
|
bonusYtd?: number;
|
|
45
45
|
payCurrency?: string;
|
|
46
46
|
}
|
|
47
|
-
/**
|
|
48
|
-
export interface UpdateDriverInput extends CreateDriverInput {
|
|
47
|
+
/** Fields accepted when updating an existing driver; employee IDs are immutable. */
|
|
48
|
+
export interface UpdateDriverInput extends Omit<CreateDriverInput, 'employeeId'> {
|
|
49
49
|
onLeaveUntil?: string | null;
|
|
50
50
|
terminatedAt?: string;
|
|
51
51
|
terminationReason?: string;
|
|
@@ -25,12 +25,12 @@ import { EntityCombobox as re } from "../components/EntityCombobox.js";
|
|
|
25
25
|
import { useEffect as ie, useState as z } from "react";
|
|
26
26
|
import { useLocation as B, useParams as V } from "react-router";
|
|
27
27
|
import { Fragment as H, jsx as U, jsxs as W } from "react/jsx-runtime";
|
|
28
|
-
import { EmphasisPanel as ae, NextSteps as G, PagePurpose as
|
|
28
|
+
import { EmphasisPanel as ae, NextSteps as G, PagePurpose as oe, ResponsiveDialog as se, ResponsiveDialogContent as ce, ResponsiveDialogDescription as le, ResponsiveDialogFooter as ue, ResponsiveDialogHeader as de, ResponsiveDialogTitle as fe } from "@burdenoff/fe-libs/ui";
|
|
29
29
|
//#region src/movethewheels/pages/DriverDetailsPage.tsx
|
|
30
|
-
var
|
|
30
|
+
var pe = Object.entries(N).map(([e, t]) => ({
|
|
31
31
|
value: e,
|
|
32
32
|
label: t
|
|
33
|
-
})),
|
|
33
|
+
})), K = (e) => e ? e.toISOString().slice(0, 10) : "", q = (e) => e ? `${e}T12:00:00.000Z` : void 0, me = {
|
|
34
34
|
employeeId: "",
|
|
35
35
|
firstName: "",
|
|
36
36
|
lastName: "",
|
|
@@ -65,36 +65,39 @@ function J({ editId: t }) {
|
|
|
65
65
|
phone: j.phone || "",
|
|
66
66
|
licenseNumber: j.licenseNumber || "",
|
|
67
67
|
licenseClass: j.licenseType || "",
|
|
68
|
-
licenseExpiry:
|
|
68
|
+
licenseExpiry: K(j.licenseExpiry),
|
|
69
69
|
status: j.status,
|
|
70
70
|
vehicleId: j.assignedVehicle || "",
|
|
71
71
|
rating: j.rating ? String(j.rating) : "",
|
|
72
72
|
notes: j.notes || "",
|
|
73
|
-
hireDate:
|
|
73
|
+
hireDate: K(j.hireDate),
|
|
74
74
|
baseSalary: j.baseSalary === void 0 ? "" : String(j.baseSalary),
|
|
75
75
|
bonusYtd: j.bonusYtd === void 0 ? "" : String(j.bonusYtd),
|
|
76
76
|
payCurrency: j.payCurrency || "",
|
|
77
|
-
onLeaveUntil:
|
|
77
|
+
onLeaveUntil: K(j.onLeaveUntil)
|
|
78
78
|
});
|
|
79
79
|
}, [t, j]);
|
|
80
|
-
let we = () => i?.(d(r, "fleet/drivers")), $ = () => ({
|
|
81
|
-
employeeId: P.employeeId.trim(),
|
|
80
|
+
let we = () => i?.(d(r, "fleet/drivers")), $ = (e = !0) => ({
|
|
82
81
|
firstName: P.firstName.trim(),
|
|
83
82
|
lastName: P.lastName.trim(),
|
|
84
83
|
email: P.email.trim() || void 0,
|
|
85
84
|
phone: P.phone.trim() || void 0,
|
|
86
85
|
licenseNumber: P.licenseNumber.trim() || void 0,
|
|
87
86
|
licenseClass: P.licenseClass.trim() || void 0,
|
|
88
|
-
licenseExpiry:
|
|
87
|
+
licenseExpiry: q(P.licenseExpiry),
|
|
89
88
|
status: p(P.status),
|
|
90
89
|
vehicleId: P.vehicleId || void 0,
|
|
91
|
-
rating: P.rating ? Number(P.rating) : void 0,
|
|
90
|
+
rating: P.rating.trim() ? Number(P.rating) : void 0,
|
|
92
91
|
notes: P.notes.trim() || void 0,
|
|
93
|
-
hireDate:
|
|
94
|
-
baseSalary: P.baseSalary ? Number(P.baseSalary) : void 0,
|
|
95
|
-
bonusYtd: P.bonusYtd ? Number(P.bonusYtd) : void 0,
|
|
96
|
-
payCurrency: P.payCurrency.trim() || void 0
|
|
97
|
-
|
|
92
|
+
hireDate: q(P.hireDate),
|
|
93
|
+
baseSalary: P.baseSalary.trim() ? Number(P.baseSalary) : void 0,
|
|
94
|
+
bonusYtd: P.bonusYtd.trim() ? Number(P.bonusYtd) : void 0,
|
|
95
|
+
payCurrency: P.payCurrency.trim() || void 0,
|
|
96
|
+
...e ? { onLeaveUntil: q(P.onLeaveUntil) ?? null } : {}
|
|
97
|
+
}), Te = () => ({
|
|
98
|
+
employeeId: P.employeeId.trim(),
|
|
99
|
+
...$(!1)
|
|
100
|
+
}), Ee = async (e) => {
|
|
98
101
|
if (e.preventDefault(), !P.employeeId.trim() || !P.firstName.trim() || !P.lastName.trim()) {
|
|
99
102
|
G("Employee ID, first name and last name are required.");
|
|
100
103
|
return;
|
|
@@ -109,13 +112,13 @@ function J({ editId: t }) {
|
|
|
109
112
|
let e = await s(t, $());
|
|
110
113
|
i?.(d(r, `fleet/drivers/${e.id}`));
|
|
111
114
|
} else {
|
|
112
|
-
let e = await o(
|
|
115
|
+
let e = await o(Te());
|
|
113
116
|
i?.(d(r, `fleet/drivers/${e.id}`));
|
|
114
117
|
}
|
|
115
118
|
} catch (e) {
|
|
116
119
|
console.error("Failed to save driver:", e), G(e instanceof Error ? e.message : "Failed to save driver. Please try again."), B(!1);
|
|
117
120
|
}
|
|
118
|
-
},
|
|
121
|
+
}, De = async () => {
|
|
119
122
|
if (!t || !X.trim()) {
|
|
120
123
|
Q("A reason is required to dismiss a driver.");
|
|
121
124
|
return;
|
|
@@ -132,7 +135,7 @@ function J({ editId: t }) {
|
|
|
132
135
|
} finally {
|
|
133
136
|
ge(!1);
|
|
134
137
|
}
|
|
135
|
-
},
|
|
138
|
+
}, Oe = N && !!j?.terminatedAt;
|
|
136
139
|
return /* @__PURE__ */ W(M, {
|
|
137
140
|
title: N ? "Edit Driver" : "Add Driver",
|
|
138
141
|
subtitle: "Add a driver to your workforce",
|
|
@@ -145,7 +148,7 @@ function J({ editId: t }) {
|
|
|
145
148
|
disabled: R,
|
|
146
149
|
children: [/* @__PURE__ */ U(e, { size: 16 }), " Cancel"]
|
|
147
150
|
}), /* @__PURE__ */ W(v, {
|
|
148
|
-
onClick:
|
|
151
|
+
onClick: Ee,
|
|
149
152
|
disabled: R,
|
|
150
153
|
children: [
|
|
151
154
|
/* @__PURE__ */ U(a, { size: 16 }),
|
|
@@ -155,11 +158,11 @@ function J({ editId: t }) {
|
|
|
155
158
|
})]
|
|
156
159
|
}),
|
|
157
160
|
children: [
|
|
158
|
-
/* @__PURE__ */ U(
|
|
161
|
+
/* @__PURE__ */ U(oe, {
|
|
159
162
|
icon: /* @__PURE__ */ U(u, { size: 18 }),
|
|
160
163
|
children: "Add a driver so they can be assigned to orders and routes. An employee ID, name and phone get them on the roster — add licensing, a vehicle and HR details to keep the record complete."
|
|
161
164
|
}),
|
|
162
|
-
|
|
165
|
+
Oe && j?.terminatedAt && /* @__PURE__ */ W("div", {
|
|
163
166
|
className: "rounded-card border border-status-error-border bg-status-error-bg-subtle p-4 text-sm text-status-error-text",
|
|
164
167
|
children: [/* @__PURE__ */ W("p", {
|
|
165
168
|
className: "font-medium",
|
|
@@ -174,7 +177,7 @@ function J({ editId: t }) {
|
|
|
174
177
|
})]
|
|
175
178
|
}),
|
|
176
179
|
/* @__PURE__ */ W("form", {
|
|
177
|
-
onSubmit:
|
|
180
|
+
onSubmit: Ee,
|
|
178
181
|
className: "max-w-3xl space-y-6",
|
|
179
182
|
children: [
|
|
180
183
|
/* @__PURE__ */ U(F, { message: V }),
|
|
@@ -191,10 +194,15 @@ function J({ editId: t }) {
|
|
|
191
194
|
className: "space-y-2",
|
|
192
195
|
children: [/* @__PURE__ */ W(w, {
|
|
193
196
|
htmlFor: "employeeId",
|
|
194
|
-
children: [
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
197
|
+
children: [
|
|
198
|
+
"Employee ID ",
|
|
199
|
+
N ? "(immutable)" : "",
|
|
200
|
+
" ",
|
|
201
|
+
/* @__PURE__ */ U("span", {
|
|
202
|
+
className: "text-status-error-text",
|
|
203
|
+
children: !N && "*"
|
|
204
|
+
})
|
|
205
|
+
]
|
|
198
206
|
}), /* @__PURE__ */ U(C, {
|
|
199
207
|
id: "employeeId",
|
|
200
208
|
value: P.employeeId,
|
|
@@ -203,7 +211,8 @@ function J({ editId: t }) {
|
|
|
203
211
|
employeeId: e.target.value
|
|
204
212
|
})),
|
|
205
213
|
placeholder: "EMP-001",
|
|
206
|
-
required: !0
|
|
214
|
+
required: !0,
|
|
215
|
+
disabled: N
|
|
207
216
|
})]
|
|
208
217
|
}),
|
|
209
218
|
/* @__PURE__ */ W("div", {
|
|
@@ -298,7 +307,7 @@ function J({ editId: t }) {
|
|
|
298
307
|
children: [/* @__PURE__ */ U(O, {
|
|
299
308
|
id: "status",
|
|
300
309
|
children: /* @__PURE__ */ U(k, {})
|
|
301
|
-
}), /* @__PURE__ */ U(E, { children:
|
|
310
|
+
}), /* @__PURE__ */ U(E, { children: pe.map((e) => /* @__PURE__ */ U(D, {
|
|
302
311
|
value: e.value,
|
|
303
312
|
children: e.label
|
|
304
313
|
}, e.value)) })]
|
|
@@ -441,9 +450,21 @@ function J({ editId: t }) {
|
|
|
441
450
|
}),
|
|
442
451
|
/* @__PURE__ */ W("div", {
|
|
443
452
|
className: "space-y-2",
|
|
444
|
-
children: [/* @__PURE__ */
|
|
445
|
-
|
|
446
|
-
children:
|
|
453
|
+
children: [/* @__PURE__ */ W("div", {
|
|
454
|
+
className: "flex items-center justify-between gap-2",
|
|
455
|
+
children: [/* @__PURE__ */ U(w, {
|
|
456
|
+
htmlFor: "onLeaveUntil",
|
|
457
|
+
children: "On Leave Until"
|
|
458
|
+
}), P.onLeaveUntil && /* @__PURE__ */ U(v, {
|
|
459
|
+
type: "button",
|
|
460
|
+
variant: "ghost",
|
|
461
|
+
size: "sm",
|
|
462
|
+
onClick: () => I((e) => ({
|
|
463
|
+
...e,
|
|
464
|
+
onLeaveUntil: ""
|
|
465
|
+
})),
|
|
466
|
+
children: "Clear"
|
|
467
|
+
})]
|
|
447
468
|
}), /* @__PURE__ */ U(C, {
|
|
448
469
|
id: "onLeaveUntil",
|
|
449
470
|
type: "date",
|
|
@@ -530,7 +551,7 @@ function J({ editId: t }) {
|
|
|
530
551
|
placeholder: "Performance notes, disciplinary history, anything worth tracking for this driver...",
|
|
531
552
|
rows: 4
|
|
532
553
|
}) })] }),
|
|
533
|
-
N && !
|
|
554
|
+
N && !Oe && /* @__PURE__ */ W(y, {
|
|
534
555
|
className: "border-status-error-border",
|
|
535
556
|
children: [/* @__PURE__ */ U(x, { children: /* @__PURE__ */ W(S, {
|
|
536
557
|
className: "flex items-center gap-2 text-status-error-text",
|
|
@@ -548,11 +569,11 @@ function J({ editId: t }) {
|
|
|
548
569
|
})
|
|
549
570
|
]
|
|
550
571
|
}),
|
|
551
|
-
/* @__PURE__ */ U(
|
|
572
|
+
/* @__PURE__ */ U(se, {
|
|
552
573
|
open: J,
|
|
553
574
|
onOpenChange: Y,
|
|
554
|
-
children: /* @__PURE__ */ W(
|
|
555
|
-
/* @__PURE__ */ W(
|
|
575
|
+
children: /* @__PURE__ */ W(ce, { children: [
|
|
576
|
+
/* @__PURE__ */ W(de, { children: [/* @__PURE__ */ U(fe, { children: "Dismiss this driver?" }), /* @__PURE__ */ U(le, { children: "This records the driver as dismissed and requires a reason. It cannot be undone from here." })] }),
|
|
556
577
|
/* @__PURE__ */ W("div", {
|
|
557
578
|
className: "space-y-2 px-1",
|
|
558
579
|
children: [
|
|
@@ -573,14 +594,14 @@ function J({ editId: t }) {
|
|
|
573
594
|
/* @__PURE__ */ U(F, { message: _e })
|
|
574
595
|
]
|
|
575
596
|
}),
|
|
576
|
-
/* @__PURE__ */ W(
|
|
597
|
+
/* @__PURE__ */ W(ue, { children: [/* @__PURE__ */ U(v, {
|
|
577
598
|
variant: "outline",
|
|
578
599
|
onClick: () => Y(!1),
|
|
579
600
|
disabled: Z,
|
|
580
601
|
children: "Cancel"
|
|
581
602
|
}), /* @__PURE__ */ U(v, {
|
|
582
603
|
className: "bg-status-error-text text-text-inverse hover:opacity-90",
|
|
583
|
-
onClick:
|
|
604
|
+
onClick: De,
|
|
584
605
|
disabled: Z,
|
|
585
606
|
children: Z ? "Dismissing..." : "Confirm dismissal"
|
|
586
607
|
})] })
|
|
@@ -650,7 +671,7 @@ function Y() {
|
|
|
650
671
|
]
|
|
651
672
|
}),
|
|
652
673
|
children: [
|
|
653
|
-
/* @__PURE__ */ U(
|
|
674
|
+
/* @__PURE__ */ U(oe, {
|
|
654
675
|
icon: /* @__PURE__ */ U(u, { size: 18 }),
|
|
655
676
|
children: "A driver's full profile — licensing, assigned vehicle, documents and HR record. Use it to confirm someone is road-ready and compliant before assigning them an order or route."
|
|
656
677
|
}),
|
|
@@ -788,9 +809,9 @@ function Y() {
|
|
|
788
809
|
}) }), /* @__PURE__ */ W(b, { children: [/* @__PURE__ */ U("p", {
|
|
789
810
|
className: "text-sm",
|
|
790
811
|
children: _.currentLocation.address || "Location updating..."
|
|
791
|
-
}), /* @__PURE__ */
|
|
812
|
+
}), /* @__PURE__ */ U("p", {
|
|
792
813
|
className: "text-xs text-text-secondary mt-1",
|
|
793
|
-
children:
|
|
814
|
+
children: _.currentLocation.timestamp ? `Updated ${L(_.currentLocation.timestamp, "relative")}` : "Update time not recorded"
|
|
794
815
|
})] })] })
|
|
795
816
|
]
|
|
796
817
|
})]
|
package/package.json
CHANGED
|
@@ -54,8 +54,8 @@ export interface CreateDriverInput {
|
|
|
54
54
|
payCurrency?: string;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
/**
|
|
58
|
-
export interface UpdateDriverInput extends CreateDriverInput {
|
|
57
|
+
/** Fields accepted when updating an existing driver; employee IDs are immutable. */
|
|
58
|
+
export interface UpdateDriverInput extends Omit<CreateDriverInput, 'employeeId'> {
|
|
59
59
|
onLeaveUntil?: string | null;
|
|
60
60
|
terminatedAt?: string;
|
|
61
61
|
terminationReason?: string;
|
|
@@ -602,7 +602,7 @@ export function mapDriver(driver: ApiDriver): Driver {
|
|
|
602
602
|
phone: driver.phone ?? '',
|
|
603
603
|
employeeId: driver.employeeId,
|
|
604
604
|
licenseNumber: driver.licenseNumber ?? '',
|
|
605
|
-
licenseType: driver.licenseClass ?? '
|
|
605
|
+
licenseType: driver.licenseClass ?? '',
|
|
606
606
|
licenseExpiry: driver.licenseExpiry ? new Date(driver.licenseExpiry) : undefined,
|
|
607
607
|
vehicleIds: driver.vehicleId ? [driver.vehicleId] : [],
|
|
608
608
|
assignedVehicle: driver.vehicleId ?? undefined,
|
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
useVehicle,
|
|
54
54
|
useVehicles,
|
|
55
55
|
toApiDriverStatus,
|
|
56
|
+
type CreateDriverInput,
|
|
56
57
|
type UpdateDriverInput,
|
|
57
58
|
} from '../hooks/useFleet';
|
|
58
59
|
import { useDebouncedValue } from '../hooks/useDebouncedValue';
|
|
@@ -163,8 +164,7 @@ function NewDriverForm({ editId }: { editId?: string }) {
|
|
|
163
164
|
|
|
164
165
|
const goBack = () => navigate?.(joinPath(basePath, 'fleet/drivers'));
|
|
165
166
|
|
|
166
|
-
const buildPayload = (): UpdateDriverInput => ({
|
|
167
|
-
employeeId: form.employeeId.trim(),
|
|
167
|
+
const buildPayload = (includeLeaveFields = true): UpdateDriverInput => ({
|
|
168
168
|
firstName: form.firstName.trim(),
|
|
169
169
|
lastName: form.lastName.trim(),
|
|
170
170
|
email: form.email.trim() || undefined,
|
|
@@ -174,12 +174,18 @@ function NewDriverForm({ editId }: { editId?: string }) {
|
|
|
174
174
|
licenseExpiry: fromDateInput(form.licenseExpiry),
|
|
175
175
|
status: toApiDriverStatus(form.status),
|
|
176
176
|
vehicleId: form.vehicleId || undefined,
|
|
177
|
-
rating: form.rating ? Number(form.rating) : undefined,
|
|
177
|
+
rating: form.rating.trim() ? Number(form.rating) : undefined,
|
|
178
178
|
notes: form.notes.trim() || undefined,
|
|
179
179
|
hireDate: fromDateInput(form.hireDate),
|
|
180
|
-
baseSalary: form.baseSalary ? Number(form.baseSalary) : undefined,
|
|
181
|
-
bonusYtd: form.bonusYtd ? Number(form.bonusYtd) : undefined,
|
|
180
|
+
baseSalary: form.baseSalary.trim() ? Number(form.baseSalary) : undefined,
|
|
181
|
+
bonusYtd: form.bonusYtd.trim() ? Number(form.bonusYtd) : undefined,
|
|
182
182
|
payCurrency: form.payCurrency.trim() || undefined,
|
|
183
|
+
...(includeLeaveFields ? { onLeaveUntil: fromDateInput(form.onLeaveUntil) ?? null } : {}),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const buildCreatePayload = (): CreateDriverInput => ({
|
|
187
|
+
employeeId: form.employeeId.trim(),
|
|
188
|
+
...buildPayload(false),
|
|
183
189
|
});
|
|
184
190
|
|
|
185
191
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
@@ -199,7 +205,7 @@ function NewDriverForm({ editId }: { editId?: string }) {
|
|
|
199
205
|
const updated = await updateDriver(editId, buildPayload());
|
|
200
206
|
navigate?.(joinPath(basePath, `fleet/drivers/${updated.id}`));
|
|
201
207
|
} else {
|
|
202
|
-
const created = await createDriver(
|
|
208
|
+
const created = await createDriver(buildCreatePayload());
|
|
203
209
|
navigate?.(joinPath(basePath, `fleet/drivers/${created.id}`));
|
|
204
210
|
}
|
|
205
211
|
} catch (err) {
|
|
@@ -280,7 +286,8 @@ function NewDriverForm({ editId }: { editId?: string }) {
|
|
|
280
286
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
281
287
|
<div className="space-y-2">
|
|
282
288
|
<Label htmlFor="employeeId">
|
|
283
|
-
Employee ID
|
|
289
|
+
Employee ID {isEditing ? '(immutable)' : ''}{' '}
|
|
290
|
+
<span className="text-status-error-text">{!isEditing && '*'}</span>
|
|
284
291
|
</Label>
|
|
285
292
|
<Input
|
|
286
293
|
id="employeeId"
|
|
@@ -288,6 +295,7 @@ function NewDriverForm({ editId }: { editId?: string }) {
|
|
|
288
295
|
onChange={(e) => setForm((prev) => ({ ...prev, employeeId: e.target.value }))}
|
|
289
296
|
placeholder="EMP-001"
|
|
290
297
|
required
|
|
298
|
+
disabled={isEditing}
|
|
291
299
|
/>
|
|
292
300
|
</div>
|
|
293
301
|
<div className="space-y-2">
|
|
@@ -445,7 +453,19 @@ function NewDriverForm({ editId }: { editId?: string }) {
|
|
|
445
453
|
/>
|
|
446
454
|
</div>
|
|
447
455
|
<div className="space-y-2">
|
|
448
|
-
<
|
|
456
|
+
<div className="flex items-center justify-between gap-2">
|
|
457
|
+
<Label htmlFor="onLeaveUntil">On Leave Until</Label>
|
|
458
|
+
{form.onLeaveUntil && (
|
|
459
|
+
<Button
|
|
460
|
+
type="button"
|
|
461
|
+
variant="ghost"
|
|
462
|
+
size="sm"
|
|
463
|
+
onClick={() => setForm((prev) => ({ ...prev, onLeaveUntil: '' }))}
|
|
464
|
+
>
|
|
465
|
+
Clear
|
|
466
|
+
</Button>
|
|
467
|
+
)}
|
|
468
|
+
</div>
|
|
449
469
|
<Input
|
|
450
470
|
id="onLeaveUntil"
|
|
451
471
|
type="date"
|
|
@@ -823,7 +843,9 @@ export default function DriverDetailsPage() {
|
|
|
823
843
|
{driver.currentLocation.address || 'Location updating...'}
|
|
824
844
|
</p>
|
|
825
845
|
<p className="text-xs text-text-secondary mt-1">
|
|
826
|
-
|
|
846
|
+
{driver.currentLocation.timestamp
|
|
847
|
+
? `Updated ${formatDate(driver.currentLocation.timestamp, 'relative')}`
|
|
848
|
+
: 'Update time not recorded'}
|
|
827
849
|
</p>
|
|
828
850
|
</CardContent>
|
|
829
851
|
</Card>
|