@lodashventure/medusa-booking-for-pickup 1.4.14 → 1.4.16

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,892 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { useState, useEffect, useMemo, useCallback } from "react";
3
+ import { Calendar, Package, ShoppingBag, Clock, MapPin, RefreshCw, ArrowLeft, Trash2 } from "lucide-react";
4
+ import { defineWidgetConfig as defineWidgetConfig$1, defineRouteConfig, useSdk } from "@medusajs/admin-sdk";
5
+ import { Container as Container$1, Heading as Heading$1, Text as Text$1, Badge as Badge$1, Button as Button$1, toast, createDataTableColumnHelper, useDataTable, DataTable, Select, clx, Switch, IconButton } from "@medusajs/ui";
6
+ import { StackPerspective, Plus } from "@medusajs/icons";
7
+ import { useNavigate, useParams, Navigate } from "react-router-dom";
8
+ import Medusa from "@medusajs/js-sdk";
9
+ import "@medusajs/admin-shared";
10
+ const CustomerPickupHistoryWidget = ({ data }) => {
11
+ const [pickupHistory, setPickupHistory] = useState([]);
12
+ const [loading, setLoading] = useState(true);
13
+ const [error, setError] = useState(null);
14
+ const customer = data;
15
+ useEffect(() => {
16
+ fetchPickupHistory();
17
+ }, [customer.id]);
18
+ const fetchPickupHistory = async () => {
19
+ setLoading(true);
20
+ setError(null);
21
+ try {
22
+ const response = await fetch(
23
+ `/admin/customers/${customer.id}/pickup-history`,
24
+ {
25
+ credentials: "include"
26
+ }
27
+ );
28
+ if (!response.ok) {
29
+ throw new Error("Failed to fetch pickup history");
30
+ }
31
+ const data2 = await response.json();
32
+ setPickupHistory(data2.pickup_history || []);
33
+ } catch (err) {
34
+ console.error("Error fetching pickup history:", err);
35
+ setError(err.message);
36
+ } finally {
37
+ setLoading(false);
38
+ }
39
+ };
40
+ const formatDate = (dateString) => {
41
+ const date = new Date(dateString);
42
+ return new Intl.DateTimeFormat("th-TH", {
43
+ year: "numeric",
44
+ month: "short",
45
+ day: "numeric",
46
+ hour: "2-digit",
47
+ minute: "2-digit"
48
+ }).format(date);
49
+ };
50
+ const formatCurrency = (amount, currency) => {
51
+ return new Intl.NumberFormat("th-TH", {
52
+ style: "currency",
53
+ currency: currency.toUpperCase()
54
+ }).format(amount / 100);
55
+ };
56
+ const getStatusBadge = (status) => {
57
+ const statusColors = {
58
+ completed: "green",
59
+ pending: "orange",
60
+ canceled: "red",
61
+ processing: "blue"
62
+ };
63
+ const statusLabels = {
64
+ completed: "เสร็จสิ้น",
65
+ pending: "รอดำเนินการ",
66
+ canceled: "ยกเลิก",
67
+ processing: "กำลังดำเนินการ"
68
+ };
69
+ return /* @__PURE__ */ jsx(Badge, { color: statusColors[status] || "grey", size: "small", children: statusLabels[status] || status });
70
+ };
71
+ if (loading) {
72
+ return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
73
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
74
+ /* @__PURE__ */ jsx(Calendar, { className: "text-ui-fg-subtle" }),
75
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "ประวัติการจองรับสินค้า" })
76
+ ] }) }),
77
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "กำลังโหลด..." }) })
78
+ ] });
79
+ }
80
+ if (error) {
81
+ return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
82
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
83
+ /* @__PURE__ */ jsx(Calendar, { className: "text-ui-fg-subtle" }),
84
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "ประวัติการจองรับสินค้า" })
85
+ ] }) }),
86
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-red-500", children: error }) })
87
+ ] });
88
+ }
89
+ return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
90
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
91
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
92
+ /* @__PURE__ */ jsx(Calendar, { className: "text-ui-fg-subtle" }),
93
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "ประวัติการจองรับสินค้า" }),
94
+ pickupHistory.length > 0 && /* @__PURE__ */ jsx(Badge, { size: "small", children: pickupHistory.length })
95
+ ] }),
96
+ /* @__PURE__ */ jsx(Button, { size: "small", variant: "transparent", onClick: fetchPickupHistory, children: "รีเฟรช" })
97
+ ] }),
98
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: pickupHistory.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
99
+ /* @__PURE__ */ jsx(Package, { className: "text-ui-fg-muted mb-4", size: 48 }),
100
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "ยังไม่มีประวัติการจองรับสินค้า" })
101
+ ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-4", children: pickupHistory.map((pickup) => {
102
+ var _a;
103
+ return /* @__PURE__ */ jsxs(
104
+ "div",
105
+ {
106
+ className: "rounded-lg border p-4 hover:bg-ui-bg-subtle transition-colors",
107
+ children: [
108
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between mb-3", children: [
109
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
110
+ /* @__PURE__ */ jsx(ShoppingBag, { className: "text-ui-fg-subtle", size: 20 }),
111
+ /* @__PURE__ */ jsx("div", { children: pickup.order ? /* @__PURE__ */ jsxs(Fragment, { children: [
112
+ /* @__PURE__ */ jsxs(Text, { weight: "plus", size: "small", children: [
113
+ "คำสั่งซื้อ #",
114
+ pickup.order.display_id
115
+ ] }),
116
+ /* @__PURE__ */ jsxs(Text, { size: "xsmall", className: "text-ui-fg-subtle", children: [
117
+ ((_a = pickup.order.items) == null ? void 0 : _a.length) || 0,
118
+ " รายการ"
119
+ ] })
120
+ ] }) : /* @__PURE__ */ jsxs(Text, { weight: "plus", size: "small", children: [
121
+ "คำสั่งซื้อ ",
122
+ pickup.order_id
123
+ ] }) })
124
+ ] }),
125
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end gap-1", children: [
126
+ pickup.order && getStatusBadge(pickup.order.status),
127
+ pickup.order && /* @__PURE__ */ jsx(Text, { weight: "plus", size: "small", children: formatCurrency(
128
+ pickup.order.total,
129
+ pickup.order.currency_code
130
+ ) })
131
+ ] })
132
+ ] }),
133
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 text-sm", children: [
134
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-ui-fg-subtle", children: [
135
+ /* @__PURE__ */ jsx(Calendar, { size: 16 }),
136
+ /* @__PURE__ */ jsxs("span", { children: [
137
+ "วันที่รับ: ",
138
+ formatDate(pickup.pickup_datetime)
139
+ ] })
140
+ ] }),
141
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-ui-fg-subtle", children: [
142
+ /* @__PURE__ */ jsx(Clock, { size: 16 }),
143
+ /* @__PURE__ */ jsxs("span", { children: [
144
+ "จองเมื่อ: ",
145
+ formatDate(pickup.created_at)
146
+ ] })
147
+ ] })
148
+ ] }),
149
+ pickup.order && pickup.order.shipping_methods && /* @__PURE__ */ jsx("div", { className: "mt-2 pt-2 border-t", children: /* @__PURE__ */ jsx(Text, { size: "xsmall", className: "text-ui-fg-subtle", children: pickup.order.shipping_methods.map((sm) => sm.name).join(", ") }) })
150
+ ]
151
+ },
152
+ pickup.id
153
+ );
154
+ }) }) })
155
+ ] });
156
+ };
157
+ defineWidgetConfig({
158
+ zone: "customer.details.after"
159
+ });
160
+ const OrderPickupBookingWidget = ({ data }) => {
161
+ var _a, _b;
162
+ const [pickupBooking, setPickupBooking] = useState(null);
163
+ const [loading, setLoading] = useState(true);
164
+ const [error, setError] = useState(null);
165
+ const order = data;
166
+ useEffect(() => {
167
+ fetchPickupBooking();
168
+ }, [order.id]);
169
+ const fetchPickupBooking = async () => {
170
+ setLoading(true);
171
+ setError(null);
172
+ try {
173
+ const response = await fetch(`/admin/orders/${order.id}/pickup-booking`, {
174
+ credentials: "include"
175
+ });
176
+ if (!response.ok) {
177
+ if (response.status === 404) {
178
+ setPickupBooking(null);
179
+ setLoading(false);
180
+ return;
181
+ }
182
+ throw new Error("Failed to fetch pickup booking");
183
+ }
184
+ const data2 = await response.json();
185
+ setPickupBooking(data2.pickup_booking);
186
+ } catch (err) {
187
+ console.error("Error fetching pickup booking:", err);
188
+ setError(err.message);
189
+ } finally {
190
+ setLoading(false);
191
+ }
192
+ };
193
+ const formatDate = (dateString) => {
194
+ const date = new Date(dateString);
195
+ return new Intl.DateTimeFormat("th-TH", {
196
+ year: "numeric",
197
+ month: "long",
198
+ day: "numeric"
199
+ }).format(date);
200
+ };
201
+ const formatTime = (dateString) => {
202
+ const date = new Date(dateString);
203
+ return new Intl.DateTimeFormat("th-TH", {
204
+ hour: "2-digit",
205
+ minute: "2-digit",
206
+ hour12: false
207
+ }).format(date);
208
+ };
209
+ const formatDateTime = (dateString) => {
210
+ const date = new Date(dateString);
211
+ return new Intl.DateTimeFormat("th-TH", {
212
+ year: "numeric",
213
+ month: "short",
214
+ day: "numeric",
215
+ hour: "2-digit",
216
+ minute: "2-digit"
217
+ }).format(date);
218
+ };
219
+ const getPickupStatus = () => {
220
+ const now = /* @__PURE__ */ new Date();
221
+ const pickupDate = pickupBooking ? new Date(pickupBooking.pickup_datetime) : null;
222
+ if (!pickupDate) return { label: "N/A", color: "grey" };
223
+ if (order.status === "completed" || order.status === "delivered") {
224
+ return { label: "รับสินค้าแล้ว", color: "green" };
225
+ }
226
+ if (order.status === "canceled") {
227
+ return { label: "ยกเลิก", color: "red" };
228
+ }
229
+ if (now > pickupDate) {
230
+ return { label: "เลยกำหนดรับ", color: "orange" };
231
+ }
232
+ return { label: "รอรับสินค้า", color: "blue" };
233
+ };
234
+ if (loading) {
235
+ return /* @__PURE__ */ jsxs(Container$1, { className: "divide-y p-0", children: [
236
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
237
+ /* @__PURE__ */ jsx(Calendar, { className: "text-ui-fg-subtle" }),
238
+ /* @__PURE__ */ jsx(Heading$1, { level: "h2", children: "การจองรับสินค้า" })
239
+ ] }) }),
240
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text$1, { className: "text-ui-fg-subtle", children: "กำลังโหลด..." }) })
241
+ ] });
242
+ }
243
+ if (error) {
244
+ return /* @__PURE__ */ jsxs(Container$1, { className: "divide-y p-0", children: [
245
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
246
+ /* @__PURE__ */ jsx(Calendar, { className: "text-ui-fg-subtle" }),
247
+ /* @__PURE__ */ jsx(Heading$1, { level: "h2", children: "การจองรับสินค้า" })
248
+ ] }) }),
249
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text$1, { className: "text-red-500", children: error }) })
250
+ ] });
251
+ }
252
+ if (!pickupBooking) {
253
+ return /* @__PURE__ */ jsxs(Container$1, { className: "divide-y p-0", children: [
254
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
255
+ /* @__PURE__ */ jsx(Calendar, { className: "text-ui-fg-subtle" }),
256
+ /* @__PURE__ */ jsx(Heading$1, { level: "h2", children: "การจองรับสินค้า" })
257
+ ] }) }),
258
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
259
+ /* @__PURE__ */ jsx(MapPin, { className: "text-ui-fg-muted mb-4", size: 48 }),
260
+ /* @__PURE__ */ jsx(Text$1, { className: "text-ui-fg-subtle", children: "คำสั่งซื้อนี้ไม่ได้จองรับสินค้าที่สาขา" }),
261
+ /* @__PURE__ */ jsx(Text$1, { size: "xsmall", className: "text-ui-fg-muted mt-2", children: "คำสั่งซื้อนี้อาจเป็นการจัดส่งแบบปกติ" })
262
+ ] }) })
263
+ ] });
264
+ }
265
+ const status = getPickupStatus();
266
+ return /* @__PURE__ */ jsxs(Container$1, { className: "divide-y p-0", children: [
267
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
268
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
269
+ /* @__PURE__ */ jsx(Calendar, { className: "text-ui-fg-subtle" }),
270
+ /* @__PURE__ */ jsx(Heading$1, { level: "h2", children: "การจองรับสินค้าที่สาขา" }),
271
+ /* @__PURE__ */ jsx(Badge$1, { color: status.color, size: "small", children: status.label })
272
+ ] }),
273
+ /* @__PURE__ */ jsx(
274
+ Button$1,
275
+ {
276
+ size: "small",
277
+ variant: "transparent",
278
+ onClick: () => {
279
+ fetchPickupBooking();
280
+ toast.success("รีเฟรชข้อมูลแล้ว");
281
+ },
282
+ children: /* @__PURE__ */ jsx(RefreshCw, { size: 16 })
283
+ }
284
+ )
285
+ ] }),
286
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
287
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
288
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
289
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-ui-fg-subtle", children: [
290
+ /* @__PURE__ */ jsx(Calendar, { size: 16 }),
291
+ /* @__PURE__ */ jsx(Text$1, { size: "xsmall", weight: "plus", children: "วันที่รับสินค้า" })
292
+ ] }),
293
+ /* @__PURE__ */ jsx(Text$1, { weight: "plus", children: formatDate(pickupBooking.pickup_datetime) })
294
+ ] }),
295
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
296
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-ui-fg-subtle", children: [
297
+ /* @__PURE__ */ jsx(Clock, { size: 16 }),
298
+ /* @__PURE__ */ jsx(Text$1, { size: "xsmall", weight: "plus", children: "เวลารับสินค้า" })
299
+ ] }),
300
+ /* @__PURE__ */ jsx(Text$1, { weight: "plus", children: formatTime(pickupBooking.pickup_datetime) })
301
+ ] })
302
+ ] }),
303
+ pickupBooking.shipping_option && /* @__PURE__ */ jsx("div", { className: "rounded-lg border p-4 bg-ui-bg-subtle", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 mb-2", children: [
304
+ /* @__PURE__ */ jsx(MapPin, { className: "text-ui-fg-muted mt-1", size: 16 }),
305
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
306
+ /* @__PURE__ */ jsx(Text$1, { weight: "plus", size: "small", className: "mb-1", children: pickupBooking.shipping_option.name }),
307
+ ((_b = (_a = pickupBooking.shipping_option.service_zone) == null ? void 0 : _a.fulfillment_set) == null ? void 0 : _b.location) && /* @__PURE__ */ jsxs(Fragment, { children: [
308
+ /* @__PURE__ */ jsx(Text$1, { size: "small", className: "text-ui-fg-subtle", children: pickupBooking.shipping_option.service_zone.fulfillment_set.location.name }),
309
+ pickupBooking.shipping_option.service_zone.fulfillment_set.location.address && /* @__PURE__ */ jsxs(Text$1, { size: "xsmall", className: "text-ui-fg-muted mt-1", children: [
310
+ pickupBooking.shipping_option.service_zone.fulfillment_set.location.address.address_1,
311
+ pickupBooking.shipping_option.service_zone.fulfillment_set.location.address.address_2 && `, ${pickupBooking.shipping_option.service_zone.fulfillment_set.location.address.address_2}`,
312
+ /* @__PURE__ */ jsx("br", {}),
313
+ pickupBooking.shipping_option.service_zone.fulfillment_set.location.address.city,
314
+ " ",
315
+ pickupBooking.shipping_option.service_zone.fulfillment_set.location.address.province,
316
+ " ",
317
+ pickupBooking.shipping_option.service_zone.fulfillment_set.location.address.postal_code
318
+ ] })
319
+ ] })
320
+ ] })
321
+ ] }) }),
322
+ /* @__PURE__ */ jsx("div", { className: "pt-4 border-t", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
323
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
324
+ /* @__PURE__ */ jsx(Text$1, { size: "xsmall", className: "text-ui-fg-subtle", children: "จองเมื่อ" }),
325
+ /* @__PURE__ */ jsx(Text$1, { size: "small", children: formatDateTime(pickupBooking.created_at) })
326
+ ] }),
327
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
328
+ /* @__PURE__ */ jsx(Text$1, { size: "xsmall", className: "text-ui-fg-subtle", children: "อัปเดตล่าสุด" }),
329
+ /* @__PURE__ */ jsx(Text$1, { size: "small", children: formatDateTime(pickupBooking.updated_at) })
330
+ ] })
331
+ ] }) }),
332
+ /* @__PURE__ */ jsx("div", { className: "pt-4 border-t", children: /* @__PURE__ */ jsxs(Text$1, { size: "xsmall", className: "text-ui-fg-muted", children: [
333
+ "Booking ID: ",
334
+ pickupBooking.id
335
+ ] }) })
336
+ ] }) })
337
+ ] });
338
+ };
339
+ defineWidgetConfig$1({
340
+ zone: "order.details.after"
341
+ });
342
+ const sdk = new Medusa({
343
+ baseUrl: "/",
344
+ debug: false,
345
+ auth: {
346
+ type: "session"
347
+ }
348
+ });
349
+ const usePickupShippingOptions = () => {
350
+ const [data, setData] = useState(
351
+ null
352
+ );
353
+ const [isLoading, setIsLoading] = useState(true);
354
+ const [error, setError] = useState(null);
355
+ const fetchOptions = async () => {
356
+ setIsLoading(true);
357
+ setError(null);
358
+ try {
359
+ const options = await sdk.admin.shippingOption.list({
360
+ fields: [
361
+ "id",
362
+ "name",
363
+ "shipping_profile.*",
364
+ "service_zone.*",
365
+ "service_zone.fulfillment_set.*"
366
+ ].join(",")
367
+ });
368
+ const filteredOptions = (options.shipping_options ?? []).filter(
369
+ (option) => {
370
+ var _a, _b;
371
+ return ((_b = (_a = option == null ? void 0 : option.service_zone) == null ? void 0 : _a.fulfillment_set) == null ? void 0 : _b.type) === "pickup";
372
+ }
373
+ );
374
+ setData({
375
+ shipping_options: filteredOptions,
376
+ count: filteredOptions.length,
377
+ limit: options.limit,
378
+ offset: options.offset
379
+ });
380
+ } catch (err) {
381
+ setError(
382
+ err instanceof Error ? err : new Error("Failed to fetch pickup shipping options")
383
+ );
384
+ } finally {
385
+ setIsLoading(false);
386
+ }
387
+ };
388
+ useEffect(() => {
389
+ fetchOptions();
390
+ }, []);
391
+ return {
392
+ data,
393
+ isLoading,
394
+ error,
395
+ refetch: fetchOptions
396
+ };
397
+ };
398
+ const columnHelper = createDataTableColumnHelper();
399
+ const columns = [
400
+ columnHelper.accessor("shipping_profile.name", {
401
+ header: "Shipping Profile"
402
+ }),
403
+ columnHelper.accessor("service_zone.name", {
404
+ header: "Service Zone"
405
+ }),
406
+ columnHelper.accessor("name", {
407
+ header: "Pickup name"
408
+ }),
409
+ columnHelper.accessor("service_zone.fulfillment_set.name", {
410
+ header: "Fulfillment name"
411
+ })
412
+ ];
413
+ const ShippingOptionList = () => {
414
+ const navigate = useNavigate();
415
+ const [pagination, setPagination] = useState({
416
+ pageSize: 1e3,
417
+ pageIndex: 0
418
+ });
419
+ const { data, isLoading } = usePickupShippingOptions();
420
+ const table = useDataTable({
421
+ columns,
422
+ data: (data == null ? void 0 : data.shipping_options) || [],
423
+ getRowId: (tier) => tier.id,
424
+ rowCount: (data == null ? void 0 : data.count) || 0,
425
+ isLoading,
426
+ pagination: {
427
+ state: pagination,
428
+ onPaginationChange: setPagination
429
+ },
430
+ onRowClick: (_, row) => {
431
+ navigate(`/pickup-shipping/${row.id}`);
432
+ }
433
+ });
434
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Container$1, { className: "flex flex-col p-0 overflow-hidden", children: /* @__PURE__ */ jsxs(DataTable, { instance: table, children: [
435
+ /* @__PURE__ */ jsx(DataTable.Toolbar, { className: "flex flex-col items-start justify-between gap-2 md:flex-row md:items-center", children: /* @__PURE__ */ jsx(Heading$1, { children: "Pickup Shipping Options" }) }),
436
+ /* @__PURE__ */ jsx(DataTable.Table, {})
437
+ ] }) }) });
438
+ };
439
+ const PickupShipping = () => {
440
+ return /* @__PURE__ */ jsx(ShippingOptionList, {});
441
+ };
442
+ const config = defineRouteConfig({
443
+ label: "Shipping Pickup",
444
+ icon: StackPerspective
445
+ });
446
+ const usePickupAvailable = (id) => {
447
+ const [data, setData] = useState(null);
448
+ const [isLoading, setIsLoading] = useState(true);
449
+ const [error, setError] = useState(null);
450
+ const fetchPickupAvailable = async () => {
451
+ if (!id) {
452
+ setError(new Error("id is required"));
453
+ setIsLoading(false);
454
+ return;
455
+ }
456
+ setIsLoading(true);
457
+ setError(null);
458
+ try {
459
+ const option = await sdk.admin.shippingOption.retrieve(id, {
460
+ fields: "*.*,pickup_availabilities.*"
461
+ });
462
+ if (option.shipping_option.service_zone.fulfillment_set.type !== "pickup") {
463
+ throw new Error("shipping option is not pickup");
464
+ }
465
+ setData(option);
466
+ } catch (err) {
467
+ setError(
468
+ err instanceof Error ? err : new Error("Failed to fetch pickup availability")
469
+ );
470
+ } finally {
471
+ setIsLoading(false);
472
+ }
473
+ };
474
+ useEffect(() => {
475
+ fetchPickupAvailable();
476
+ }, [id]);
477
+ return {
478
+ data,
479
+ isLoading,
480
+ error,
481
+ refetch: fetchPickupAvailable
482
+ };
483
+ };
484
+ const stringToMinutes = (time, defaultValue) => {
485
+ if (!time) return defaultValue;
486
+ const [hours, minutes] = time.split(":");
487
+ return parseInt(hours) * 60 + parseInt(minutes);
488
+ };
489
+ const minutesToString = (minutes) => {
490
+ const hours = Math.floor(minutes / 60);
491
+ const remainingMinutes = minutes % 60;
492
+ return `${hours.toString().padStart(2, "0")}:${remainingMinutes.toString().padStart(2, "0")}`;
493
+ };
494
+ const dateToString = (date) => {
495
+ if (typeof date === "string") {
496
+ date = new Date(date);
497
+ }
498
+ const hours = date.getHours();
499
+ const minutes = date.getMinutes();
500
+ return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
501
+ };
502
+ const TimeSelect = ({
503
+ className,
504
+ intervalMinutes,
505
+ defaultValue,
506
+ onChange,
507
+ minTime,
508
+ maxTime
509
+ }) => {
510
+ const minTimeMinutes = stringToMinutes(minTime, 0);
511
+ const maxTimeMinutes = stringToMinutes(maxTime, 24 * 60);
512
+ const times = useMemo(() => {
513
+ const times2 = [];
514
+ for (let i = minTimeMinutes; i <= maxTimeMinutes; i += intervalMinutes) {
515
+ times2.push(minutesToString(i));
516
+ }
517
+ return times2;
518
+ }, [minTimeMinutes, maxTimeMinutes, intervalMinutes]);
519
+ return /* @__PURE__ */ jsxs(Select, { onValueChange: onChange, defaultValue, children: [
520
+ /* @__PURE__ */ jsx(Select.Trigger, { className: clx("w-24", className), children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Select a time" }) }),
521
+ /* @__PURE__ */ jsx(Select.Content, { children: times.map((time) => /* @__PURE__ */ jsx(Select.Item, { value: time, children: time }, time)) })
522
+ ] });
523
+ };
524
+ function TimeTable({
525
+ schedule,
526
+ intervalMinutes = 30
527
+ }) {
528
+ const days = [
529
+ "monday",
530
+ "tuesday",
531
+ "wednesday",
532
+ "thursday",
533
+ "friday",
534
+ "saturday",
535
+ "sunday"
536
+ ];
537
+ const timeSlots = useMemo(() => {
538
+ const minTimeMinutes = Math.min(
539
+ ...Object.values(schedule).filter((d) => d.enabled).map((d) => d.timeSlots.map((s) => stringToMinutes(s.start, 0))).flat()
540
+ );
541
+ const maxTimeMinutes = Math.max(
542
+ ...Object.values(schedule).filter((d) => d.enabled).map((d) => d.timeSlots.map((s) => stringToMinutes(s.end, 24 * 60))).flat()
543
+ );
544
+ const slots = [];
545
+ for (let i = Math.max(minTimeMinutes - 2 * intervalMinutes, 0); i <= Math.min(maxTimeMinutes + 2 * intervalMinutes, 24 * 60); i += intervalMinutes) {
546
+ slots.push(minutesToString(i));
547
+ }
548
+ return slots;
549
+ }, [schedule, intervalMinutes]);
550
+ const isWorkingHour = (day, timeSlot) => {
551
+ var _a;
552
+ if (!((_a = schedule[day]) == null ? void 0 : _a.enabled)) return false;
553
+ const [hours, minutes] = timeSlot.split(":");
554
+ const timeInMinutes = Number(hours) * 60 + Number(minutes);
555
+ return schedule[day].timeSlots.some((slot) => {
556
+ const startMatch = slot.start.match(/(\d+):(\d+)/);
557
+ if (!startMatch) return false;
558
+ let startHour = Number(startMatch[1]);
559
+ const startMinute = Number(startMatch[2]);
560
+ const startInMinutes = startHour * 60 + startMinute;
561
+ const endMatch = slot.end.match(/(\d+):(\d+)/);
562
+ if (!endMatch) return false;
563
+ let endHour = Number(endMatch[1]);
564
+ const endMinute = Number(endMatch[2]);
565
+ const endInMinutes = endHour * 60 + endMinute;
566
+ return timeInMinutes >= startInMinutes && timeInMinutes < endInMinutes;
567
+ });
568
+ };
569
+ if (timeSlots.length === 0) return null;
570
+ return /* @__PURE__ */ jsxs(Container$1, { className: "p-4 overflow-x-auto", children: [
571
+ /* @__PURE__ */ jsx(Heading$1, { className: "text-xl font-bold mb-4", children: "Pickup Hours Schedule" }),
572
+ /* @__PURE__ */ jsxs("div", { className: "min-w-full flex", children: [
573
+ /* @__PURE__ */ jsx("div", { className: "ml-2 mt-7 flex flex-col items-end", children: timeSlots.map((timeSlot) => /* @__PURE__ */ jsx(Text$1, { className: "h-8", children: timeSlot }, timeSlot)) }),
574
+ /* @__PURE__ */ jsxs("table", { className: "ml-2 w-full border-collapse", children: [
575
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: days.map((day) => /* @__PURE__ */ jsx(
576
+ "th",
577
+ {
578
+ className: "py-2 px-4 border-b border-gray-800 text-center capitalize",
579
+ children: day.slice(0, 3)
580
+ },
581
+ day
582
+ )) }) }),
583
+ /* @__PURE__ */ jsx("tbody", { children: timeSlots.map((timeSlot) => /* @__PURE__ */ jsx("tr", { className: "hover:bg-ui-bg-base-hover", children: days.map((day) => /* @__PURE__ */ jsx(
584
+ "td",
585
+ {
586
+ className: clx(
587
+ `py-1 px-4 border-b border-gray-800 text-center h-8`,
588
+ isWorkingHour(day, timeSlot) && "bg-ui-bg-interactive"
589
+ ),
590
+ children: ""
591
+ },
592
+ `${day}-${timeSlot}`
593
+ )) }, timeSlot)) })
594
+ ] })
595
+ ] })
596
+ ] });
597
+ }
598
+ const INTERVAL_MINUTES = 30;
599
+ const DEFAULT_SCHEDULE = {
600
+ monday: {
601
+ enabled: false,
602
+ timeSlots: []
603
+ },
604
+ tuesday: {
605
+ enabled: false,
606
+ timeSlots: []
607
+ },
608
+ wednesday: {
609
+ enabled: false,
610
+ timeSlots: []
611
+ },
612
+ thursday: {
613
+ enabled: false,
614
+ timeSlots: []
615
+ },
616
+ friday: {
617
+ enabled: false,
618
+ timeSlots: []
619
+ },
620
+ saturday: {
621
+ enabled: false,
622
+ timeSlots: []
623
+ },
624
+ sunday: { enabled: false, timeSlots: [] }
625
+ };
626
+ function ShippingOptionDetails() {
627
+ const { id } = useParams();
628
+ const navigate = useNavigate();
629
+ const sdk2 = useSdk();
630
+ const [schedule, setSchedule] = useState(
631
+ structuredClone(DEFAULT_SCHEDULE)
632
+ );
633
+ const { data, refetch, error } = usePickupAvailable(id);
634
+ useEffect(() => {
635
+ if (!data) return;
636
+ const _schedule = structuredClone(DEFAULT_SCHEDULE);
637
+ data.shipping_option.pickup_availabilities.forEach((availability) => {
638
+ const timeSlots = _schedule[availability.day_of_week].timeSlots;
639
+ timeSlots.push({
640
+ start: dateToString(availability.timeStart),
641
+ end: dateToString(availability.timeEnd)
642
+ });
643
+ _schedule[availability.day_of_week] = {
644
+ enabled: availability.enable,
645
+ timeSlots
646
+ };
647
+ });
648
+ setSchedule({ ..._schedule });
649
+ }, [data]);
650
+ const toggleDay = useCallback((day) => {
651
+ setSchedule((prev) => ({
652
+ ...prev,
653
+ [day]: {
654
+ ...prev[day],
655
+ enabled: !prev[day].enabled
656
+ }
657
+ }));
658
+ }, []);
659
+ const handleUpdateSchedules = async () => {
660
+ const invalidSchedules = Object.entries(schedule).filter(
661
+ ([_, schedule2]) => {
662
+ if (!schedule2.enabled) return false;
663
+ return schedule2.timeSlots.filter((slot) => {
664
+ if (!slot.start || !slot.end) {
665
+ return true;
666
+ }
667
+ }).length > 0;
668
+ }
669
+ );
670
+ if (invalidSchedules.length > 0) {
671
+ toast.error("Invalid time slot", {
672
+ description: "Please fill in all time slots",
673
+ duration: 2500,
674
+ dismissable: true
675
+ });
676
+ return;
677
+ }
678
+ try {
679
+ await sdk2.client.fetch(`/admin/pickup-date/${id}`, {
680
+ method: "POST",
681
+ headers: {
682
+ "Content-Type": "application/json"
683
+ },
684
+ body: JSON.stringify({
685
+ schedules: Object.entries(schedule).filter(([_, daySchedule]) => daySchedule.enabled).map(([day, { enabled, timeSlots }]) => ({
686
+ day_of_week: day,
687
+ enable: enabled,
688
+ timeSlots: timeSlots.map((slot) => ({
689
+ start: slot.start,
690
+ end: slot.end
691
+ }))
692
+ }))
693
+ })
694
+ });
695
+ refetch();
696
+ toast.success("Availability updated", {
697
+ duration: 2500,
698
+ dismissable: true
699
+ });
700
+ } catch (error2) {
701
+ if (error2 instanceof Error) {
702
+ toast.error(error2.message, {
703
+ duration: 2500,
704
+ dismissable: true
705
+ });
706
+ return;
707
+ }
708
+ toast.error("Error while creating availability", {
709
+ duration: 2500,
710
+ dismissable: true
711
+ });
712
+ }
713
+ };
714
+ const updateTimeSlot = useCallback(
715
+ (day, index, field, value) => {
716
+ setSchedule((prev) => {
717
+ const newTimeSlots = [...prev[day].timeSlots];
718
+ newTimeSlots[index] = {
719
+ ...newTimeSlots[index],
720
+ [field]: value
721
+ };
722
+ return {
723
+ ...prev,
724
+ [day]: {
725
+ ...prev[day],
726
+ timeSlots: newTimeSlots
727
+ }
728
+ };
729
+ });
730
+ },
731
+ []
732
+ );
733
+ const addTimeSlot = useCallback((day) => {
734
+ setSchedule((prev) => {
735
+ const newTimeSlots = [...prev[day].timeSlots];
736
+ newTimeSlots.push({ start: "", end: "" });
737
+ return {
738
+ ...prev,
739
+ [day]: {
740
+ ...prev[day],
741
+ timeSlots: newTimeSlots
742
+ }
743
+ };
744
+ });
745
+ }, []);
746
+ const removeTimeSlot = useCallback((day, index) => {
747
+ setSchedule((prev) => {
748
+ const newTimeSlots = [...prev[day].timeSlots];
749
+ newTimeSlots.splice(index, 1);
750
+ return {
751
+ ...prev,
752
+ [day]: {
753
+ ...prev[day],
754
+ timeSlots: newTimeSlots
755
+ }
756
+ };
757
+ });
758
+ }, []);
759
+ if (error) {
760
+ return /* @__PURE__ */ jsx(Navigate, { to: "/pickup-shipping" });
761
+ }
762
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
763
+ /* @__PURE__ */ jsxs(Container$1, { className: "p-4 ", children: [
764
+ /* @__PURE__ */ jsxs(
765
+ Button$1,
766
+ {
767
+ onClick: () => navigate("/pickup-shipping"),
768
+ variant: "transparent",
769
+ className: "p-0 mb-4",
770
+ children: [
771
+ /* @__PURE__ */ jsx(ArrowLeft, { size: 20 }),
772
+ /* @__PURE__ */ jsx(Text$1, { className: "ml-2", children: "Back" })
773
+ ]
774
+ }
775
+ ),
776
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 mb-4", children: [
777
+ /* @__PURE__ */ jsx(Heading$1, { className: "text-xl font-bold", children: "Pickup Hours" }),
778
+ /* @__PURE__ */ jsx(Button$1, { onClick: handleUpdateSchedules, children: "Save" })
779
+ ] }),
780
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", children: Object.entries(schedule).map(([day, { enabled, timeSlots }]) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
781
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 mb-2 h-8", children: [
782
+ /* @__PURE__ */ jsx(
783
+ Switch,
784
+ {
785
+ checked: enabled,
786
+ onCheckedChange: () => toggleDay(day)
787
+ },
788
+ `switch-${day}`
789
+ ),
790
+ /* @__PURE__ */ jsx(Text$1, { className: "capitalize", children: day }),
791
+ enabled && /* @__PURE__ */ jsx(
792
+ IconButton,
793
+ {
794
+ variant: "transparent",
795
+ onClick: () => addTimeSlot(day),
796
+ children: /* @__PURE__ */ jsx(Plus, { className: "" })
797
+ }
798
+ )
799
+ ] }),
800
+ enabled && /* @__PURE__ */ jsx("div", { className: "ml-12 space-y-2", children: timeSlots.map((slot, index) => /* @__PURE__ */ jsxs(
801
+ "div",
802
+ {
803
+ className: "flex items-center gap-2",
804
+ children: [
805
+ /* @__PURE__ */ jsx(
806
+ TimeSelect,
807
+ {
808
+ className: "w-40",
809
+ intervalMinutes: INTERVAL_MINUTES,
810
+ defaultValue: slot.start,
811
+ onChange: (value) => updateTimeSlot(day, index, "start", value),
812
+ maxTime: slot.end
813
+ }
814
+ ),
815
+ /* @__PURE__ */ jsx(Text$1, { className: "text-gray-400", children: "-" }),
816
+ /* @__PURE__ */ jsx(
817
+ TimeSelect,
818
+ {
819
+ className: "w-40",
820
+ intervalMinutes: INTERVAL_MINUTES,
821
+ defaultValue: slot.end,
822
+ onChange: (value) => updateTimeSlot(day, index, "end", value),
823
+ minTime: slot.start
824
+ }
825
+ ),
826
+ /* @__PURE__ */ jsx(
827
+ IconButton,
828
+ {
829
+ variant: "transparent",
830
+ onClick: () => removeTimeSlot(day, index),
831
+ className: " hover:text-red-400",
832
+ children: /* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" })
833
+ }
834
+ )
835
+ ]
836
+ },
837
+ `${day}-slot-${index}`
838
+ )) })
839
+ ] }, day)) })
840
+ ] }),
841
+ /* @__PURE__ */ jsx("div", { className: "h-2" }),
842
+ /* @__PURE__ */ jsx(TimeTable, { schedule })
843
+ ] });
844
+ }
845
+ const widgetModule = { widgets: [
846
+ {
847
+ Component: CustomerPickupHistoryWidget,
848
+ zone: ["customer.details.after"]
849
+ },
850
+ {
851
+ Component: OrderPickupBookingWidget,
852
+ zone: ["order.details.after"]
853
+ }
854
+ ] };
855
+ const routeModule = {
856
+ routes: [
857
+ {
858
+ Component: PickupShipping,
859
+ path: "/pickup-shipping"
860
+ },
861
+ {
862
+ Component: ShippingOptionDetails,
863
+ path: "/pickup-shipping/:id"
864
+ }
865
+ ]
866
+ };
867
+ const menuItemModule = {
868
+ menuItems: [
869
+ {
870
+ label: config.label,
871
+ icon: config.icon,
872
+ path: "/pickup-shipping",
873
+ nested: void 0
874
+ }
875
+ ]
876
+ };
877
+ const formModule = { customFields: {} };
878
+ const displayModule = {
879
+ displays: {}
880
+ };
881
+ const i18nModule = { resources: {} };
882
+ const plugin = {
883
+ widgetModule,
884
+ routeModule,
885
+ menuItemModule,
886
+ formModule,
887
+ displayModule,
888
+ i18nModule
889
+ };
890
+ export {
891
+ plugin as default
892
+ };