@burdenoff/microfe-movethewheels 2026.920.2 → 2026.920.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/components/CustomerPicker.js +260 -0
- package/dist/movethewheels/components/CustomerPicker.d.ts +17 -0
- package/dist/movethewheels/utils/navigation.d.ts +14 -0
- package/dist/pages/CreateOrderPage.js +221 -216
- package/dist/pages/CustomerDetailsPage.js +220 -220
- package/dist/utils/navigation.js +22 -1
- package/package.json +1 -1
- package/src/movethewheels/components/CustomerPicker.tsx +307 -0
- package/src/movethewheels/pages/CreateOrderPage.tsx +84 -31
- package/src/movethewheels/pages/CustomerDetailsPage.tsx +15 -6
- package/src/movethewheels/utils/navigation.ts +32 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { Plus as e } from "../node_modules/lucide-react/dist/esm/icons/plus.js";
|
|
2
|
+
import { RefreshCw as t } from "../node_modules/lucide-react/dist/esm/icons/refresh-cw.js";
|
|
3
|
+
import { UserPlus as n } from "../node_modules/lucide-react/dist/esm/icons/user-plus.js";
|
|
4
|
+
import { Button as r, Dialog as i, DialogContent as a, DialogDescription as o, DialogFooter as s, DialogHeader as c, DialogTitle as l, Input as u, Label as d, Select as f, SelectContent as p, SelectItem as m, SelectTrigger as h, SelectValue as g } from "./ui.js";
|
|
5
|
+
import { FormError as _ } from "./FormError.js";
|
|
6
|
+
import { useState as v } from "react";
|
|
7
|
+
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
8
|
+
//#region src/movethewheels/components/CustomerPicker.tsx
|
|
9
|
+
var x = {
|
|
10
|
+
name: "",
|
|
11
|
+
type: "b2c",
|
|
12
|
+
email: "",
|
|
13
|
+
phone: "",
|
|
14
|
+
company: ""
|
|
15
|
+
};
|
|
16
|
+
function S(e, t) {
|
|
17
|
+
return !t || e.some((e) => e.id === t.id) ? e : [t, ...e];
|
|
18
|
+
}
|
|
19
|
+
function C({ customers: C, value: w, onValueChange: T, onCreated: E, createCustomer: D, isLoading: O, error: k, onRetry: A, onOpenCustomers: j, selectedCustomer: M, disabled: N = !1 }) {
|
|
20
|
+
let [P, F] = v(!1), [I, L] = v(x), [R, z] = v(!1), [B, V] = v(null), H = S(C, M), U = () => {
|
|
21
|
+
V(null), L(x), F(!0);
|
|
22
|
+
}, W = () => {
|
|
23
|
+
R || F(!1);
|
|
24
|
+
};
|
|
25
|
+
return /* @__PURE__ */ b("div", {
|
|
26
|
+
className: "space-y-2",
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ b("div", {
|
|
29
|
+
className: "flex items-center justify-between gap-2",
|
|
30
|
+
children: [/* @__PURE__ */ y(d, {
|
|
31
|
+
htmlFor: "customer",
|
|
32
|
+
children: "Customer"
|
|
33
|
+
}), /* @__PURE__ */ b(r, {
|
|
34
|
+
type: "button",
|
|
35
|
+
variant: "ghost",
|
|
36
|
+
size: "sm",
|
|
37
|
+
onClick: U,
|
|
38
|
+
disabled: N || O,
|
|
39
|
+
children: [/* @__PURE__ */ y(e, {
|
|
40
|
+
size: 14,
|
|
41
|
+
className: "mr-1"
|
|
42
|
+
}), "Add customer"]
|
|
43
|
+
})]
|
|
44
|
+
}),
|
|
45
|
+
/* @__PURE__ */ b("div", {
|
|
46
|
+
className: "flex flex-col gap-2 sm:flex-row sm:items-start",
|
|
47
|
+
children: [/* @__PURE__ */ b(f, {
|
|
48
|
+
value: w,
|
|
49
|
+
onValueChange: T,
|
|
50
|
+
disabled: N || O || !!k,
|
|
51
|
+
children: [/* @__PURE__ */ y(h, {
|
|
52
|
+
id: "customer",
|
|
53
|
+
className: "flex-1",
|
|
54
|
+
children: /* @__PURE__ */ y(g, { placeholder: O ? "Loading customers..." : "Select customer" })
|
|
55
|
+
}), /* @__PURE__ */ y(p, { children: H.map((e) => /* @__PURE__ */ b(m, {
|
|
56
|
+
value: e.id,
|
|
57
|
+
children: [
|
|
58
|
+
e.name,
|
|
59
|
+
" ",
|
|
60
|
+
e.email ? `(${e.email})` : ""
|
|
61
|
+
]
|
|
62
|
+
}, e.id)) })]
|
|
63
|
+
}), k && /* @__PURE__ */ b(r, {
|
|
64
|
+
type: "button",
|
|
65
|
+
variant: "outline",
|
|
66
|
+
size: "sm",
|
|
67
|
+
onClick: A,
|
|
68
|
+
children: [/* @__PURE__ */ y(t, {
|
|
69
|
+
size: 14,
|
|
70
|
+
className: "mr-1"
|
|
71
|
+
}), "Retry"]
|
|
72
|
+
})]
|
|
73
|
+
}),
|
|
74
|
+
k ? /* @__PURE__ */ b("div", {
|
|
75
|
+
className: "flex flex-wrap items-center gap-2",
|
|
76
|
+
children: [/* @__PURE__ */ y("p", {
|
|
77
|
+
role: "alert",
|
|
78
|
+
className: "text-sm text-status-error-text",
|
|
79
|
+
children: "Customers could not be loaded. Retry or add a customer to continue."
|
|
80
|
+
}), j && /* @__PURE__ */ y(r, {
|
|
81
|
+
type: "button",
|
|
82
|
+
variant: "outline",
|
|
83
|
+
size: "sm",
|
|
84
|
+
onClick: j,
|
|
85
|
+
children: "Open customer page"
|
|
86
|
+
})]
|
|
87
|
+
}) : !O && H.length === 0 ? /* @__PURE__ */ b("div", {
|
|
88
|
+
className: "rounded-lg border border-border-seam bg-bg-sunken/40 p-3",
|
|
89
|
+
children: [/* @__PURE__ */ b("div", {
|
|
90
|
+
className: "flex items-start gap-2",
|
|
91
|
+
children: [/* @__PURE__ */ y(n, {
|
|
92
|
+
size: 16,
|
|
93
|
+
className: "mt-0.5 shrink-0 text-text-secondary"
|
|
94
|
+
}), /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("p", {
|
|
95
|
+
className: "text-sm font-medium text-text-primary",
|
|
96
|
+
children: "Add a customer to continue"
|
|
97
|
+
}), /* @__PURE__ */ y("p", {
|
|
98
|
+
className: "text-sm text-text-secondary",
|
|
99
|
+
children: "The customer will be selected automatically and your order details will stay here."
|
|
100
|
+
})] })]
|
|
101
|
+
}), /* @__PURE__ */ b(r, {
|
|
102
|
+
type: "button",
|
|
103
|
+
size: "sm",
|
|
104
|
+
className: "mt-3",
|
|
105
|
+
onClick: U,
|
|
106
|
+
disabled: N,
|
|
107
|
+
children: [/* @__PURE__ */ y(e, {
|
|
108
|
+
size: 14,
|
|
109
|
+
className: "mr-1.5"
|
|
110
|
+
}), "Create customer"]
|
|
111
|
+
})]
|
|
112
|
+
}) : null,
|
|
113
|
+
/* @__PURE__ */ y(i, {
|
|
114
|
+
open: P,
|
|
115
|
+
onOpenChange: (e) => e ? F(!0) : W(),
|
|
116
|
+
children: /* @__PURE__ */ b(a, { children: [/* @__PURE__ */ b(c, { children: [/* @__PURE__ */ y(l, { children: "Create customer" }), /* @__PURE__ */ y(o, { children: "Add the customer without leaving this order. They will be selected automatically." })] }), /* @__PURE__ */ b("form", {
|
|
117
|
+
onSubmit: async (e) => {
|
|
118
|
+
e.preventDefault(), e.stopPropagation();
|
|
119
|
+
let t = I.name.trim();
|
|
120
|
+
if (!t) {
|
|
121
|
+
V("Customer name is required.");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
z(!0), V(null);
|
|
125
|
+
try {
|
|
126
|
+
E(await D({
|
|
127
|
+
name: t,
|
|
128
|
+
type: I.type,
|
|
129
|
+
email: I.email.trim() || void 0,
|
|
130
|
+
phone: I.phone.trim() || void 0,
|
|
131
|
+
company: I.company.trim() || void 0
|
|
132
|
+
})), F(!1), L(x);
|
|
133
|
+
} catch (e) {
|
|
134
|
+
V(e instanceof Error ? e.message : "Could not create the customer. Please try again.");
|
|
135
|
+
} finally {
|
|
136
|
+
z(!1);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
className: "space-y-4",
|
|
140
|
+
children: [
|
|
141
|
+
/* @__PURE__ */ y(_, { message: B }),
|
|
142
|
+
/* @__PURE__ */ b("div", {
|
|
143
|
+
className: "space-y-2",
|
|
144
|
+
children: [/* @__PURE__ */ y(d, {
|
|
145
|
+
htmlFor: "quick-customer-name",
|
|
146
|
+
children: "Name"
|
|
147
|
+
}), /* @__PURE__ */ y(u, {
|
|
148
|
+
id: "quick-customer-name",
|
|
149
|
+
value: I.name,
|
|
150
|
+
onChange: (e) => L((t) => ({
|
|
151
|
+
...t,
|
|
152
|
+
name: e.target.value
|
|
153
|
+
})),
|
|
154
|
+
placeholder: "Acme Corp or Jane Doe",
|
|
155
|
+
autoFocus: !0,
|
|
156
|
+
required: !0
|
|
157
|
+
})]
|
|
158
|
+
}),
|
|
159
|
+
/* @__PURE__ */ b("div", {
|
|
160
|
+
className: "space-y-2",
|
|
161
|
+
children: [/* @__PURE__ */ y(d, {
|
|
162
|
+
htmlFor: "quick-customer-type",
|
|
163
|
+
children: "Customer type"
|
|
164
|
+
}), /* @__PURE__ */ b(f, {
|
|
165
|
+
value: I.type,
|
|
166
|
+
onValueChange: (e) => L((t) => ({
|
|
167
|
+
...t,
|
|
168
|
+
type: e
|
|
169
|
+
})),
|
|
170
|
+
children: [/* @__PURE__ */ y(h, {
|
|
171
|
+
id: "quick-customer-type",
|
|
172
|
+
children: /* @__PURE__ */ y(g, {})
|
|
173
|
+
}), /* @__PURE__ */ b(p, { children: [/* @__PURE__ */ y(m, {
|
|
174
|
+
value: "b2c",
|
|
175
|
+
children: "B2C (Individual)"
|
|
176
|
+
}), /* @__PURE__ */ y(m, {
|
|
177
|
+
value: "b2b",
|
|
178
|
+
children: "B2B (Business)"
|
|
179
|
+
})] })]
|
|
180
|
+
})]
|
|
181
|
+
}),
|
|
182
|
+
/* @__PURE__ */ b("div", {
|
|
183
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
184
|
+
children: [/* @__PURE__ */ b("div", {
|
|
185
|
+
className: "space-y-2",
|
|
186
|
+
children: [/* @__PURE__ */ y(d, {
|
|
187
|
+
htmlFor: "quick-customer-email",
|
|
188
|
+
children: "Email"
|
|
189
|
+
}), /* @__PURE__ */ y(u, {
|
|
190
|
+
id: "quick-customer-email",
|
|
191
|
+
type: "email",
|
|
192
|
+
value: I.email,
|
|
193
|
+
onChange: (e) => L((t) => ({
|
|
194
|
+
...t,
|
|
195
|
+
email: e.target.value
|
|
196
|
+
})),
|
|
197
|
+
placeholder: "customer@example.com"
|
|
198
|
+
})]
|
|
199
|
+
}), /* @__PURE__ */ b("div", {
|
|
200
|
+
className: "space-y-2",
|
|
201
|
+
children: [/* @__PURE__ */ y(d, {
|
|
202
|
+
htmlFor: "quick-customer-phone",
|
|
203
|
+
children: "Phone"
|
|
204
|
+
}), /* @__PURE__ */ y(u, {
|
|
205
|
+
id: "quick-customer-phone",
|
|
206
|
+
value: I.phone,
|
|
207
|
+
onChange: (e) => L((t) => ({
|
|
208
|
+
...t,
|
|
209
|
+
phone: e.target.value
|
|
210
|
+
})),
|
|
211
|
+
placeholder: "+1-555-0100"
|
|
212
|
+
})]
|
|
213
|
+
})]
|
|
214
|
+
}),
|
|
215
|
+
/* @__PURE__ */ b("div", {
|
|
216
|
+
className: "space-y-2",
|
|
217
|
+
children: [/* @__PURE__ */ y(d, {
|
|
218
|
+
htmlFor: "quick-customer-company",
|
|
219
|
+
children: "Company"
|
|
220
|
+
}), /* @__PURE__ */ y(u, {
|
|
221
|
+
id: "quick-customer-company",
|
|
222
|
+
value: I.company,
|
|
223
|
+
onChange: (e) => L((t) => ({
|
|
224
|
+
...t,
|
|
225
|
+
company: e.target.value
|
|
226
|
+
})),
|
|
227
|
+
placeholder: "Company name (optional)"
|
|
228
|
+
})]
|
|
229
|
+
}),
|
|
230
|
+
/* @__PURE__ */ b(s, { children: [
|
|
231
|
+
j && /* @__PURE__ */ y(r, {
|
|
232
|
+
type: "button",
|
|
233
|
+
variant: "outline",
|
|
234
|
+
onClick: () => {
|
|
235
|
+
W(), j();
|
|
236
|
+
},
|
|
237
|
+
disabled: R,
|
|
238
|
+
children: "Use customer page"
|
|
239
|
+
}),
|
|
240
|
+
/* @__PURE__ */ y(r, {
|
|
241
|
+
type: "button",
|
|
242
|
+
variant: "outline",
|
|
243
|
+
onClick: W,
|
|
244
|
+
disabled: R,
|
|
245
|
+
children: "Cancel"
|
|
246
|
+
}),
|
|
247
|
+
/* @__PURE__ */ b(r, {
|
|
248
|
+
type: "submit",
|
|
249
|
+
disabled: R,
|
|
250
|
+
children: [/* @__PURE__ */ y(e, { size: 16 }), R ? "Creating..." : "Create and select"]
|
|
251
|
+
})
|
|
252
|
+
] })
|
|
253
|
+
]
|
|
254
|
+
})] })
|
|
255
|
+
})
|
|
256
|
+
]
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
//#endregion
|
|
260
|
+
export { C as CustomerPicker };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Customer } from '../types';
|
|
2
|
+
|
|
3
|
+
interface CustomerPickerProps {
|
|
4
|
+
customers: Customer[];
|
|
5
|
+
value: string;
|
|
6
|
+
onValueChange: (value: string) => void;
|
|
7
|
+
onCreated: (customer: Customer) => void;
|
|
8
|
+
createCustomer: (customer: Partial<Customer>) => Promise<Customer>;
|
|
9
|
+
isLoading: boolean;
|
|
10
|
+
error: Error | null;
|
|
11
|
+
onRetry: () => void;
|
|
12
|
+
onOpenCustomers?: () => void;
|
|
13
|
+
selectedCustomer?: Customer | null;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function CustomerPicker({ customers, value, onValueChange, onCreated, createCustomer, isLoading, error, onRetry, onOpenCustomers, selectedCustomer, disabled, }: CustomerPickerProps): import("react").JSX.Element;
|
|
17
|
+
export {};
|
|
@@ -21,3 +21,17 @@ export declare function buildUrl(base: string, params?: Record<string, string |
|
|
|
21
21
|
* Parse query parameters from a URL.
|
|
22
22
|
*/
|
|
23
23
|
export declare function parseQueryParams(search: string): Record<string, string>;
|
|
24
|
+
/**
|
|
25
|
+
* Return only an internal order destination for customer create-and-return flows.
|
|
26
|
+
* Query strings are intentionally discarded so callers cannot smuggle a second
|
|
27
|
+
* return target or an open redirect through the customer form.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getSafeCustomerReturnPath(value: string | null | undefined): string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Build an order route with a customer preselected after a customer is created.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildOrderCustomerPath(orderPath: string, customerId: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Build the local customer creation route while preserving a safe order return path.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildCustomerCreatePath(orderPath: string): string;
|