@burdenoff/microfe-movethewheels 2026.726.1 → 2026.728.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.
Files changed (29) hide show
  1. package/dist/MoveTheWheelsRoutes.js +7 -3
  2. package/dist/components/ProofOfDeliveryCard.js +155 -0
  3. package/dist/components/SignaturePad.js +82 -0
  4. package/dist/hooks/index.js +1 -0
  5. package/dist/hooks/useEpod.js +208 -0
  6. package/dist/index.js +22 -21
  7. package/dist/movethewheels/components/ProofOfDeliveryCard.d.ts +5 -0
  8. package/dist/movethewheels/components/SignaturePad.d.ts +11 -0
  9. package/dist/movethewheels/hooks/index.d.ts +1 -0
  10. package/dist/movethewheels/hooks/useEpod.d.ts +59 -0
  11. package/dist/movethewheels/pages/DeliveryCapturePage.d.ts +1 -0
  12. package/dist/movethewheels/types/index.d.ts +18 -0
  13. package/dist/node_modules/lucide-react/dist/esm/icons/loader-circle.js +8 -0
  14. package/dist/node_modules/lucide-react/dist/esm/icons/pen-line.js +11 -0
  15. package/dist/node_modules/lucide-react/dist/esm/icons/type.js +24 -0
  16. package/dist/pages/DeliveryCapturePage.js +245 -0
  17. package/dist/pages/OrderDetailsPage.js +14 -12
  18. package/dist/pages/TrackingPage.js +156 -154
  19. package/dist/utils/formatters.js +13 -2
  20. package/package.json +1 -1
  21. package/src/movethewheels/MoveTheWheelsRoutes.tsx +4 -0
  22. package/src/movethewheels/components/ProofOfDeliveryCard.tsx +149 -0
  23. package/src/movethewheels/components/SignaturePad.tsx +157 -0
  24. package/src/movethewheels/hooks/index.ts +12 -0
  25. package/src/movethewheels/hooks/useEpod.ts +410 -0
  26. package/src/movethewheels/pages/DeliveryCapturePage.tsx +297 -0
  27. package/src/movethewheels/pages/OrderDetailsPage.tsx +4 -0
  28. package/src/movethewheels/pages/TrackingPage.tsx +4 -0
  29. package/src/movethewheels/types/index.ts +21 -0
@@ -230,6 +230,24 @@ export interface Order {
230
230
  createdAt: Date;
231
231
  updatedAt: Date;
232
232
  }
233
+ export type SignatureType = 'drawn' | 'typed';
234
+ export interface ProofOfDelivery {
235
+ id: string;
236
+ deliveryJobId: string;
237
+ shipmentId?: string;
238
+ recipientName: string;
239
+ signatureType: SignatureType;
240
+ /** Drawn: a `data:image/png;base64,...` capture. Typed: the typed full name. */
241
+ signatureData: string;
242
+ /** wspace-files-svc File ids for any attached delivery photos. */
243
+ photoFileIds: string[];
244
+ latitude?: number;
245
+ longitude?: number;
246
+ locationAccuracy?: number;
247
+ deliveredAt: Date;
248
+ capturedByDriverId: string;
249
+ notes?: string;
250
+ }
233
251
  export type VehicleType = 'van' | 'truck' | 'motorcycle' | 'bicycle' | 'car';
234
252
  export type VehicleStatus = 'available' | 'assigned' | 'maintenance' | 'out_of_service';
