@burdenoff/microfe-movethewheels 2026.510.105

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 (208) hide show
  1. package/README.md +82 -0
  2. package/dist/AIAssistantPage-hD0VYJdH.js +210 -0
  3. package/dist/AnalyticsPage-DHTHCUtr.js +201 -0
  4. package/dist/CreateOrderPage-Cprg4Y9V.js +471 -0
  5. package/dist/CustomerDetailsPage-DNDEw7IW.js +239 -0
  6. package/dist/CustomersPage-CDjjeCEL.js +119 -0
  7. package/dist/DashboardPage-8iTPXRAG.js +374 -0
  8. package/dist/DataTable-CRIKfdIN.js +239 -0
  9. package/dist/DriverDetailsPage-CRyRCno7.js +297 -0
  10. package/dist/DriversPage-16O8fVmf.js +127 -0
  11. package/dist/FinancePage-BYUxK5dR.js +154 -0
  12. package/dist/FleetPage-CHYETCWT.js +293 -0
  13. package/dist/ImportExportPage-C3MKKxfc.js +232 -0
  14. package/dist/InventoryPage--822AxZM.js +223 -0
  15. package/dist/LiveTrackingPage-Dp3rTJDr.js +332 -0
  16. package/dist/MarketplacePage-DjEqudfM.js +192 -0
  17. package/dist/MetricCard-GTbxAk1a.js +135 -0
  18. package/dist/OrderDetailsPage-BIuYG0ub.js +398 -0
  19. package/dist/OrdersListPage-CW5V0Uvh.js +257 -0
  20. package/dist/PageLayout-B7b0vl0R.js +1894 -0
  21. package/dist/ProductDetailsPage-Q3X7AT-7.js +168 -0
  22. package/dist/ProductsPage-CUj9JpnW.js +131 -0
  23. package/dist/ReportsPage-DblO5CdJ.js +227 -0
  24. package/dist/RouteDetailsPage-CLctgk6A.js +240 -0
  25. package/dist/RoutesPage-8hrv6RWT.js +116 -0
  26. package/dist/SettingsPage-BJ5BQeqn.js +247 -0
  27. package/dist/StatusBadge-BrrwraIA.js +206 -0
  28. package/dist/TrackingPage-BGqHDh-w.js +322 -0
  29. package/dist/VehicleDetailsPage-XnDH4iQR.js +194 -0
  30. package/dist/VehiclesPage-Cs4XxHkA.js +127 -0
  31. package/dist/WarehouseDetailsPage-GemdMvr_.js +215 -0
  32. package/dist/WarehousesPage-QTiuDuXy.js +121 -0
  33. package/dist/arrow-left-6CiLhqVp.js +11 -0
  34. package/dist/box-BunB_4UH.js +18 -0
  35. package/dist/chart-column-DWwVEVQ-.js +22 -0
  36. package/dist/chevron-right-DhZVf20o.js +8 -0
  37. package/dist/circle-alert-D5f6RZxt.js +26 -0
  38. package/dist/circle-check-big-D-JMHcTe.js +11 -0
  39. package/dist/clock-CvwBKbQP.js +13 -0
  40. package/dist/dev/main.d.ts +1 -0
  41. package/dist/dollar-sign-CP9qeU5d.js +14 -0
  42. package/dist/download-CIuG04pJ.js +21 -0
  43. package/dist/file-text-Dd_thxkn.js +26 -0
  44. package/dist/filter-DyRMX9CU.js +8 -0
  45. package/dist/formatters-_vJlC-47.js +50 -0
  46. package/dist/generated/global-operations.d.ts +1 -0
  47. package/dist/generated/global-types.d.ts +20715 -0
  48. package/dist/generated/wspace-operations.d.ts +3704 -0
  49. package/dist/generated/wspace-types.d.ts +53362 -0
  50. package/dist/graphqlClient-CdJyR_ed.js +55 -0
  51. package/dist/index.d.ts +4 -0
  52. package/dist/index.js +772 -0
  53. package/dist/map-BqH1cBJi.js +18 -0
  54. package/dist/map-pin-CFBOmh-A.js +13 -0
  55. package/dist/movethewheels/MoveTheWheelsRoot.d.ts +25 -0
  56. package/dist/movethewheels/MoveTheWheelsRoutes.d.ts +7 -0
  57. package/dist/movethewheels/components/DataTable.d.ts +32 -0
  58. package/dist/movethewheels/components/MetricCard.d.ts +43 -0
  59. package/dist/movethewheels/components/PageLayout.d.ts +68 -0
  60. package/dist/movethewheels/components/StatusBadge.d.ts +49 -0
  61. package/dist/movethewheels/components/index.d.ts +10 -0
  62. package/dist/movethewheels/components/ui.d.ts +22 -0
  63. package/dist/movethewheels/constants/index.d.ts +24 -0
  64. package/dist/movethewheels/constants/mockData.d.ts +33 -0
  65. package/dist/movethewheels/hooks/index.d.ts +12 -0
  66. package/dist/movethewheels/hooks/useAnalytics.d.ts +118 -0
  67. package/dist/movethewheels/hooks/useCustomers.d.ts +37 -0
  68. package/dist/movethewheels/hooks/useFleet.d.ts +71 -0
  69. package/dist/movethewheels/hooks/useInventory.d.ts +60 -0
  70. package/dist/movethewheels/hooks/useOrders.d.ts +47 -0
  71. package/dist/movethewheels/hooks/useRoutes.d.ts +41 -0
  72. package/dist/movethewheels/hooks/useTracking.d.ts +69 -0
  73. package/dist/movethewheels/index.d.ts +30 -0
  74. package/dist/movethewheels/pages/AIAssistantPage.d.ts +4 -0
  75. package/dist/movethewheels/pages/AnalyticsPage.d.ts +4 -0
  76. package/dist/movethewheels/pages/CreateOrderPage.d.ts +6 -0
  77. package/dist/movethewheels/pages/CustomerDetailsPage.d.ts +4 -0
  78. package/dist/movethewheels/pages/CustomersPage.d.ts +4 -0
  79. package/dist/movethewheels/pages/DashboardPage.d.ts +6 -0
  80. package/dist/movethewheels/pages/DriverDetailsPage.d.ts +4 -0
  81. package/dist/movethewheels/pages/DriversPage.d.ts +4 -0
  82. package/dist/movethewheels/pages/FinancePage.d.ts +4 -0
  83. package/dist/movethewheels/pages/FleetPage.d.ts +6 -0
  84. package/dist/movethewheels/pages/ImportExportPage.d.ts +4 -0
  85. package/dist/movethewheels/pages/InventoryPage.d.ts +4 -0
  86. package/dist/movethewheels/pages/LiveTrackingPage.d.ts +6 -0
  87. package/dist/movethewheels/pages/MarketplacePage.d.ts +4 -0
  88. package/dist/movethewheels/pages/OrderDetailsPage.d.ts +6 -0
  89. package/dist/movethewheels/pages/OrdersListPage.d.ts +6 -0
  90. package/dist/movethewheels/pages/ProductDetailsPage.d.ts +4 -0
  91. package/dist/movethewheels/pages/ProductsPage.d.ts +4 -0
  92. package/dist/movethewheels/pages/ReportsPage.d.ts +4 -0
  93. package/dist/movethewheels/pages/RouteDetailsPage.d.ts +4 -0
  94. package/dist/movethewheels/pages/RoutesPage.d.ts +4 -0
  95. package/dist/movethewheels/pages/SettingsPage.d.ts +4 -0
  96. package/dist/movethewheels/pages/TrackingPage.d.ts +6 -0
  97. package/dist/movethewheels/pages/VehicleDetailsPage.d.ts +4 -0
  98. package/dist/movethewheels/pages/VehiclesPage.d.ts +4 -0
  99. package/dist/movethewheels/pages/WarehouseDetailsPage.d.ts +4 -0
  100. package/dist/movethewheels/pages/WarehousesPage.d.ts +4 -0
  101. package/dist/movethewheels/providers/MoveTheWheelsProvider.d.ts +16 -0
  102. package/dist/movethewheels/store/movethewheelsStore.d.ts +73 -0
  103. package/dist/movethewheels/types/index.d.ts +655 -0
  104. package/dist/movethewheels/utils/cn.d.ts +6 -0
  105. package/dist/movethewheels/utils/formatters.d.ts +60 -0
  106. package/dist/movethewheels/utils/graphqlClient.d.ts +11 -0
  107. package/dist/movethewheels/utils/index.d.ts +7 -0
  108. package/dist/movethewheels/utils/navigation.d.ts +23 -0
  109. package/dist/navigation-BgnOfsVd.js +6 -0
  110. package/dist/navigation-C2fY_aS9.js +8 -0
  111. package/dist/package-DVZbDRcV.js +22 -0
  112. package/dist/phone-KdwpVmC4.js +18 -0
  113. package/dist/plus-Bl7uX6Ji.js +11 -0
  114. package/dist/refresh-cw-BYjl3K-8.js +22 -0
  115. package/dist/route-Ce_poKFi.js +51 -0
  116. package/dist/save-C-qDVat-.js +18 -0
  117. package/dist/search-5pdn5eOO.js +13 -0
  118. package/dist/settings-C4kIDsYg.js +28 -0
  119. package/dist/square-pen-BwQ67vLE.js +11 -0
  120. package/dist/star-BlVsC3Ad.js +8 -0
  121. package/dist/store-DTmQT5M0.js +26 -0
  122. package/dist/trending-up-C1faflCI.js +11 -0
  123. package/dist/triangle-alert-CUoVAA4L.js +18 -0
  124. package/dist/truck-BmDAzu05.js +30 -0
  125. package/dist/useAnalytics-ph7eTIK6.js +297 -0
  126. package/dist/useCustomers-bS3a4ytk.js +186 -0
  127. package/dist/useFleet-BdETplNE.js +398 -0
  128. package/dist/useInventory-Dwn18FPz.js +323 -0
  129. package/dist/useOrders-D_3_hGMp.js +324 -0
  130. package/dist/useRoutes-v4aBaS-E.js +224 -0
  131. package/dist/useTracking-De2KIUNu.js +261 -0
  132. package/dist/user-BplzDrLP.js +13 -0
  133. package/dist/users-i-igmsP4.js +24 -0
  134. package/dist/warehouse-DewG0PXh.js +25 -0
  135. package/dist/wrench-CoSDEIC7.js +31 -0
  136. package/package.json +107 -0
  137. package/src/dev/main.tsx +110 -0
  138. package/src/dev/styles.css +139 -0
  139. package/src/generated/global-operations.ts +2 -0
  140. package/src/generated/global-types.ts +24048 -0
  141. package/src/generated/wspace-operations.ts +3734 -0
  142. package/src/generated/wspace-types.ts +60715 -0
  143. package/src/index.ts +4 -0
  144. package/src/movethewheels/MoveTheWheelsRoot.tsx +258 -0
  145. package/src/movethewheels/MoveTheWheelsRoutes.tsx +119 -0
  146. package/src/movethewheels/components/DataTable.tsx +367 -0
  147. package/src/movethewheels/components/MetricCard.tsx +180 -0
  148. package/src/movethewheels/components/PageLayout.tsx +234 -0
  149. package/src/movethewheels/components/StatusBadge.tsx +243 -0
  150. package/src/movethewheels/components/index.ts +26 -0
  151. package/src/movethewheels/components/ui.tsx +124 -0
  152. package/src/movethewheels/constants/index.ts +65 -0
  153. package/src/movethewheels/constants/mockData.ts +1342 -0
  154. package/src/movethewheels/hooks/index.ts +55 -0
  155. package/src/movethewheels/hooks/useAnalytics.ts +476 -0
  156. package/src/movethewheels/hooks/useCustomers.ts +359 -0
  157. package/src/movethewheels/hooks/useFleet.ts +778 -0
  158. package/src/movethewheels/hooks/useInventory.ts +632 -0
  159. package/src/movethewheels/hooks/useOrders.ts +703 -0
  160. package/src/movethewheels/hooks/useRoutes.ts +453 -0
  161. package/src/movethewheels/hooks/useTracking.ts +505 -0
  162. package/src/movethewheels/index.ts +68 -0
  163. package/src/movethewheels/pages/AIAssistantPage.tsx +160 -0
  164. package/src/movethewheels/pages/AnalyticsPage.tsx +190 -0
  165. package/src/movethewheels/pages/CreateOrderPage.tsx +454 -0
  166. package/src/movethewheels/pages/CustomerDetailsPage.tsx +207 -0
  167. package/src/movethewheels/pages/CustomersPage.tsx +115 -0
  168. package/src/movethewheels/pages/DashboardPage.tsx +414 -0
  169. package/src/movethewheels/pages/DriverDetailsPage.tsx +261 -0
  170. package/src/movethewheels/pages/DriversPage.tsx +118 -0
  171. package/src/movethewheels/pages/FinancePage.tsx +141 -0
  172. package/src/movethewheels/pages/FleetPage.tsx +289 -0
  173. package/src/movethewheels/pages/ImportExportPage.tsx +165 -0
  174. package/src/movethewheels/pages/InventoryPage.tsx +212 -0
  175. package/src/movethewheels/pages/LiveTrackingPage.tsx +325 -0
  176. package/src/movethewheels/pages/MarketplacePage.tsx +235 -0
  177. package/src/movethewheels/pages/OrderDetailsPage.tsx +387 -0
  178. package/src/movethewheels/pages/OrdersListPage.tsx +241 -0
  179. package/src/movethewheels/pages/ProductDetailsPage.tsx +155 -0
  180. package/src/movethewheels/pages/ProductsPage.tsx +124 -0
  181. package/src/movethewheels/pages/ReportsPage.tsx +164 -0
  182. package/src/movethewheels/pages/RouteDetailsPage.tsx +245 -0
  183. package/src/movethewheels/pages/RoutesPage.tsx +104 -0
  184. package/src/movethewheels/pages/SettingsPage.tsx +242 -0
  185. package/src/movethewheels/pages/TrackingPage.tsx +419 -0
  186. package/src/movethewheels/pages/VehicleDetailsPage.tsx +218 -0
  187. package/src/movethewheels/pages/VehiclesPage.tsx +124 -0
  188. package/src/movethewheels/pages/WarehouseDetailsPage.tsx +216 -0
  189. package/src/movethewheels/pages/WarehousesPage.tsx +122 -0
  190. package/src/movethewheels/providers/MoveTheWheelsProvider.tsx +66 -0
  191. package/src/movethewheels/store/movethewheelsStore.ts +136 -0
  192. package/src/movethewheels/types/index.ts +744 -0
  193. package/src/movethewheels/utils/cn.ts +9 -0
  194. package/src/movethewheels/utils/formatters.ts +215 -0
  195. package/src/movethewheels/utils/graphqlClient.ts +63 -0
  196. package/src/movethewheels/utils/index.ts +8 -0
  197. package/src/movethewheels/utils/navigation.ts +70 -0
  198. package/src/operations/global/.gitkeep +0 -0
  199. package/src/operations/wspace/movethewheels/fragments/core.graphql +191 -0
  200. package/src/operations/wspace/movethewheels/mutations/entities.graphql +87 -0
  201. package/src/operations/wspace/movethewheels/mutations/logistics.graphql +86 -0
  202. package/src/operations/wspace/movethewheels/mutations/marketplace-reports.graphql +81 -0
  203. package/src/operations/wspace/movethewheels/mutations/orders.graphql +21 -0
  204. package/src/operations/wspace/movethewheels/queries/dashboard.graphql +61 -0
  205. package/src/operations/wspace/movethewheels/queries/entities.graphql +83 -0
  206. package/src/operations/wspace/movethewheels/queries/logistics.graphql +84 -0
  207. package/src/operations/wspace/movethewheels/queries/marketplace-reports.graphql +40 -0
  208. package/src/operations/wspace/movethewheels/queries/orders.graphql +43 -0
