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