235
253
  export interface VehicleDocument {
@@ -0,0 +1,8 @@
1
+ import { createLucideIcon as e } from "../createLucideIcon.js";
2
+ //#region node_modules/lucide-react/dist/esm/icons/loader-circle.js
3
+ var t = e("LoaderCircle", [["path", {
4
+ d: "M21 12a9 9 0 1 1-6.219-8.56",
5
+ key: "13zald"
6
+ }]]);
7
+ //#endregion
8
+ export { t as LoaderCircle };
@@ -0,0 +1,11 @@
1
+ import { createLucideIcon as e } from "../createLucideIcon.js";
2
+ //#region node_modules/lucide-react/dist/esm/icons/pen-line.js
3
+ var t = e("PenLine", [["path", {
4
+ d: "M12 20h9",
5
+ key: "t2du7b"
6
+ }], ["path", {
7
+ d: "M16.376 3.622a1 1 0 0 1 3.002 3.002L7.368 18.635a2 2 0 0 1-.855.506l-2.872.838a.5.5 0 0 1-.62-.62l.838-2.872a2 2 0 0 1 .506-.854z",
8
+ key: "1ykcvy"
9
+ }]]);
10
+ //#endregion
11
+ export { t as PenLine };
@@ -0,0 +1,24 @@
1
+ import { createLucideIcon as e } from "../createLucideIcon.js";
2
+ //#region node_modules/lucide-react/dist/esm/icons/type.js
3
+ var t = e("Type", [
4
+ ["polyline", {
5
+ points: "4 7 4 4 20 4 20 7",
6
+ key: "1nosan"
7
+ }],
8
+ ["line", {
9
+ x1: "9",
10
+ x2: "15",
11
+ y1: "20",
12
+ y2: "20",
13
+ key: "swin9y"
14
+ }],
15
+ ["line", {
16
+ x1: "12",
17
+ x2: "12",
18
+ y1: "4",
19
+ y2: "20",
20
+ key: "1tx1rr"
21
+ }]
22
+ ]);
23
+ //#endregion
24
+ export { t as Type };
@@ -0,0 +1,245 @@
1
+ import { CircleCheck as e } from "../node_modules/lucide-react/dist/esm/icons/circle-check.js";
2
+ import { LoaderCircle as t } from "../node_modules/lucide-react/dist/esm/icons/loader-circle.js";
3
+ import { MapPin as n } from "../node_modules/lucide-react/dist/esm/icons/map-pin.js";
4
+ import { Package as r } from "../node_modules/lucide-react/dist/esm/icons/package.js";
5
+ import { PenLine as i } from "../node_modules/lucide-react/dist/esm/icons/pen-line.js";
6
+ import { Truck as a } from "../node_modules/lucide-react/dist/esm/icons/truck.js";
7
+ import { Type as o } from "../node_modules/lucide-react/dist/esm/icons/type.js";
8
+ import { Upload as s } from "../node_modules/lucide-react/dist/esm/icons/upload.js";
9
+ import { useCaptureEpod as c, useEpodPhotoUpload as l, useMyPendingEpodDeliveryJobs as u } from "../hooks/useEpod.js";
10
+ import { Button as d, Card as f, CardContent as p, CardHeader as m, CardTitle as h, Input as g, Label as _, Textarea as v } from "../components/ui.js";
11
+ import { PageLayout as y } from "../components/PageLayout.js";
12
+ import { FormError as b } from "../components/FormError.js";
13
+ import { Notice as x, useNotice as S } from "../components/Notice.js";
14
+ import { formatAddress as C, formatDate as w } from "../utils/formatters.js";
15
+ import { SignaturePad as T } from "../components/SignaturePad.js";
16
+ import { useRef as E, useState as D } from "react";
17
+ import { Fragment as O, jsx as k, jsxs as A } from "react/jsx-runtime";
18
+ //#region src/movethewheels/pages/DeliveryCapturePage.tsx
19
+ function j() {
20
+ return new Promise((e) => {
21
+ if (!navigator.geolocation) {
22
+ e(null);
23
+ return;
24
+ }
25
+ navigator.geolocation.getCurrentPosition((t) => e(t), () => e(null), {
26
+ enableHighAccuracy: !0,
27
+ timeout: 8e3,
28
+ maximumAge: 3e4
29
+ });
30
+ });
31
+ }
32
+ function M({ job: r, onCaptured: u, onCancel: y }) {
33
+ let { captureEpod: x, isSubmitting: S, error: w } = c(), { upload: M, isUploading: N, error: P } = l(), F = E(null), [I, L] = D(""), [R, z] = D("drawn"), [B, V] = D(""), [H, U] = D(null), [W, G] = D(""), [K, q] = D(null), J = async () => {
34
+ q(null);
35
+ let e = I.trim();
36
+ if (!e) {
37
+ q("Recipient name is required.");
38
+ return;
39
+ }
40
+ let t = R === "typed" ? B.trim() : F.current?.toDataUrl() ?? null;
41
+ if (!t) {
42
+ q(R === "typed" ? "Type the recipient full name as their signature." : "A signature is required.");
43
+ return;
44
+ }
45
+ try {
46
+ let n;
47
+ H && (n = [(await M(H)).fileId]);
48
+ let i = await j();
49
+ await x({
50
+ deliveryJobId: r.id,
51
+ recipientName: e,
52
+ signatureType: R,
53
+ signatureData: t,
54
+ photoFileIds: n,
55
+ latitude: i?.coords.latitude,
56
+ longitude: i?.coords.longitude,
57
+ locationAccuracy: i?.coords.accuracy,
58
+ notes: W.trim() || void 0
59
+ }), u();
60
+ } catch {}
61
+ }, Y = S || N;
62
+ return /* @__PURE__ */ A(f, { children: [/* @__PURE__ */ k(m, { children: /* @__PURE__ */ A(h, {
63
+ className: "flex items-center gap-2",
64
+ children: [
65
+ /* @__PURE__ */ k(a, { size: 18 }),
66
+ " Capture delivery for ",
67
+ r.deliveryJobNumber
68
+ ]
69
+ }) }), /* @__PURE__ */ A(p, {
70
+ className: "space-y-4",
71
+ children: [
72
+ /* @__PURE__ */ A("div", {
73
+ className: "text-sm text-text-secondary space-y-1",
74
+ children: [r.customerName && /* @__PURE__ */ A("p", { children: ["Recipient on file: ", r.customerName] }), r.deliveryAddress && /* @__PURE__ */ k("p", { children: C(r.deliveryAddress) })]
75
+ }),
76
+ /* @__PURE__ */ A("div", {
77
+ className: "space-y-1.5",
78
+ children: [/* @__PURE__ */ k(_, {
79
+ htmlFor: "epod-recipient-name",
80
+ children: "Recipient name"
81
+ }), /* @__PURE__ */ k(g, {
82
+ id: "epod-recipient-name",
83
+ value: I,
84
+ onChange: (e) => L(e.target.value),
85
+ placeholder: "Who received the delivery?"
86
+ })]
87
+ }),
88
+ /* @__PURE__ */ A("div", {
89
+ className: "space-y-1.5",
90
+ children: [
91
+ /* @__PURE__ */ k(_, { children: "Signature" }),
92
+ /* @__PURE__ */ A("div", {
93
+ className: "flex gap-2",
94
+ children: [/* @__PURE__ */ A(d, {
95
+ type: "button",
96
+ variant: R === "drawn" ? "default" : "outline",
97
+ size: "sm",
98
+ onClick: () => z("drawn"),
99
+ children: [/* @__PURE__ */ k(i, { size: 14 }), " Draw"]
100
+ }), /* @__PURE__ */ A(d, {
101
+ type: "button",
102
+ variant: R === "typed" ? "default" : "outline",
103
+ size: "sm",
104
+ onClick: () => z("typed"),
105
+ children: [/* @__PURE__ */ k(o, { size: 14 }), " Type name instead"]
106
+ })]
107
+ }),
108
+ R === "drawn" ? /* @__PURE__ */ k(T, { ref: F }) : /* @__PURE__ */ k(g, {
109
+ value: B,
110
+ onChange: (e) => V(e.target.value),
111
+ placeholder: "Type full name as signature"
112
+ })
113
+ ]
114
+ }),
115
+ /* @__PURE__ */ A("div", {
116
+ className: "space-y-1.5",
117
+ children: [
118
+ /* @__PURE__ */ k(_, {
119
+ htmlFor: "epod-photo",
120
+ children: "Delivery photo (optional)"
121
+ }),
122
+ /* @__PURE__ */ A("div", {
123
+ className: "flex items-center gap-2",
124
+ children: [/* @__PURE__ */ k("input", {
125
+ id: "epod-photo",
126
+ type: "file",
127
+ accept: "image/*",
128
+ capture: "environment",
129
+ onChange: (e) => U(e.target.files?.[0] ?? null),
130
+ className: "text-sm"
131
+ }), H && /* @__PURE__ */ k(s, {
132
+ size: 14,
133
+ className: "text-text-secondary"
134
+ })]
135
+ }),
136
+ H && /* @__PURE__ */ k("p", {
137
+ className: "text-xs text-text-secondary",
138
+ children: H.name
139
+ })
140
+ ]
141
+ }),
142
+ /* @__PURE__ */ A("div", {
143
+ className: "space-y-1.5",
144
+ children: [/* @__PURE__ */ k(_, {
145
+ htmlFor: "epod-notes",
146
+ children: "Notes (optional)"
147
+ }), /* @__PURE__ */ k(v, {
148
+ id: "epod-notes",
149
+ value: W,
150
+ onChange: (e) => G(e.target.value),
151
+ placeholder: "e.g. Left with front desk, gate code used, etc.",
152
+ rows: 2
153
+ })]
154
+ }),
155
+ /* @__PURE__ */ A("p", {
156
+ className: "flex items-center gap-1.5 text-xs text-text-secondary",
157
+ children: [/* @__PURE__ */ k(n, { size: 12 }), " Device GPS is captured automatically when permitted by the browser."]
158
+ }),
159
+ K && /* @__PURE__ */ k(b, { message: K }),
160
+ w && /* @__PURE__ */ k(b, { message: w.message }),
161
+ P && /* @__PURE__ */ k(b, { message: P.message }),
162
+ /* @__PURE__ */ A("div", {
163
+ className: "flex gap-2 justify-end",
164
+ children: [/* @__PURE__ */ k(d, {
165
+ type: "button",
166
+ variant: "outline",
167
+ onClick: y,
168
+ disabled: Y,
169
+ children: "Cancel"
170
+ }), /* @__PURE__ */ k(d, {
171
+ type: "button",
172
+ onClick: () => void J(),
173
+ disabled: Y,
174
+ children: Y ? /* @__PURE__ */ A(O, { children: [/* @__PURE__ */ k(t, {
175
+ size: 14,
176
+ className: "animate-spin"
177
+ }), " Submitting…"] }) : /* @__PURE__ */ A(O, { children: [/* @__PURE__ */ k(e, { size: 14 }), " Confirm delivery"] })
178
+ })]
179
+ })
180
+ ]
181
+ })] });
182
+ }
183
+ function N() {
184
+ let { jobs: t, isLoading: n, error: i, refetch: o } = u(), [s, c] = D(null), { message: l, notify: m } = S();
185
+ return /* @__PURE__ */ k(y, {
186
+ title: "My Deliveries",
187
+ subtitle: "Capture proof of delivery for your assigned stops",
188
+ icon: /* @__PURE__ */ k(a, { size: 24 }),
189
+ children: /* @__PURE__ */ A("div", {
190
+ className: "space-y-4",
191
+ children: [/* @__PURE__ */ k(x, { message: l }), s ? /* @__PURE__ */ k(M, {
192
+ job: s,
193
+ onCaptured: () => {
194
+ m("Proof of delivery captured — delivery marked complete."), c(null), o();
195
+ },
196
+ onCancel: () => c(null)
197
+ }) : n ? /* @__PURE__ */ k("p", {
198
+ className: "text-text-secondary text-center py-8",
199
+ children: "Loading your deliveries…"
200
+ }) : i ? /* @__PURE__ */ k(b, { message: i.message }) : t.length > 0 ? /* @__PURE__ */ k("div", {
201
+ className: "space-y-3",
202
+ children: t.map((e) => /* @__PURE__ */ k(f, { children: /* @__PURE__ */ A(p, {
203
+ className: "flex items-center justify-between gap-3 py-4",
204
+ children: [/* @__PURE__ */ A("div", {
205
+ className: "min-w-0",
206
+ children: [
207
+ /* @__PURE__ */ A("p", {
208
+ className: "font-medium flex items-center gap-2",
209
+ children: [
210
+ /* @__PURE__ */ k(r, { size: 16 }),
211
+ " ",
212
+ e.deliveryJobNumber
213
+ ]
214
+ }),
215
+ e.deliveryAddress && /* @__PURE__ */ k("p", {
216
+ className: "text-sm text-text-secondary truncate",
217
+ children: C(e.deliveryAddress)
218
+ }),
219
+ e.scheduledDelivery && /* @__PURE__ */ A("p", {
220
+ className: "text-xs text-text-secondary",
221
+ children: ["Scheduled ", w(e.scheduledDelivery, "short")]
222
+ })
223
+ ]
224
+ }), /* @__PURE__ */ k(d, {
225
+ type: "button",
226
+ size: "sm",
227
+ onClick: () => c(e),
228
+ children: "Capture"
229
+ })]
230
+ }) }, e.id))
231
+ }) : /* @__PURE__ */ A("div", {
232
+ className: "text-center py-12",
233
+ children: [/* @__PURE__ */ k(e, {
234
+ size: 40,
235
+ className: "mx-auto mb-3 text-text-secondary opacity-50"
236
+ }), /* @__PURE__ */ k("p", {
237
+ className: "text-text-secondary",
238
+ children: "No deliveries awaiting proof of delivery."
239
+ })]
240
+ })]
241
+ })
242
+ });
243
+ }
244
+ //#endregion
245
+ export { N as default };
@@ -25,10 +25,11 @@ import { FormError as xe } from "../components/FormError.js";
25
25
  import { Notice as Se, useNotice as T } from "../components/Notice.js";
26
26
  import { formatAddress as E, formatCurrency as D, formatDate as O } from "../utils/formatters.js";
27
27
  import { DocumentsCard as Ce } from "../components/DocumentsCard.js";
28
+ import { ProofOfDeliveryCard as we } from "../components/ProofOfDeliveryCard.js";
28
29
  import { useState as k } from "react";
29
- import { useParams as we } from "react-router-dom";
30
+ import { useParams as Te } from "react-router-dom";
30
31
  import { jsx as A, jsxs as j } from "react/jsx-runtime";
31
- import { EmphasisPanel as Te, NextSteps as Ee, PagePurpose as De } from "@burdenoff/fe-libs/ui";
32
+ import { EmphasisPanel as Ee, NextSteps as De, PagePurpose as Oe } from "@burdenoff/fe-libs/ui";
32
33
  //#region src/movethewheels/pages/OrderDetailsPage.tsx
33
34
  var M = [
34
35
  "pending",
@@ -49,19 +50,19 @@ var M = [
49
50
  cancelled: "Cancelled",
50
51
  failed: "Failed"
51
52
  };
52
- function Oe(e) {
53
+ function ke(e) {
53
54
  let t = M.indexOf(e);
54
55
  return t === -1 || t >= M.length - 1 ? null : M[t + 1];
55
56
  }
56
57
  function P() {
57
- let { orderId: M } = we(), { basePath: P, navigate: F } = le(), { order: I, isLoading: ke, error: Ae, refetch: L } = ue(M), { updateOrder: je, updateOrderStatus: Me } = de(), { vehicles: Ne, drivers: Pe } = fe(), { message: R, notify: z } = T(), [B, V] = k(""), [H, U] = k(""), [W, G] = k(!1), [K, q] = k(!1), [J, Y] = k(null), X = Ne.filter((e) => e.status === "available" || e.id === I?.vehicleId), Z = Pe.filter((e) => e.status === "available" || e.id === I?.driverId), Fe = async () => {
58
+ let { orderId: M } = Te(), { basePath: P, navigate: F } = le(), { order: I, isLoading: Ae, error: je, refetch: L } = ue(M), { updateOrder: Me, updateOrderStatus: Ne } = de(), { vehicles: Pe, drivers: Fe } = fe(), { message: R, notify: z } = T(), [B, V] = k(""), [H, U] = k(""), [W, G] = k(!1), [K, q] = k(!1), [J, Y] = k(null), X = Pe.filter((e) => e.status === "available" || e.id === I?.vehicleId), Z = Fe.filter((e) => e.status === "available" || e.id === I?.driverId), Ie = async () => {
58
59
  if (!M || !B && !H) {
59
60
  Y("Pick a vehicle and/or a driver to assign.");
60
61
  return;
61
62
  }
62
63
  G(!0), Y(null);
63
64
  try {
64
- await je(M, {
65
+ await Me(M, {
65
66
  vehicleId: B || void 0,
66
67
  driverId: H || void 0
67
68
  }), z("Assignment updated."), V(""), U(""), L();
@@ -74,7 +75,7 @@ function P() {
74
75
  if (M) {
75
76
  q(!0), Y(null);
76
77
  try {
77
- await Me(M, e), z(t), L();
78
+ await Ne(M, e), z(t), L();
78
79
  } catch (e) {
79
80
  Y(e instanceof Error ? e.message : "Failed to update status.");
80
81
  } finally {
@@ -84,7 +85,7 @@ function P() {
84
85
  }, $ = (e) => {
85
86
  F && F(s(P, e));
86
87
  };
87
- return ke ? /* @__PURE__ */ A(_e, { message: "Loading order details..." }) : Ae || !I ? /* @__PURE__ */ A(ge, {
88
+ return Ae ? /* @__PURE__ */ A(_e, { message: "Loading order details..." }) : je || !I ? /* @__PURE__ */ A(ge, {
88
89
  title: "Order not found",
89
90
  message: "The order you're looking for doesn't exist or has been removed.",
90
91
  onRetry: L
@@ -124,11 +125,11 @@ function P() {
124
125
  ]
125
126
  }),
126
127
  children: [
127
- /* @__PURE__ */ A(De, {
128
+ /* @__PURE__ */ A(Oe, {
128
129
  icon: /* @__PURE__ */ A(i, { size: 18 }),
129
130
  children: "Everything about this one delivery in a single place — who it's for, where it's going, what's inside, the running cost and the full status history. Use this page to review the order, hand it to a driver, print the docket, or jump into live tracking."
130
131
  }),
131
- /* @__PURE__ */ A(Ee, {
132
+ /* @__PURE__ */ A(De, {
132
133
  storageKey: "movethewheels-order-details",
133
134
  steps: [
134
135
  {
@@ -358,7 +359,7 @@ function P() {
358
359
  }), /* @__PURE__ */ j("div", {
359
360
  className: "space-y-6",
360
361
  children: [
361
- /* @__PURE__ */ j(Te, { children: [/* @__PURE__ */ j("h2", {
362
+ /* @__PURE__ */ j(Ee, { children: [/* @__PURE__ */ j("h2", {
362
363
  className: "flex items-center gap-2 text-base font-semibold text-text-primary mb-4",
363
364
  children: [/* @__PURE__ */ A(ne, { size: 18 }), "Order Summary"]
364
365
  }), /* @__PURE__ */ j("div", {
@@ -492,7 +493,7 @@ function P() {
492
493
  }),
493
494
  /* @__PURE__ */ j(l, {
494
495
  className: "w-full",
495
- onClick: Fe,
496
+ onClick: Ie,
496
497
  disabled: W || !B && !H,
497
498
  children: [/* @__PURE__ */ A(se, { size: 16 }), W ? "Assigning…" : "Assign driver & vehicle"]
498
499
  })
@@ -514,7 +515,7 @@ function P() {
514
515
  ]
515
516
  }),
516
517
  (() => {
517
- let e = Oe(I.status);
518
+ let e = ke(I.status);
518
519
  return e ? /* @__PURE__ */ j(l, {
519
520
  variant: "outline",
520
521
  className: "w-full",
@@ -544,6 +545,7 @@ function P() {
544
545
  entityType: "order",
545
546
  entityId: I.id
546
547
  }),
548
+ /* @__PURE__ */ A(we, { deliveryJobId: I.id }),
547
549
  I.specialInstructions && /* @__PURE__ */ j(u, { children: [/* @__PURE__ */ A(f, { children: /* @__PURE__ */ j(p, {
548
550
  className: "flex items-center gap-2",
549
551
  children: [/* @__PURE__ */ A(re, { size: 18 }), "Special Instructions"]