@@ -0,0 +1,454 @@
1
+ /**
2
+ * Create/Edit Order Page
3
+ *
4
+ * Form for creating or editing orders.
5
+ */
6
+
7
+ import { useState } from 'react';
8
+ import { useParams } from 'react-router-dom';
9
+ import { Package, ArrowLeft, Save, Plus, Trash2 } from 'lucide-react';
10
+ import { PageLayout } from '../components/PageLayout';
11
+ import {
12
+ Card,
13
+ CardContent,
14
+ CardHeader,
15
+ CardTitle,
16
+ Button,
17
+ Input,
18
+ Label,
19
+ Textarea,
20
+ Select,
21
+ SelectContent,
22
+ SelectItem,
23
+ SelectTrigger,
24
+ SelectValue,
25
+ Separator,
26
+ } from '../components/ui';
27
+ import { useOrder, useOrders } from '../hooks/useOrders';
28
+ import { useCustomers } from '../hooks/useCustomers';
29
+ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
30
+ import { joinPath } from '../utils/navigation';
31
+
32
+ export default function CreateOrderPage() {
33
+ const { orderId } = useParams<{ orderId: string }>();
34
+ const { basePath, navigate } = useMoveTheWheels();
35
+ const { order } = useOrder(orderId);
36
+ const { createOrder } = useOrders();
37
+ const { customers } = useCustomers({ limit: 100 });
38
+
39
+ const isEditing = !!orderId && !!order;
40
+
41
+ const [formData, setFormData] = useState({
42
+ customerId: order?.customerId || '',
43
+ orderType: order?.orderType || 'b2c',
44
+ priority: order?.priority || 'normal',
45
+ pickupAddress: {
46
+ street: order?.pickupAddress.street || '',
47
+ city: order?.pickupAddress.city || '',
48
+ state: order?.pickupAddress.state || '',
49
+ postalCode: order?.pickupAddress.postalCode || '',
50
+ country: order?.pickupAddress.country || 'USA',
51
+ },
52
+ deliveryAddress: {
53
+ street: order?.deliveryAddress.street || '',
54
+ city: order?.deliveryAddress.city || '',
55
+ state: order?.deliveryAddress.state || '',
56
+ postalCode: order?.deliveryAddress.postalCode || '',
57
+ country: order?.deliveryAddress.country || 'USA',
58
+ },
59
+ items: order?.items || [],
60
+ specialInstructions: order?.specialInstructions || '',
61
+ });
62
+
63
+ const [isSubmitting, setIsSubmitting] = useState(false);
64
+
65
+ const goBack = () => {
66
+ if (navigate) {
67
+ navigate(joinPath(basePath, 'orders'));
68
+ }
69
+ };
70
+
71
+ const handleSubmit = async (e: React.FormEvent) => {
72
+ e.preventDefault();
73
+ setIsSubmitting(true);
74
+
75
+ try {
76
+ const selectedCustomer = customers.find((c) => c.id === formData.customerId);
77
+
78
+ await createOrder({
79
+ customerId: formData.customerId,
80
+ customerInfo: selectedCustomer
81
+ ? {
82
+ name: selectedCustomer.name,
83
+ email: selectedCustomer.email,
84
+ phone: selectedCustomer.phone,
85
+ company: selectedCustomer.company,
86
+ }
87
+ : {
88
+ name: 'New Customer',
89
+ email: 'customer@example.com',
90
+ phone: '+1-555-0000',
91
+ },
92
+ orderType: formData.orderType as 'b2b' | 'b2c',
93
+ priority: formData.priority as 'low' | 'normal' | 'high' | 'urgent',
94
+ pickupAddress: formData.pickupAddress,
95
+ deliveryAddress: formData.deliveryAddress,
96
+ items: formData.items,
97
+ specialInstructions: formData.specialInstructions,
98
+ });
99
+
100
+ goBack();
101
+ } catch (error) {
102
+ console.error('Failed to create order:', error);
103
+ } finally {
104
+ setIsSubmitting(false);
105
+ }
106
+ };
107
+
108
+ const updateField = (field: string, value: unknown) => {
109
+ setFormData((prev) => ({ ...prev, [field]: value }));
110
+ };
111
+
112
+ const updateAddress = (
113
+ type: 'pickupAddress' | 'deliveryAddress',
114
+ field: string,
115
+ value: string
116
+ ) => {
117
+ setFormData((prev) => ({
118
+ ...prev,
119
+ [type]: { ...prev[type], [field]: value },
120
+ }));
121
+ };
122
+
123
+ const addItem = () => {
124
+ setFormData((prev) => ({
125
+ ...prev,
126
+ items: [
127
+ ...prev.items,
128
+ {
129
+ id: `item-${Date.now()}`,
130
+ productId: '',
131
+ name: '',
132
+ quantity: 1,
133
+ weight: 0,
134
+ dimensions: { length: 0, width: 0, height: 0, unit: 'cm' as const },
135
+ value: 0,
136
+ sku: '',
137
+ },
138
+ ],
139
+ }));
140
+ };
141
+
142
+ const removeItem = (index: number) => {
143
+ setFormData((prev) => ({
144
+ ...prev,
145
+ items: prev.items.filter((_, i) => i !== index),
146
+ }));
147
+ };
148
+
149
+ const updateItem = (index: number, field: string, value: unknown) => {
150
+ setFormData((prev) => ({
151
+ ...prev,
152
+ items: prev.items.map((item, i) => (i === index ? { ...item, [field]: value } : item)),
153
+ }));
154
+ };
155
+
156
+ return (
157
+ <PageLayout
158
+ title={isEditing ? 'Edit Order' : 'Create Order'}
159
+ subtitle={isEditing ? `Editing ${order?.orderNumber}` : 'Create a new delivery order'}
160
+ icon={<Package size={24} />}
161
+ headerActions={
162
+ <div className="flex gap-2">
163
+ <Button variant="outline" onClick={goBack}>
164
+ <ArrowLeft size={16} />
165
+ Cancel
166
+ </Button>
167
+ <Button onClick={handleSubmit} disabled={isSubmitting}>
168
+ <Save size={16} />
169
+ {isSubmitting ? 'Saving...' : isEditing ? 'Save Changes' : 'Create Order'}
170
+ </Button>
171
+ </div>
172
+ }
173
+ >
174
+ <form onSubmit={handleSubmit} className="space-y-6">
175
+ {/* Order Details */}
176
+ <Card>
177
+ <CardHeader>
178
+ <CardTitle>Order Details</CardTitle>
179
+ </CardHeader>
180
+ <CardContent className="space-y-4">
181
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
182
+ <div className="space-y-2">
183
+ <Label htmlFor="customer">Customer</Label>
184
+ <Select
185
+ value={formData.customerId}
186
+ onValueChange={(value) => updateField('customerId', value)}
187
+ >
188
+ <SelectTrigger id="customer">
189
+ <SelectValue placeholder="Select customer" />
190
+ </SelectTrigger>
191
+ <SelectContent>
192
+ {customers.map((customer) => (
193
+ <SelectItem key={customer.id} value={customer.id}>
194
+ {customer.name} ({customer.email})
195
+ </SelectItem>
196
+ ))}
197
+ </SelectContent>
198
+ </Select>
199
+ </div>
200
+ <div className="space-y-2">
201
+ <Label htmlFor="orderType">Order Type</Label>
202
+ <Select
203
+ value={formData.orderType}
204
+ onValueChange={(value) => updateField('orderType', value)}
205
+ >
206
+ <SelectTrigger id="orderType">
207
+ <SelectValue />
208
+ </SelectTrigger>
209
+ <SelectContent>
210
+ <SelectItem value="b2c">B2C (Individual)</SelectItem>
211
+ <SelectItem value="b2b">B2B (Business)</SelectItem>
212
+ </SelectContent>
213
+ </Select>
214
+ </div>
215
+ <div className="space-y-2">
216
+ <Label htmlFor="priority">Priority</Label>
217
+ <Select
218
+ value={formData.priority}
219
+ onValueChange={(value) => updateField('priority', value)}
220
+ >
221
+ <SelectTrigger id="priority">
222
+ <SelectValue />
223
+ </SelectTrigger>
224
+ <SelectContent>
225
+ <SelectItem value="low">Low</SelectItem>
226
+ <SelectItem value="normal">Normal</SelectItem>
227
+ <SelectItem value="high">High</SelectItem>
228
+ <SelectItem value="urgent">Urgent</SelectItem>
229
+ </SelectContent>
230
+ </Select>
231
+ </div>
232
+ </div>
233
+ </CardContent>
234
+ </Card>
235
+
236
+ {/* Addresses */}
237
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
238
+ {/* Pickup Address */}
239
+ <Card>
240
+ <CardHeader>
241
+ <CardTitle>Pickup Address</CardTitle>
242
+ </CardHeader>
243
+ <CardContent className="space-y-4">
244
+ <div className="space-y-2">
245
+ <Label htmlFor="pickupStreet">Street Address</Label>
246
+ <Input
247
+ id="pickupStreet"
248
+ value={formData.pickupAddress.street}
249
+ onChange={(e) => updateAddress('pickupAddress', 'street', e.target.value)}
250
+ placeholder="123 Main St"
251
+ />
252
+ </div>
253
+ <div className="grid grid-cols-2 gap-4">
254
+ <div className="space-y-2">
255
+ <Label htmlFor="pickupCity">City</Label>
256
+ <Input
257
+ id="pickupCity"
258
+ value={formData.pickupAddress.city}
259
+ onChange={(e) => updateAddress('pickupAddress', 'city', e.target.value)}
260
+ placeholder="San Francisco"
261
+ />
262
+ </div>
263
+ <div className="space-y-2">
264
+ <Label htmlFor="pickupState">State</Label>
265
+ <Input
266
+ id="pickupState"
267
+ value={formData.pickupAddress.state}
268
+ onChange={(e) => updateAddress('pickupAddress', 'state', e.target.value)}
269
+ placeholder="CA"
270
+ />
271
+ </div>
272
+ </div>
273
+ <div className="grid grid-cols-2 gap-4">
274
+ <div className="space-y-2">
275
+ <Label htmlFor="pickupPostal">Postal Code</Label>
276
+ <Input
277
+ id="pickupPostal"
278
+ value={formData.pickupAddress.postalCode}
279
+ onChange={(e) => updateAddress('pickupAddress', 'postalCode', e.target.value)}
280
+ placeholder="94103"
281
+ />
282
+ </div>
283
+ <div className="space-y-2">
284
+ <Label htmlFor="pickupCountry">Country</Label>
285
+ <Input
286
+ id="pickupCountry"
287
+ value={formData.pickupAddress.country}
288
+ onChange={(e) => updateAddress('pickupAddress', 'country', e.target.value)}
289
+ placeholder="USA"
290
+ />
291
+ </div>
292
+ </div>
293
+ </CardContent>
294
+ </Card>
295
+
296
+ {/* Delivery Address */}
297
+ <Card>
298
+ <CardHeader>
299
+ <CardTitle>Delivery Address</CardTitle>
300
+ </CardHeader>
301
+ <CardContent className="space-y-4">
302
+ <div className="space-y-2">
303
+ <Label htmlFor="deliveryStreet">Street Address</Label>
304
+ <Input
305
+ id="deliveryStreet"
306
+ value={formData.deliveryAddress.street}
307
+ onChange={(e) => updateAddress('deliveryAddress', 'street', e.target.value)}
308
+ placeholder="456 Oak Ave"
309
+ />
310
+ </div>
311
+ <div className="grid grid-cols-2 gap-4">
312
+ <div className="space-y-2">
313
+ <Label htmlFor="deliveryCity">City</Label>
314
+ <Input
315
+ id="deliveryCity"
316
+ value={formData.deliveryAddress.city}
317
+ onChange={(e) => updateAddress('deliveryAddress', 'city', e.target.value)}
318
+ placeholder="Los Angeles"
319
+ />
320
+ </div>
321
+ <div className="space-y-2">
322
+ <Label htmlFor="deliveryState">State</Label>
323
+ <Input
324
+ id="deliveryState"
325
+ value={formData.deliveryAddress.state}
326
+ onChange={(e) => updateAddress('deliveryAddress', 'state', e.target.value)}
327
+ placeholder="CA"
328
+ />
329
+ </div>
330
+ </div>
331
+ <div className="grid grid-cols-2 gap-4">
332
+ <div className="space-y-2">
333
+ <Label htmlFor="deliveryPostal">Postal Code</Label>
334
+ <Input
335
+ id="deliveryPostal"
336
+ value={formData.deliveryAddress.postalCode}
337
+ onChange={(e) => updateAddress('deliveryAddress', 'postalCode', e.target.value)}
338
+ placeholder="90210"
339
+ />
340
+ </div>
341
+ <div className="space-y-2">
342
+ <Label htmlFor="deliveryCountry">Country</Label>
343
+ <Input
344
+ id="deliveryCountry"
345
+ value={formData.deliveryAddress.country}
346
+ onChange={(e) => updateAddress('deliveryAddress', 'country', e.target.value)}
347
+ placeholder="USA"
348
+ />
349
+ </div>
350
+ </div>
351
+ </CardContent>
352
+ </Card>
353
+ </div>
354
+
355
+ {/* Order Items */}
356
+ <Card>
357
+ <CardHeader className="flex flex-row items-center justify-between">
358
+ <CardTitle>Order Items</CardTitle>
359
+ <Button type="button" variant="outline" size="sm" onClick={addItem}>
360
+ <Plus size={16} />
361
+ Add Item
362
+ </Button>
363
+ </CardHeader>
364
+ <CardContent>
365
+ {formData.items.length === 0 ? (
366
+ <div className="text-center py-8 text-muted-foreground">
367
+ <Package size={48} className="mx-auto mb-4 opacity-50" />
368
+ <p>No items added yet</p>
369
+ <Button type="button" variant="link" onClick={addItem}>
370
+ Add your first item
371
+ </Button>
372
+ </div>
373
+ ) : (
374
+ <div className="space-y-4">
375
+ {formData.items.map((item, index) => (
376
+ <div key={item.id}>
377
+ {index > 0 && <Separator className="my-4" />}
378
+ <div className="grid grid-cols-1 md:grid-cols-6 gap-4">
379
+ <div className="md:col-span-2 space-y-2">
380
+ <Label>Item Name</Label>
381
+ <Input
382
+ value={item.name}
383
+ onChange={(e) => updateItem(index, 'name', e.target.value)}
384
+ placeholder="Product name"
385
+ />
386
+ </div>
387
+ <div className="space-y-2">
388
+ <Label>SKU</Label>
389
+ <Input
390
+ value={item.sku}
391
+ onChange={(e) => updateItem(index, 'sku', e.target.value)}
392
+ placeholder="SKU-001"
393
+ />
394
+ </div>
395
+ <div className="space-y-2">
396
+ <Label>Quantity</Label>
397
+ <Input
398
+ type="number"
399
+ min="1"
400
+ value={item.quantity}
401
+ onChange={(e) =>
402
+ updateItem(index, 'quantity', parseInt(e.target.value) || 1)
403
+ }
404
+ />
405
+ </div>
406
+ <div className="space-y-2">
407
+ <Label>Value ($)</Label>
408
+ <Input
409
+ type="number"
410
+ min="0"
411
+ step="0.01"
412
+ value={item.value}
413
+ onChange={(e) =>
414
+ updateItem(index, 'value', parseFloat(e.target.value) || 0)
415
+ }
416
+ />
417
+ </div>
418
+ <div className="flex items-end">
419
+ <Button
420
+ type="button"
421
+ variant="ghost"
422
+ size="icon"
423
+ className="text-destructive"
424
+ onClick={() => removeItem(index)}
425
+ >
426
+ <Trash2 size={16} />
427
+ </Button>
428
+ </div>
429
+ </div>
430
+ </div>
431
+ ))}
432
+ </div>
433
+ )}
434
+ </CardContent>
435
+ </Card>
436
+
437
+ {/* Special Instructions */}
438
+ <Card>
439
+ <CardHeader>
440
+ <CardTitle>Special Instructions</CardTitle>
441
+ </CardHeader>
442
+ <CardContent>
443
+ <Textarea
444
+ value={formData.specialInstructions}
445
+ onChange={(e) => updateField('specialInstructions', e.target.value)}
446
+ placeholder="Any special handling or delivery instructions..."
447
+ rows={4}
448
+ />
449
+ </CardContent>
450
+ </Card>
451
+ </form>
452
+ </PageLayout>
453
+ );
454
+ }
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Customer Details Page
3
+ */
4
+
5
+ import { useParams } from 'react-router-dom';
6
+ import {
7
+ Users,
8
+ ArrowLeft,
9
+ Edit,
10
+ MapPin,
11
+ Mail,
12
+ Phone,
13
+ Star,
14
+ Building,
15
+ FileText,
16
+ } from 'lucide-react';
17
+ import { PageLayout, LoadingState, ErrorState } from '../components/PageLayout';
18
+ import { TypeBadge } from '../components/StatusBadge';
19
+ import { Card, CardContent, CardHeader, CardTitle, Button, Badge } from '../components/ui';
20
+ import { useCustomer } from '../hooks/useCustomers';
21
+ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
22
+ import { formatAddress, formatDate } from '../utils/formatters';
23
+ import { joinPath } from '../utils/navigation';
24
+
25
+ export default function CustomerDetailsPage() {
26
+ const { customerId } = useParams<{ customerId: string }>();
27
+ const { basePath, navigate } = useMoveTheWheels();
28
+ const { customer, isLoading, error, refetch } = useCustomer(customerId);
29
+
30
+ const goBack = () => navigate?.(joinPath(basePath, 'customers'));
31
+
32
+ // Handle new customer form
33
+ if (!customerId) {
34
+ return (
35
+ <PageLayout
36
+ title="New Customer"
37
+ subtitle="Add a new customer"
38
+ icon={<Users size={24} />}
39
+ headerActions={
40
+ <Button variant="outline" onClick={goBack}>
41
+ <ArrowLeft size={16} /> Back
42
+ </Button>
43
+ }
44
+ >
45
+ <Card>
46
+ <CardContent className="p-8 text-center">
47
+ <p className="text-muted-foreground">Customer creation form would go here</p>
48
+ </CardContent>
49
+ </Card>
50
+ </PageLayout>
51
+ );
52
+ }
53
+
54
+ if (isLoading) return <LoadingState message="Loading customer details..." />;
55
+ if (error || !customer) return <ErrorState title="Customer not found" onRetry={refetch} />;
56
+
57
+ return (
58
+ <PageLayout
59
+ title={customer.name}
60
+ subtitle={customer.company || customer.email}
61
+ icon={<Users size={24} />}
62
+ headerActions={
63
+ <div className="flex gap-2">
64
+ <Button variant="outline" onClick={goBack}>
65
+ <ArrowLeft size={16} /> Back
66
+ </Button>
67
+ <Button>
68
+ <Edit size={16} /> Edit
69
+ </Button>
70
+ </div>
71
+ }
72
+ headerContent={
73
+ <div className="flex items-center gap-4">
74
+ <TypeBadge type={customer.type} />
75
+ <Badge variant={customer.isActive ? 'default' : 'secondary'}>
76
+ {customer.isActive ? 'Active' : 'Inactive'}
77
+ </Badge>
78
+ <div className="flex items-center gap-1">
79
+ <Star size={16} className="text-yellow-500 fill-yellow-500" />
80
+ <span className="font-medium">{customer.rating.toFixed(1)}</span>
81
+ </div>
82
+ </div>
83
+ }
84
+ >
85
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
86
+ <div className="lg:col-span-2 space-y-6">
87
+ <Card>
88
+ <CardHeader>
89
+ <CardTitle>Contact Information</CardTitle>
90
+ </CardHeader>
91
+ <CardContent>
92
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
93
+ <div className="space-y-4">
94
+ <div className="flex items-center gap-3">
95
+ <Mail size={18} className="text-muted-foreground" />
96
+ <div>
97
+ <p className="text-sm text-muted-foreground">Email</p>
98
+ <p className="font-medium">{customer.email}</p>
99
+ </div>
100
+ </div>
101
+ <div className="flex items-center gap-3">
102
+ <Phone size={18} className="text-muted-foreground" />
103
+ <div>
104
+ <p className="text-sm text-muted-foreground">Phone</p>
105
+ <p className="font-medium">{customer.phone}</p>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ <div className="space-y-4">
110
+ {customer.company && (
111
+ <div className="flex items-center gap-3">
112
+ <Building size={18} className="text-muted-foreground" />
113
+ <div>
114
+ <p className="text-sm text-muted-foreground">Company</p>
115
+ <p className="font-medium">{customer.company}</p>
116
+ </div>
117
+ </div>
118
+ )}
119
+ {customer.website && (
120
+ <div>
121
+ <p className="text-sm text-muted-foreground">Website</p>
122
+ <a href={customer.website} className="text-primary hover:underline">
123
+ {customer.website}
124
+ </a>
125
+ </div>
126
+ )}
127
+ </div>
128
+ </div>
129
+ </CardContent>
130
+ </Card>
131
+
132
+ <Card>
133
+ <CardHeader>
134
+ <CardTitle className="flex items-center gap-2">
135
+ <MapPin size={18} /> Address
136
+ </CardTitle>
137
+ </CardHeader>
138
+ <CardContent>
139
+ <p>{formatAddress(customer.address)}</p>
140
+ </CardContent>
141
+ </Card>
142
+
143
+ {customer.notes && (
144
+ <Card>
145
+ <CardHeader>
146
+ <CardTitle className="flex items-center gap-2">
147
+ <FileText size={18} /> Notes
148
+ </CardTitle>
149
+ </CardHeader>
150
+ <CardContent>
151
+ <p>{customer.notes}</p>
152
+ </CardContent>
153
+ </Card>
154
+ )}
155
+ </div>
156
+
157
+ <div className="space-y-6">
158
+ <Card>
159
+ <CardHeader>
160
+ <CardTitle>Business Details</CardTitle>
161
+ </CardHeader>
162
+ <CardContent className="space-y-3">
163
+ {customer.businessLicense && (
164
+ <div>
165
+ <p className="text-sm text-muted-foreground">Business License</p>
166
+ <p className="font-mono">{customer.businessLicense}</p>
167
+ </div>
168
+ )}
169
+ {customer.taxId && (
170
+ <div>
171
+ <p className="text-sm text-muted-foreground">Tax ID</p>
172
+ <p className="font-mono">{customer.taxId}</p>
173
+ </div>
174
+ )}
175
+ <div>
176
+ <p className="text-sm text-muted-foreground">Preferred Contact</p>
177
+ <p className="capitalize">{customer.preferredContactMethod}</p>
178
+ </div>
179
+ <div>
180
+ <p className="text-sm text-muted-foreground">Marketing Opt-in</p>
181
+ <Badge variant={customer.marketingOptIn ? 'default' : 'secondary'}>
182
+ {customer.marketingOptIn ? 'Yes' : 'No'}
183
+ </Badge>
184
+ </div>
185
+ </CardContent>
186
+ </Card>
187
+
188
+ <Card>
189
+ <CardHeader>
190
+ <CardTitle>Account Info</CardTitle>
191
+ </CardHeader>
192
+ <CardContent className="space-y-3">
193
+ <div>
194
+ <p className="text-sm text-muted-foreground">Created</p>
195
+ <p>{formatDate(customer.createdAt, 'full')}</p>
196
+ </div>
197
+ <div>
198
+ <p className="text-sm text-muted-foreground">Last Updated</p>
199
+ <p>{formatDate(customer.updatedAt, 'relative')}</p>
200
+ </div>
201
+ </CardContent>
202
+ </Card>
203
+ </div>
204
+ </div>
205
+ </PageLayout>
206
+ );
207
+ }