@lodashventure/medusa-parcel-shipping 0.4.29 → 0.4.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/index.js +725 -725
- package/.medusa/server/src/admin/index.mjs +727 -727
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
|
|
3
|
-
import { Container, Heading, Button, Text, Badge, Drawer, Label,
|
|
3
|
+
import { Container, Heading, Button, Text, Badge, Drawer, Label, Input, Switch, toast, Table, Prompt, Select, Textarea } from "@medusajs/ui";
|
|
4
4
|
import { useState, useEffect, useMemo } from "react";
|
|
5
5
|
import { Package, AlertCircle, Truck, Clock, Plus, Edit, Trash, ExternalLink } from "lucide-react";
|
|
6
|
-
import {
|
|
6
|
+
import { Directions, ArchiveBox, MapPin, CurrencyDollarSolid, HandTruck } from "@medusajs/icons";
|
|
7
7
|
import "@medusajs/admin-shared";
|
|
8
8
|
const OrderShippingQuoteWidget = ({ data }) => {
|
|
9
9
|
const [quote, setQuote] = useState(null);
|
|
@@ -235,116 +235,49 @@ const OrderShippingQuoteWidget = ({ data }) => {
|
|
|
235
235
|
defineWidgetConfig({
|
|
236
236
|
zone: "order.details.after"
|
|
237
237
|
});
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
"กาฬสินธุ์",
|
|
243
|
-
"กำแพงเพชร",
|
|
244
|
-
"ขอนแก่น",
|
|
245
|
-
"จันทบุรี",
|
|
246
|
-
"ฉะเชิงเทรา",
|
|
247
|
-
"ชลบุรี",
|
|
248
|
-
"ชัยนาท",
|
|
249
|
-
"ชัยภูมิ",
|
|
250
|
-
"ชุมพร",
|
|
251
|
-
"เชียงราย",
|
|
252
|
-
"เชียงใหม่",
|
|
253
|
-
"ตรัง",
|
|
254
|
-
"ตราด",
|
|
255
|
-
"ตาก",
|
|
256
|
-
"นครนายก",
|
|
257
|
-
"นครปฐม",
|
|
258
|
-
"นครพนม",
|
|
259
|
-
"นครราชสีมา",
|
|
260
|
-
"นครศรีธรรมราช",
|
|
261
|
-
"นครสวรรค์",
|
|
262
|
-
"นนทบุรี",
|
|
263
|
-
"นราธิวาส",
|
|
264
|
-
"น่าน",
|
|
265
|
-
"บึงกาฬ",
|
|
266
|
-
"บุรีรัมย์",
|
|
267
|
-
"ปทุมธานี",
|
|
268
|
-
"ประจวบคีรีขันธ์",
|
|
269
|
-
"ปราจีนบุรี",
|
|
270
|
-
"ปัตตานี",
|
|
271
|
-
"พระนครศรีอยุธยา",
|
|
272
|
-
"พะเยา",
|
|
273
|
-
"พังงา",
|
|
274
|
-
"พัทลุง",
|
|
275
|
-
"พิจิตร",
|
|
276
|
-
"พิษณุโลก",
|
|
277
|
-
"เพชรบุรี",
|
|
278
|
-
"เพชรบูรณ์",
|
|
279
|
-
"แพร่",
|
|
280
|
-
"ภูเก็ต",
|
|
281
|
-
"มหาสารคาม",
|
|
282
|
-
"มุกดาหาร",
|
|
283
|
-
"แม่ฮ่องสอน",
|
|
284
|
-
"ยโสธร",
|
|
285
|
-
"ยะลา",
|
|
286
|
-
"ร้อยเอ็ด",
|
|
287
|
-
"ระนอง",
|
|
288
|
-
"ระยอง",
|
|
289
|
-
"ราชบุรี",
|
|
290
|
-
"ลพบุรี",
|
|
291
|
-
"ลำปาง",
|
|
292
|
-
"ลำพูน",
|
|
293
|
-
"เลย",
|
|
294
|
-
"ศรีสะเกษ",
|
|
295
|
-
"สกลนคร",
|
|
296
|
-
"สงขลา",
|
|
297
|
-
"สตูล",
|
|
298
|
-
"สมุทรปราการ",
|
|
299
|
-
"สมุทรสงคราม",
|
|
300
|
-
"สมุทรสาคร",
|
|
301
|
-
"สระแก้ว",
|
|
302
|
-
"สระบุรี",
|
|
303
|
-
"สิงห์บุรี",
|
|
304
|
-
"สุโขทัย",
|
|
305
|
-
"สุพรรณบุรี",
|
|
306
|
-
"สุราษฎร์ธานี",
|
|
307
|
-
"สุรินทร์",
|
|
308
|
-
"หนองคาย",
|
|
309
|
-
"หนองบัวลำภู",
|
|
310
|
-
"อ่างทอง",
|
|
311
|
-
"อำนาจเจริญ",
|
|
312
|
-
"อุดรธานี",
|
|
313
|
-
"อุตรดิตถ์",
|
|
314
|
-
"อุทัยธานี",
|
|
315
|
-
"อุบลราชธานี"
|
|
316
|
-
];
|
|
317
|
-
const ServiceAreaModal = ({ area, onClose }) => {
|
|
238
|
+
const MessengerBaseRateModal = ({
|
|
239
|
+
rate,
|
|
240
|
+
onClose
|
|
241
|
+
}) => {
|
|
318
242
|
const [formData, setFormData] = useState({
|
|
319
|
-
|
|
320
|
-
|
|
243
|
+
min_distance_km: "0",
|
|
244
|
+
max_distance_km: "",
|
|
245
|
+
price: "",
|
|
246
|
+
priority: "0",
|
|
321
247
|
active: true
|
|
322
248
|
});
|
|
323
249
|
const [isSaving, setIsSaving] = useState(false);
|
|
324
250
|
useEffect(() => {
|
|
325
|
-
|
|
251
|
+
var _a, _b;
|
|
252
|
+
if (rate) {
|
|
326
253
|
setFormData({
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
254
|
+
min_distance_km: ((_a = rate.min_distance_km) == null ? void 0 : _a.toString()) ?? "0",
|
|
255
|
+
max_distance_km: ((_b = rate.max_distance_km) == null ? void 0 : _b.toString()) || "",
|
|
256
|
+
price: rate.price.toString(),
|
|
257
|
+
priority: (rate.priority ?? 0).toString(),
|
|
258
|
+
active: rate.active
|
|
330
259
|
});
|
|
331
260
|
}
|
|
332
|
-
}, [
|
|
261
|
+
}, [rate]);
|
|
333
262
|
const handleSubmit = async (e) => {
|
|
334
263
|
e.preventDefault();
|
|
335
264
|
const errors = [];
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
265
|
+
const minDistance = parseFloat(formData.min_distance_km || "0");
|
|
266
|
+
const hasMaxDistance = formData.max_distance_km !== "";
|
|
267
|
+
const maxDistance = hasMaxDistance ? parseFloat(formData.max_distance_km) : void 0;
|
|
268
|
+
const price = parseFloat(formData.price);
|
|
269
|
+
const priority = parseInt(formData.priority || "0", 10);
|
|
270
|
+
if (isNaN(minDistance) || minDistance < 0) {
|
|
271
|
+
errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
|
|
339
272
|
}
|
|
340
|
-
if (
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
273
|
+
if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
|
|
274
|
+
errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
|
|
275
|
+
}
|
|
276
|
+
if (isNaN(price) || price < 0) {
|
|
277
|
+
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
278
|
+
}
|
|
279
|
+
if (isNaN(priority)) {
|
|
280
|
+
errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
|
|
348
281
|
}
|
|
349
282
|
if (errors.length > 0) {
|
|
350
283
|
toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
@@ -355,27 +288,32 @@ const ServiceAreaModal = ({ area, onClose }) => {
|
|
|
355
288
|
setIsSaving(true);
|
|
356
289
|
try {
|
|
357
290
|
const payload = {
|
|
358
|
-
|
|
359
|
-
|
|
291
|
+
carrier_type: "COMPANY_FLEET",
|
|
292
|
+
carrier: "COMPANY_FLEET",
|
|
293
|
+
service_code: "MESSENGER_3H",
|
|
294
|
+
min_distance_km: minDistance,
|
|
295
|
+
max_distance_km: maxDistance === void 0 || isNaN(maxDistance) ? null : maxDistance,
|
|
296
|
+
price,
|
|
297
|
+
currency: "THB",
|
|
298
|
+
priority,
|
|
360
299
|
active: formData.active
|
|
361
300
|
};
|
|
362
|
-
const url =
|
|
363
|
-
const method =
|
|
301
|
+
const url = rate ? `/admin/base-shipping-prices/${rate.id}` : "/admin/base-shipping-prices";
|
|
302
|
+
const method = rate ? "PUT" : "POST";
|
|
364
303
|
const response = await fetch(url, {
|
|
365
304
|
method,
|
|
366
305
|
headers: { "Content-Type": "application/json" },
|
|
367
306
|
credentials: "include",
|
|
368
307
|
body: JSON.stringify(payload)
|
|
369
308
|
});
|
|
370
|
-
if (response.ok) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
});
|
|
374
|
-
onClose();
|
|
375
|
-
} else {
|
|
376
|
-
const error = await response.json();
|
|
377
|
-
throw new Error(error.message || "Failed to save");
|
|
309
|
+
if (!response.ok) {
|
|
310
|
+
const error = await response.json().catch(() => ({}));
|
|
311
|
+
throw new Error(error.message || "ไม่สามารถบันทึกข้อมูลได้");
|
|
378
312
|
}
|
|
313
|
+
toast.success("สำเร็จ", {
|
|
314
|
+
description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
|
|
315
|
+
});
|
|
316
|
+
onClose();
|
|
379
317
|
} catch (error) {
|
|
380
318
|
toast.error("ข้อผิดพลาด", {
|
|
381
319
|
description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
|
|
@@ -385,53 +323,69 @@ const ServiceAreaModal = ({ area, onClose }) => {
|
|
|
385
323
|
}
|
|
386
324
|
};
|
|
387
325
|
return /* @__PURE__ */ jsx(Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(Drawer.Content, { children: [
|
|
388
|
-
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children:
|
|
326
|
+
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: rate ? "แก้ไขฐานราคาตามระยะทาง" : "สร้างฐานราคาตามระยะทาง" }) }),
|
|
389
327
|
/* @__PURE__ */ jsx(Drawer.Body, { children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
328
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
329
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
330
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "min_distance_km", children: "ระยะทางเริ่มต้น (กม.) *" }),
|
|
331
|
+
/* @__PURE__ */ jsx(
|
|
332
|
+
Input,
|
|
333
|
+
{
|
|
334
|
+
id: "min_distance_km",
|
|
335
|
+
type: "number",
|
|
336
|
+
min: "0",
|
|
337
|
+
step: "0.1",
|
|
338
|
+
value: formData.min_distance_km,
|
|
339
|
+
onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
|
|
340
|
+
required: true
|
|
341
|
+
}
|
|
342
|
+
)
|
|
343
|
+
] }),
|
|
344
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
345
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "max_distance_km", children: "ระยะทางสูงสุด (กม.)" }),
|
|
346
|
+
/* @__PURE__ */ jsx(
|
|
347
|
+
Input,
|
|
348
|
+
{
|
|
349
|
+
id: "max_distance_km",
|
|
350
|
+
type: "number",
|
|
351
|
+
min: "0",
|
|
352
|
+
step: "0.1",
|
|
353
|
+
placeholder: "ปล่อยว่างหากไม่มีที่สิ้นสุด",
|
|
354
|
+
value: formData.max_distance_km,
|
|
355
|
+
onChange: (e) => setFormData({ ...formData, max_distance_km: e.target.value })
|
|
356
|
+
}
|
|
357
|
+
),
|
|
358
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
|
|
359
|
+
] })
|
|
360
|
+
] }),
|
|
390
361
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
391
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
392
|
-
/* @__PURE__ */
|
|
393
|
-
|
|
362
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "price", children: "ราคา (THB) *" }),
|
|
363
|
+
/* @__PURE__ */ jsx(
|
|
364
|
+
Input,
|
|
394
365
|
{
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
/* @__PURE__ */ jsx(Select.Item, { value: "POSTCODE_PREFIX", children: "รหัสไปรษณีย์" })
|
|
403
|
-
] })
|
|
404
|
-
]
|
|
366
|
+
id: "price",
|
|
367
|
+
type: "number",
|
|
368
|
+
min: "0",
|
|
369
|
+
step: "0.01",
|
|
370
|
+
value: formData.price,
|
|
371
|
+
onChange: (e) => setFormData({ ...formData, price: e.target.value }),
|
|
372
|
+
required: true
|
|
405
373
|
}
|
|
406
374
|
)
|
|
407
375
|
] }),
|
|
408
376
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
409
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
410
|
-
|
|
411
|
-
Select,
|
|
412
|
-
{
|
|
413
|
-
value: formData.value,
|
|
414
|
-
onValueChange: (value) => setFormData({ ...formData, value }),
|
|
415
|
-
required: true,
|
|
416
|
-
children: [
|
|
417
|
-
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกจังหวัด" }) }),
|
|
418
|
-
/* @__PURE__ */ jsx(Select.Content, { children: TH_PROVINCES.map((province) => /* @__PURE__ */ jsx(Select.Item, { value: province, children: province }, province)) })
|
|
419
|
-
]
|
|
420
|
-
}
|
|
421
|
-
) : /* @__PURE__ */ jsx(
|
|
377
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
|
|
378
|
+
/* @__PURE__ */ jsx(
|
|
422
379
|
Input,
|
|
423
380
|
{
|
|
424
|
-
id: "
|
|
425
|
-
type: "
|
|
426
|
-
value: formData.
|
|
427
|
-
onChange: (e) => setFormData({ ...formData,
|
|
428
|
-
placeholder: "
|
|
429
|
-
maxLength: 5,
|
|
430
|
-
pattern: "\\d{1,5}",
|
|
431
|
-
required: true
|
|
381
|
+
id: "priority",
|
|
382
|
+
type: "number",
|
|
383
|
+
value: formData.priority,
|
|
384
|
+
onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
|
|
385
|
+
placeholder: "0"
|
|
432
386
|
}
|
|
433
387
|
),
|
|
434
|
-
|
|
388
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
|
|
435
389
|
] }),
|
|
436
390
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
437
391
|
/* @__PURE__ */ jsx(
|
|
@@ -460,200 +414,148 @@ const ServiceAreaModal = ({ area, onClose }) => {
|
|
|
460
414
|
] }) })
|
|
461
415
|
] }) });
|
|
462
416
|
};
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
{ value: "PROVINCE", label: "จังหวัด" },
|
|
466
|
-
{ value: "POSTCODE_PREFIX", label: "รหัสไปรษณีย์" }
|
|
467
|
-
];
|
|
468
|
-
const ServiceAreasTable = () => {
|
|
469
|
-
const [areas, setAreas] = useState([]);
|
|
470
|
-
const [filteredAreas, setFilteredAreas] = useState([]);
|
|
471
|
-
const [searchTerm, setSearchTerm] = useState("");
|
|
472
|
-
const [kindFilter, setKindFilter] = useState("ALL");
|
|
473
|
-
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
474
|
-
const [editingArea, setEditingArea] = useState(null);
|
|
417
|
+
const MessengerBasePricingTable = () => {
|
|
418
|
+
const [rates, setRates] = useState([]);
|
|
475
419
|
const [isLoading, setIsLoading] = useState(false);
|
|
476
|
-
const [
|
|
477
|
-
const [
|
|
420
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
421
|
+
const [editingRate, setEditingRate] = useState(null);
|
|
422
|
+
const [deleteId, setDeleteId] = useState(null);
|
|
478
423
|
useEffect(() => {
|
|
479
|
-
|
|
424
|
+
fetchRates();
|
|
480
425
|
}, []);
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}, [searchTerm, kindFilter, areas]);
|
|
493
|
-
const fetchAreas = async () => {
|
|
426
|
+
const orderedRates = useMemo(() => {
|
|
427
|
+
return [...rates].sort((a, b) => {
|
|
428
|
+
if (a.min_distance_km !== b.min_distance_km) {
|
|
429
|
+
return a.min_distance_km - b.min_distance_km;
|
|
430
|
+
}
|
|
431
|
+
const aMax = a.max_distance_km ?? Number.POSITIVE_INFINITY;
|
|
432
|
+
const bMax = b.max_distance_km ?? Number.POSITIVE_INFINITY;
|
|
433
|
+
return aMax - bMax;
|
|
434
|
+
});
|
|
435
|
+
}, [rates]);
|
|
436
|
+
const fetchRates = async () => {
|
|
494
437
|
setIsLoading(true);
|
|
495
438
|
try {
|
|
496
|
-
const response = await fetch(
|
|
497
|
-
|
|
498
|
-
|
|
439
|
+
const response = await fetch(
|
|
440
|
+
"/admin/base-shipping-prices?carrier_type=COMPANY_FLEET&service_code=MESSENGER_3H",
|
|
441
|
+
{
|
|
442
|
+
credentials: "include"
|
|
443
|
+
}
|
|
444
|
+
);
|
|
445
|
+
if (!response.ok) {
|
|
446
|
+
throw new Error("failed_to_load_base_rates");
|
|
447
|
+
}
|
|
499
448
|
const data = await response.json();
|
|
500
|
-
|
|
501
|
-
setAreas(data.service_areas || []);
|
|
449
|
+
setRates(data.base_rates || []);
|
|
502
450
|
} catch (error) {
|
|
503
451
|
toast.error("ข้อผิดพลาด", {
|
|
504
|
-
description: "
|
|
452
|
+
description: "ไม่สามารถโหลดฐานราคาตามระยะทางได้"
|
|
505
453
|
});
|
|
506
454
|
} finally {
|
|
507
455
|
setIsLoading(false);
|
|
508
456
|
}
|
|
509
457
|
};
|
|
510
|
-
const handleToggleActive = async (area) => {
|
|
511
|
-
try {
|
|
512
|
-
const response = await fetch(`/admin/service-areas/${area.id}`, {
|
|
513
|
-
method: "PUT",
|
|
514
|
-
headers: { "Content-Type": "application/json" },
|
|
515
|
-
credentials: "include",
|
|
516
|
-
body: JSON.stringify({ ...area, active: !area.active })
|
|
517
|
-
});
|
|
518
|
-
if (response.ok) {
|
|
519
|
-
toast.success("สำเร็จ", {
|
|
520
|
-
description: `${area.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}พื้นที่ ${area.value} แล้ว`
|
|
521
|
-
});
|
|
522
|
-
fetchAreas();
|
|
523
|
-
} else {
|
|
524
|
-
throw new Error("Failed to update");
|
|
525
|
-
}
|
|
526
|
-
} catch (error) {
|
|
527
|
-
toast.error("ข้อผิดพลาด", {
|
|
528
|
-
description: "ไม่สามารถอัพเดทสถานะได้"
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
};
|
|
532
458
|
const handleDelete = async () => {
|
|
533
|
-
if (!
|
|
459
|
+
if (!deleteId) return;
|
|
534
460
|
try {
|
|
535
|
-
const response = await fetch(
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
} else {
|
|
545
|
-
throw new Error("Failed to delete");
|
|
461
|
+
const response = await fetch(
|
|
462
|
+
`/admin/base-shipping-prices/${deleteId}`,
|
|
463
|
+
{
|
|
464
|
+
method: "DELETE",
|
|
465
|
+
credentials: "include"
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
if (!response.ok) {
|
|
469
|
+
throw new Error("failed_to_delete");
|
|
546
470
|
}
|
|
471
|
+
toast.success("สำเร็จ", {
|
|
472
|
+
description: "ลบฐานราคาสำเร็จ"
|
|
473
|
+
});
|
|
474
|
+
fetchRates();
|
|
547
475
|
} catch (error) {
|
|
548
476
|
toast.error("ข้อผิดพลาด", {
|
|
549
|
-
description: "
|
|
477
|
+
description: "ไม่สามารถลบฐานราคาได้"
|
|
550
478
|
});
|
|
551
479
|
} finally {
|
|
552
|
-
|
|
553
|
-
setDeleteAreaValue("");
|
|
480
|
+
setDeleteId(null);
|
|
554
481
|
}
|
|
555
482
|
};
|
|
556
|
-
const handleEdit = (area) => {
|
|
557
|
-
setEditingArea(area);
|
|
558
|
-
setIsModalOpen(true);
|
|
559
|
-
};
|
|
560
|
-
const handleCreateNew = () => {
|
|
561
|
-
setEditingArea(null);
|
|
562
|
-
setIsModalOpen(true);
|
|
563
|
-
};
|
|
564
483
|
const handleModalClose = () => {
|
|
565
484
|
setIsModalOpen(false);
|
|
566
|
-
|
|
567
|
-
|
|
485
|
+
setEditingRate(null);
|
|
486
|
+
fetchRates();
|
|
568
487
|
};
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
|
|
488
|
+
const formatDistanceRange = (rate) => {
|
|
489
|
+
const min = rate.min_distance_km ?? 0;
|
|
490
|
+
const max = rate.max_distance_km;
|
|
491
|
+
if (max === void 0 || max === null) {
|
|
492
|
+
return `${min}+ กม.`;
|
|
493
|
+
}
|
|
494
|
+
return `${min}-${max} กม.`;
|
|
572
495
|
};
|
|
573
496
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
574
|
-
/* @__PURE__ */ jsxs("div", { className: "flex
|
|
575
|
-
/* @__PURE__ */ jsxs("div", {
|
|
576
|
-
/* @__PURE__ */
|
|
497
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
498
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
499
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: "ฐานราคาตามระยะทาง (Messenger 3H)" }),
|
|
500
|
+
/* @__PURE__ */ jsx("p", { className: "text-ui-fg-subtle text-sm", children: "กำหนดราคาเริ่มต้นตามระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
|
|
501
|
+
] }),
|
|
502
|
+
/* @__PURE__ */ jsxs(Button, { onClick: () => setIsModalOpen(true), children: [
|
|
503
|
+
/* @__PURE__ */ jsx(Plus, {}),
|
|
504
|
+
"เพิ่มช่วงราคา"
|
|
505
|
+
] })
|
|
506
|
+
] }),
|
|
507
|
+
/* @__PURE__ */ jsxs(Table, { className: "mt-4", children: [
|
|
508
|
+
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
509
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ช่วงระยะทาง" }),
|
|
510
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
511
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ลำดับ" }),
|
|
512
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
513
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
514
|
+
] }) }),
|
|
515
|
+
/* @__PURE__ */ jsx(Table.Body, { children: isLoading ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : orderedRates.length === 0 ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "ยังไม่ได้ตั้งค่าฐานราคาตามระยะทาง" }) }) : orderedRates.map((rate) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
516
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: formatDistanceRange(rate) }),
|
|
517
|
+
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
518
|
+
"฿",
|
|
519
|
+
rate.price.toFixed(2)
|
|
520
|
+
] }),
|
|
521
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: rate.priority ?? 0 }),
|
|
522
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
523
|
+
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
577
524
|
/* @__PURE__ */ jsx(
|
|
578
|
-
|
|
525
|
+
Button,
|
|
579
526
|
{
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
527
|
+
variant: "transparent",
|
|
528
|
+
size: "small",
|
|
529
|
+
onClick: () => {
|
|
530
|
+
setEditingRate(rate);
|
|
531
|
+
setIsModalOpen(true);
|
|
532
|
+
},
|
|
533
|
+
children: /* @__PURE__ */ jsx(Edit, {})
|
|
585
534
|
}
|
|
586
535
|
),
|
|
587
|
-
/* @__PURE__ */
|
|
588
|
-
|
|
589
|
-
/* @__PURE__ */ jsx(Select.Content, { children: AREA_KINDS.map((kind) => /* @__PURE__ */ jsx(Select.Item, { value: kind.value, children: kind.label }, kind.value)) })
|
|
590
|
-
] })
|
|
591
|
-
] }),
|
|
592
|
-
/* @__PURE__ */ jsxs(Button, { onClick: handleCreateNew, children: [
|
|
593
|
-
/* @__PURE__ */ jsx(Plus, {}),
|
|
594
|
-
"สร้างพื้นที่ใหม่"
|
|
595
|
-
] })
|
|
596
|
-
] }),
|
|
597
|
-
/* @__PURE__ */ jsxs(Table, { children: [
|
|
598
|
-
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
599
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ประเภท" }),
|
|
600
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ค่า" }),
|
|
601
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
602
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
603
|
-
] }) }),
|
|
604
|
-
/* @__PURE__ */ jsx(Table.Body, { children: isLoading ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredAreas.length === 0 ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลพื้นที่บริการ" }) }) : filteredAreas.map((area) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
605
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: area.kind === "PROVINCE" ? "blue" : "purple", children: area.kind === "PROVINCE" ? "จังหวัด" : "รหัสไปรษณีย์" }) }),
|
|
606
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: area.value }),
|
|
607
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
|
|
608
|
-
Badge,
|
|
536
|
+
/* @__PURE__ */ jsx(
|
|
537
|
+
Button,
|
|
609
538
|
{
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
onClick: () =>
|
|
613
|
-
children:
|
|
539
|
+
variant: "transparent",
|
|
540
|
+
size: "small",
|
|
541
|
+
onClick: () => setDeleteId(rate.id),
|
|
542
|
+
children: /* @__PURE__ */ jsx(Trash, {})
|
|
614
543
|
}
|
|
615
|
-
)
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
Button,
|
|
619
|
-
{
|
|
620
|
-
variant: "transparent",
|
|
621
|
-
size: "small",
|
|
622
|
-
onClick: () => handleEdit(area),
|
|
623
|
-
children: /* @__PURE__ */ jsx(Edit, {})
|
|
624
|
-
}
|
|
625
|
-
),
|
|
626
|
-
/* @__PURE__ */ jsx(
|
|
627
|
-
Button,
|
|
628
|
-
{
|
|
629
|
-
variant: "transparent",
|
|
630
|
-
size: "small",
|
|
631
|
-
onClick: () => openDeletePrompt(area.id, area.value),
|
|
632
|
-
children: /* @__PURE__ */ jsx(Trash, {})
|
|
633
|
-
}
|
|
634
|
-
)
|
|
635
|
-
] }) })
|
|
636
|
-
] }, area.id)) })
|
|
637
|
-
] })
|
|
544
|
+
)
|
|
545
|
+
] }) })
|
|
546
|
+
] }, rate.id)) })
|
|
638
547
|
] }),
|
|
639
|
-
isModalOpen && /* @__PURE__ */ jsx(
|
|
548
|
+
isModalOpen && /* @__PURE__ */ jsx(MessengerBaseRateModal, { rate: editingRate, onClose: handleModalClose }),
|
|
640
549
|
/* @__PURE__ */ jsx(
|
|
641
550
|
Prompt,
|
|
642
551
|
{
|
|
643
552
|
variant: "confirmation",
|
|
644
|
-
open: !!
|
|
645
|
-
onOpenChange: () =>
|
|
646
|
-
setDeleteAreaId(null);
|
|
647
|
-
setDeleteAreaValue("");
|
|
648
|
-
},
|
|
553
|
+
open: !!deleteId,
|
|
554
|
+
onOpenChange: () => setDeleteId(null),
|
|
649
555
|
children: /* @__PURE__ */ jsxs(Prompt.Content, { children: [
|
|
650
556
|
/* @__PURE__ */ jsxs(Prompt.Header, { children: [
|
|
651
|
-
/* @__PURE__ */ jsx(Prompt.Title, { children: "
|
|
652
|
-
/* @__PURE__ */
|
|
653
|
-
'คุณต้องการลบพื้นที่ "',
|
|
654
|
-
deleteAreaValue,
|
|
655
|
-
'" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
|
|
656
|
-
] })
|
|
557
|
+
/* @__PURE__ */ jsx(Prompt.Title, { children: "ลบฐานราคา" }),
|
|
558
|
+
/* @__PURE__ */ jsx(Prompt.Description, { children: "คุณต้องการลบช่วงราคานี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
|
|
657
559
|
] }),
|
|
658
560
|
/* @__PURE__ */ jsxs(Prompt.Footer, { children: [
|
|
659
561
|
/* @__PURE__ */ jsx(Prompt.Cancel, { children: "ยกเลิก" }),
|
|
@@ -664,60 +566,69 @@ const ServiceAreasTable = () => {
|
|
|
664
566
|
)
|
|
665
567
|
] });
|
|
666
568
|
};
|
|
667
|
-
const
|
|
668
|
-
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-
|
|
669
|
-
/* @__PURE__ */
|
|
670
|
-
|
|
569
|
+
const BasePricingPage = () => {
|
|
570
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-6", children: [
|
|
571
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
572
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "ฐานราคาตามระยะทาง (Messenger)" }),
|
|
573
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "ตั้งราคาฐานสำหรับบริการ Messenger 3 ชั่วโมงตามช่วงระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
|
|
574
|
+
] }),
|
|
575
|
+
/* @__PURE__ */ jsx(MessengerBasePricingTable, {})
|
|
671
576
|
] }) });
|
|
672
577
|
};
|
|
673
578
|
const config$4 = defineRouteConfig({
|
|
674
|
-
icon:
|
|
675
|
-
label: "
|
|
579
|
+
icon: Directions,
|
|
580
|
+
label: "ฐานราคาตามระยะทาง"
|
|
676
581
|
});
|
|
677
|
-
const
|
|
678
|
-
rate,
|
|
679
|
-
onClose
|
|
680
|
-
}) => {
|
|
582
|
+
const ParcelBoxModal = ({ box, onClose }) => {
|
|
681
583
|
const [formData, setFormData] = useState({
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
584
|
+
name: "",
|
|
585
|
+
width_cm: "",
|
|
586
|
+
length_cm: "",
|
|
587
|
+
height_cm: "",
|
|
588
|
+
max_weight_kg: "",
|
|
589
|
+
price_thb: "",
|
|
686
590
|
active: true
|
|
687
591
|
});
|
|
688
592
|
const [isSaving, setIsSaving] = useState(false);
|
|
689
593
|
useEffect(() => {
|
|
690
|
-
|
|
691
|
-
if (rate) {
|
|
594
|
+
if (box) {
|
|
692
595
|
setFormData({
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
596
|
+
name: box.name,
|
|
597
|
+
width_cm: box.width_cm.toString(),
|
|
598
|
+
length_cm: box.length_cm.toString(),
|
|
599
|
+
height_cm: box.height_cm.toString(),
|
|
600
|
+
max_weight_kg: box.max_weight_kg.toString(),
|
|
601
|
+
price_thb: box.price_thb.toString(),
|
|
602
|
+
active: box.active
|
|
698
603
|
});
|
|
699
604
|
}
|
|
700
|
-
}, [
|
|
605
|
+
}, [box]);
|
|
701
606
|
const handleSubmit = async (e) => {
|
|
702
607
|
e.preventDefault();
|
|
703
608
|
const errors = [];
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
const maxDistance = hasMaxDistance ? parseFloat(formData.max_distance_km) : void 0;
|
|
707
|
-
const price = parseFloat(formData.price);
|
|
708
|
-
const priority = parseInt(formData.priority || "0", 10);
|
|
709
|
-
if (isNaN(minDistance) || minDistance < 0) {
|
|
710
|
-
errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
|
|
609
|
+
if (!formData.name.trim()) {
|
|
610
|
+
errors.push("กรุณากรอกชื่อกล่อง");
|
|
711
611
|
}
|
|
712
|
-
|
|
713
|
-
|
|
612
|
+
const width = parseFloat(formData.width_cm);
|
|
613
|
+
const length = parseFloat(formData.length_cm);
|
|
614
|
+
const height = parseFloat(formData.height_cm);
|
|
615
|
+
const maxWeight = parseFloat(formData.max_weight_kg);
|
|
616
|
+
const price = parseFloat(formData.price_thb);
|
|
617
|
+
if (isNaN(width) || width <= 0) {
|
|
618
|
+
errors.push("ความกว้างต้องเป็นตัวเลขมากกว่า 0");
|
|
619
|
+
}
|
|
620
|
+
if (isNaN(length) || length <= 0) {
|
|
621
|
+
errors.push("ความยาวต้องเป็นตัวเลขมากกว่า 0");
|
|
622
|
+
}
|
|
623
|
+
if (isNaN(height) || height <= 0) {
|
|
624
|
+
errors.push("ความสูงต้องเป็นตัวเลขมากกว่า 0");
|
|
625
|
+
}
|
|
626
|
+
if (isNaN(maxWeight) || maxWeight <= 0) {
|
|
627
|
+
errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
|
|
714
628
|
}
|
|
715
629
|
if (isNaN(price) || price < 0) {
|
|
716
630
|
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
717
631
|
}
|
|
718
|
-
if (isNaN(priority)) {
|
|
719
|
-
errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
|
|
720
|
-
}
|
|
721
632
|
if (errors.length > 0) {
|
|
722
633
|
toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
723
634
|
description: errors.join(", ")
|
|
@@ -727,32 +638,31 @@ const MessengerBaseRateModal = ({
|
|
|
727
638
|
setIsSaving(true);
|
|
728
639
|
try {
|
|
729
640
|
const payload = {
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
price,
|
|
736
|
-
currency: "THB",
|
|
737
|
-
priority,
|
|
641
|
+
name: formData.name.trim(),
|
|
642
|
+
width_cm: width,
|
|
643
|
+
length_cm: length,
|
|
644
|
+
height_cm: height,
|
|
645
|
+
max_weight_kg: maxWeight,
|
|
646
|
+
price_thb: price,
|
|
738
647
|
active: formData.active
|
|
739
648
|
};
|
|
740
|
-
const url =
|
|
741
|
-
const method =
|
|
649
|
+
const url = box ? `/admin/boxes/${box.id}` : "/admin/boxes";
|
|
650
|
+
const method = box ? "PUT" : "POST";
|
|
742
651
|
const response = await fetch(url, {
|
|
743
652
|
method,
|
|
744
653
|
headers: { "Content-Type": "application/json" },
|
|
745
654
|
credentials: "include",
|
|
746
655
|
body: JSON.stringify(payload)
|
|
747
656
|
});
|
|
748
|
-
if (
|
|
749
|
-
|
|
750
|
-
|
|
657
|
+
if (response.ok) {
|
|
658
|
+
toast.success("สำเร็จ", {
|
|
659
|
+
description: box ? "แก้ไขกล่องพัสดุแล้ว" : "สร้างกล่องพัสดุใหม่แล้ว"
|
|
660
|
+
});
|
|
661
|
+
onClose();
|
|
662
|
+
} else {
|
|
663
|
+
const error = await response.json();
|
|
664
|
+
throw new Error(error.message || "Failed to save");
|
|
751
665
|
}
|
|
752
|
-
toast.success("สำเร็จ", {
|
|
753
|
-
description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
|
|
754
|
-
});
|
|
755
|
-
onClose();
|
|
756
666
|
} catch (error) {
|
|
757
667
|
toast.error("ข้อผิดพลาด", {
|
|
758
668
|
description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
|
|
@@ -762,41 +672,83 @@ const MessengerBaseRateModal = ({
|
|
|
762
672
|
}
|
|
763
673
|
};
|
|
764
674
|
return /* @__PURE__ */ jsx(Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(Drawer.Content, { children: [
|
|
765
|
-
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children:
|
|
675
|
+
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: box ? "แก้ไขกล่องพัสดุ" : "สร้างกล่องพัสดุใหม่" }) }),
|
|
766
676
|
/* @__PURE__ */ jsx(Drawer.Body, { children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
767
|
-
/* @__PURE__ */ jsxs("div", {
|
|
677
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
678
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "name", children: "ชื่อกล่อง *" }),
|
|
679
|
+
/* @__PURE__ */ jsx(
|
|
680
|
+
Input,
|
|
681
|
+
{
|
|
682
|
+
id: "name",
|
|
683
|
+
value: formData.name,
|
|
684
|
+
onChange: (e) => setFormData({ ...formData, name: e.target.value }),
|
|
685
|
+
placeholder: "เช่น กล่อง S, กล่อง M",
|
|
686
|
+
required: true
|
|
687
|
+
}
|
|
688
|
+
)
|
|
689
|
+
] }),
|
|
690
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-x-4", children: [
|
|
768
691
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
769
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
692
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "width", children: "ความกว้าง (cm) *" }),
|
|
770
693
|
/* @__PURE__ */ jsx(
|
|
771
694
|
Input,
|
|
772
695
|
{
|
|
773
|
-
id: "
|
|
696
|
+
id: "width",
|
|
774
697
|
type: "number",
|
|
775
|
-
min: "0",
|
|
776
698
|
step: "0.1",
|
|
777
|
-
value: formData.
|
|
778
|
-
onChange: (e) => setFormData({ ...formData,
|
|
699
|
+
value: formData.width_cm,
|
|
700
|
+
onChange: (e) => setFormData({ ...formData, width_cm: e.target.value }),
|
|
701
|
+
placeholder: "20",
|
|
779
702
|
required: true
|
|
780
703
|
}
|
|
781
704
|
)
|
|
782
705
|
] }),
|
|
783
706
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
784
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
707
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "length", children: "ความยาว (cm) *" }),
|
|
785
708
|
/* @__PURE__ */ jsx(
|
|
786
709
|
Input,
|
|
787
710
|
{
|
|
788
|
-
id: "
|
|
711
|
+
id: "length",
|
|
789
712
|
type: "number",
|
|
790
|
-
min: "0",
|
|
791
713
|
step: "0.1",
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
714
|
+
value: formData.length_cm,
|
|
715
|
+
onChange: (e) => setFormData({ ...formData, length_cm: e.target.value }),
|
|
716
|
+
placeholder: "30",
|
|
717
|
+
required: true
|
|
795
718
|
}
|
|
796
|
-
)
|
|
797
|
-
|
|
719
|
+
)
|
|
720
|
+
] }),
|
|
721
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
722
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "height", children: "ความสูง (cm) *" }),
|
|
723
|
+
/* @__PURE__ */ jsx(
|
|
724
|
+
Input,
|
|
725
|
+
{
|
|
726
|
+
id: "height",
|
|
727
|
+
type: "number",
|
|
728
|
+
step: "0.1",
|
|
729
|
+
value: formData.height_cm,
|
|
730
|
+
onChange: (e) => setFormData({ ...formData, height_cm: e.target.value }),
|
|
731
|
+
placeholder: "15",
|
|
732
|
+
required: true
|
|
733
|
+
}
|
|
734
|
+
)
|
|
798
735
|
] })
|
|
799
736
|
] }),
|
|
737
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
738
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
|
|
739
|
+
/* @__PURE__ */ jsx(
|
|
740
|
+
Input,
|
|
741
|
+
{
|
|
742
|
+
id: "maxWeight",
|
|
743
|
+
type: "number",
|
|
744
|
+
step: "0.1",
|
|
745
|
+
value: formData.max_weight_kg,
|
|
746
|
+
onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
|
|
747
|
+
placeholder: "0.5",
|
|
748
|
+
required: true
|
|
749
|
+
}
|
|
750
|
+
)
|
|
751
|
+
] }),
|
|
800
752
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
801
753
|
/* @__PURE__ */ jsx(Label, { htmlFor: "price", children: "ราคา (THB) *" }),
|
|
802
754
|
/* @__PURE__ */ jsx(
|
|
@@ -804,28 +756,14 @@ const MessengerBaseRateModal = ({
|
|
|
804
756
|
{
|
|
805
757
|
id: "price",
|
|
806
758
|
type: "number",
|
|
807
|
-
min: "0",
|
|
808
759
|
step: "0.01",
|
|
809
|
-
value: formData.
|
|
810
|
-
onChange: (e) => setFormData({ ...formData,
|
|
760
|
+
value: formData.price_thb,
|
|
761
|
+
onChange: (e) => setFormData({ ...formData, price_thb: e.target.value }),
|
|
762
|
+
placeholder: "10.00",
|
|
811
763
|
required: true
|
|
812
764
|
}
|
|
813
765
|
)
|
|
814
766
|
] }),
|
|
815
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
816
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
|
|
817
|
-
/* @__PURE__ */ jsx(
|
|
818
|
-
Input,
|
|
819
|
-
{
|
|
820
|
-
id: "priority",
|
|
821
|
-
type: "number",
|
|
822
|
-
value: formData.priority,
|
|
823
|
-
onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
|
|
824
|
-
placeholder: "0"
|
|
825
|
-
}
|
|
826
|
-
),
|
|
827
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
|
|
828
|
-
] }),
|
|
829
767
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
830
768
|
/* @__PURE__ */ jsx(
|
|
831
769
|
Switch,
|
|
@@ -838,163 +776,197 @@ const MessengerBaseRateModal = ({
|
|
|
838
776
|
/* @__PURE__ */ jsx(Label, { htmlFor: "active", children: "เปิดใช้งาน" })
|
|
839
777
|
] }),
|
|
840
778
|
/* @__PURE__ */ jsxs(Drawer.Footer, { children: [
|
|
841
|
-
/* @__PURE__ */ jsx(
|
|
842
|
-
Button,
|
|
843
|
-
{
|
|
844
|
-
type: "button",
|
|
845
|
-
variant: "secondary",
|
|
846
|
-
onClick: onClose,
|
|
847
|
-
disabled: isSaving,
|
|
848
|
-
children: "ยกเลิก"
|
|
849
|
-
}
|
|
850
|
-
),
|
|
779
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
|
|
851
780
|
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
|
|
852
781
|
] })
|
|
853
782
|
] }) })
|
|
854
783
|
] }) });
|
|
855
784
|
};
|
|
856
|
-
const
|
|
857
|
-
const [
|
|
858
|
-
const [
|
|
785
|
+
const ParcelBoxesTable = () => {
|
|
786
|
+
const [boxes, setBoxes] = useState([]);
|
|
787
|
+
const [filteredBoxes, setFilteredBoxes] = useState([]);
|
|
788
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
859
789
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
860
|
-
const [
|
|
861
|
-
const [
|
|
790
|
+
const [editingBox, setEditingBox] = useState(null);
|
|
791
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
792
|
+
const [deleteBoxId, setDeleteBoxId] = useState(null);
|
|
793
|
+
const [deleteBoxName, setDeleteBoxName] = useState("");
|
|
862
794
|
useEffect(() => {
|
|
863
|
-
|
|
795
|
+
fetchBoxes();
|
|
864
796
|
}, []);
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
return aMax - bMax;
|
|
873
|
-
});
|
|
874
|
-
}, [rates]);
|
|
875
|
-
const fetchRates = async () => {
|
|
797
|
+
useEffect(() => {
|
|
798
|
+
const filtered = boxes.filter(
|
|
799
|
+
(box) => box.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
800
|
+
);
|
|
801
|
+
setFilteredBoxes(filtered);
|
|
802
|
+
}, [searchTerm, boxes]);
|
|
803
|
+
const fetchBoxes = async () => {
|
|
876
804
|
setIsLoading(true);
|
|
877
805
|
try {
|
|
878
|
-
const response = await fetch(
|
|
879
|
-
"
|
|
880
|
-
|
|
881
|
-
credentials: "include"
|
|
882
|
-
}
|
|
883
|
-
);
|
|
884
|
-
if (!response.ok) {
|
|
885
|
-
throw new Error("failed_to_load_base_rates");
|
|
886
|
-
}
|
|
806
|
+
const response = await fetch("/admin/boxes", {
|
|
807
|
+
credentials: "include"
|
|
808
|
+
});
|
|
887
809
|
const data = await response.json();
|
|
888
|
-
|
|
810
|
+
setBoxes(data.boxes || []);
|
|
889
811
|
} catch (error) {
|
|
890
812
|
toast.error("ข้อผิดพลาด", {
|
|
891
|
-
description: "
|
|
813
|
+
description: "ไม่สามารถโหลดข้อมูลกล่องพัสดุได้"
|
|
892
814
|
});
|
|
893
815
|
} finally {
|
|
894
816
|
setIsLoading(false);
|
|
895
817
|
}
|
|
896
818
|
};
|
|
897
|
-
const
|
|
898
|
-
if (!deleteId) return;
|
|
819
|
+
const handleToggleActive = async (box) => {
|
|
899
820
|
try {
|
|
900
|
-
const response = await fetch(
|
|
901
|
-
|
|
902
|
-
{
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
)
|
|
907
|
-
|
|
908
|
-
|
|
821
|
+
const response = await fetch(`/admin/boxes/${box.id}`, {
|
|
822
|
+
method: "PUT",
|
|
823
|
+
headers: { "Content-Type": "application/json" },
|
|
824
|
+
credentials: "include",
|
|
825
|
+
body: JSON.stringify({ ...box, active: !box.active })
|
|
826
|
+
});
|
|
827
|
+
if (response.ok) {
|
|
828
|
+
toast.success("สำเร็จ", {
|
|
829
|
+
description: `${box.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}กล่อง ${box.name} แล้ว`
|
|
830
|
+
});
|
|
831
|
+
fetchBoxes();
|
|
832
|
+
} else {
|
|
833
|
+
throw new Error("Failed to update");
|
|
909
834
|
}
|
|
910
|
-
|
|
911
|
-
|
|
835
|
+
} catch (error) {
|
|
836
|
+
toast.error("ข้อผิดพลาด", {
|
|
837
|
+
description: "ไม่สามารถอัพเดทสถานะได้"
|
|
912
838
|
});
|
|
913
|
-
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
const handleDelete = async () => {
|
|
842
|
+
if (!deleteBoxId) return;
|
|
843
|
+
try {
|
|
844
|
+
const response = await fetch(`/admin/boxes/${deleteBoxId}`, {
|
|
845
|
+
method: "DELETE",
|
|
846
|
+
credentials: "include"
|
|
847
|
+
});
|
|
848
|
+
if (response.ok) {
|
|
849
|
+
toast.success("สำเร็จ", {
|
|
850
|
+
description: `ลบกล่อง ${deleteBoxName} แล้ว`
|
|
851
|
+
});
|
|
852
|
+
fetchBoxes();
|
|
853
|
+
} else {
|
|
854
|
+
throw new Error("Failed to delete");
|
|
855
|
+
}
|
|
914
856
|
} catch (error) {
|
|
915
857
|
toast.error("ข้อผิดพลาด", {
|
|
916
|
-
description: "
|
|
858
|
+
description: "ไม่สามารถลบกล่องได้"
|
|
917
859
|
});
|
|
918
860
|
} finally {
|
|
919
|
-
|
|
861
|
+
setDeleteBoxId(null);
|
|
862
|
+
setDeleteBoxName("");
|
|
920
863
|
}
|
|
921
864
|
};
|
|
865
|
+
const handleEdit = (box) => {
|
|
866
|
+
setEditingBox(box);
|
|
867
|
+
setIsModalOpen(true);
|
|
868
|
+
};
|
|
869
|
+
const handleCreateNew = () => {
|
|
870
|
+
setEditingBox(null);
|
|
871
|
+
setIsModalOpen(true);
|
|
872
|
+
};
|
|
922
873
|
const handleModalClose = () => {
|
|
923
874
|
setIsModalOpen(false);
|
|
924
|
-
|
|
925
|
-
|
|
875
|
+
setEditingBox(null);
|
|
876
|
+
fetchBoxes();
|
|
926
877
|
};
|
|
927
|
-
const
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
if (max === void 0 || max === null) {
|
|
931
|
-
return `${min}+ กม.`;
|
|
932
|
-
}
|
|
933
|
-
return `${min}-${max} กม.`;
|
|
878
|
+
const openDeletePrompt = (id, name) => {
|
|
879
|
+
setDeleteBoxId(id);
|
|
880
|
+
setDeleteBoxName(name);
|
|
934
881
|
};
|
|
935
882
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
936
|
-
/* @__PURE__ */ jsxs("div", { className: "flex
|
|
937
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
938
|
-
/* @__PURE__ */ jsx(
|
|
939
|
-
|
|
883
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
884
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
885
|
+
/* @__PURE__ */ jsx(
|
|
886
|
+
Input,
|
|
887
|
+
{
|
|
888
|
+
type: "search",
|
|
889
|
+
placeholder: "ค้นหาชื่อกล่อง...",
|
|
890
|
+
value: searchTerm,
|
|
891
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
892
|
+
className: "max-w-md"
|
|
893
|
+
}
|
|
894
|
+
),
|
|
895
|
+
/* @__PURE__ */ jsxs(Button, { onClick: handleCreateNew, children: [
|
|
896
|
+
/* @__PURE__ */ jsx(Plus, {}),
|
|
897
|
+
"สร้างกล่องใหม่"
|
|
898
|
+
] })
|
|
940
899
|
] }),
|
|
941
|
-
/* @__PURE__ */ jsxs(
|
|
942
|
-
/* @__PURE__ */ jsx(
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
/* @__PURE__ */ jsx(Table.
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
/* @__PURE__ */ jsx(
|
|
964
|
-
Button,
|
|
965
|
-
{
|
|
966
|
-
variant: "transparent",
|
|
967
|
-
size: "small",
|
|
968
|
-
onClick: () => {
|
|
969
|
-
setEditingRate(rate);
|
|
970
|
-
setIsModalOpen(true);
|
|
971
|
-
},
|
|
972
|
-
children: /* @__PURE__ */ jsx(Edit, {})
|
|
973
|
-
}
|
|
974
|
-
),
|
|
975
|
-
/* @__PURE__ */ jsx(
|
|
976
|
-
Button,
|
|
900
|
+
/* @__PURE__ */ jsxs(Table, { children: [
|
|
901
|
+
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
902
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ชื่อ" }),
|
|
903
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ขนาด (กว้าง×ยาว×สูง cm)" }),
|
|
904
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "น้ำหนักสูงสุด (kg)" }),
|
|
905
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
906
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
907
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
908
|
+
] }) }),
|
|
909
|
+
/* @__PURE__ */ jsx(Table.Body, { children: isLoading ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredBoxes.length === 0 ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลกล่องพัสดุ" }) }) : filteredBoxes.map((box) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
910
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: box.name }),
|
|
911
|
+
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
912
|
+
box.width_cm,
|
|
913
|
+
" × ",
|
|
914
|
+
box.length_cm,
|
|
915
|
+
" × ",
|
|
916
|
+
box.height_cm
|
|
917
|
+
] }),
|
|
918
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: box.max_weight_kg }),
|
|
919
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: box.price_thb.toFixed(2) }),
|
|
920
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
|
|
921
|
+
Badge,
|
|
977
922
|
{
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
onClick: () =>
|
|
981
|
-
children:
|
|
923
|
+
color: box.active ? "green" : "grey",
|
|
924
|
+
className: "cursor-pointer",
|
|
925
|
+
onClick: () => handleToggleActive(box),
|
|
926
|
+
children: box.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
982
927
|
}
|
|
983
|
-
)
|
|
984
|
-
|
|
985
|
-
|
|
928
|
+
) }),
|
|
929
|
+
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
930
|
+
/* @__PURE__ */ jsx(
|
|
931
|
+
Button,
|
|
932
|
+
{
|
|
933
|
+
variant: "transparent",
|
|
934
|
+
size: "small",
|
|
935
|
+
onClick: () => handleEdit(box),
|
|
936
|
+
children: /* @__PURE__ */ jsx(Edit, {})
|
|
937
|
+
}
|
|
938
|
+
),
|
|
939
|
+
/* @__PURE__ */ jsx(
|
|
940
|
+
Button,
|
|
941
|
+
{
|
|
942
|
+
variant: "transparent",
|
|
943
|
+
size: "small",
|
|
944
|
+
onClick: () => openDeletePrompt(box.id, box.name),
|
|
945
|
+
children: /* @__PURE__ */ jsx(Trash, {})
|
|
946
|
+
}
|
|
947
|
+
)
|
|
948
|
+
] }) })
|
|
949
|
+
] }, box.id)) })
|
|
950
|
+
] })
|
|
986
951
|
] }),
|
|
987
|
-
isModalOpen && /* @__PURE__ */ jsx(
|
|
952
|
+
isModalOpen && /* @__PURE__ */ jsx(ParcelBoxModal, { box: editingBox, onClose: handleModalClose }),
|
|
988
953
|
/* @__PURE__ */ jsx(
|
|
989
954
|
Prompt,
|
|
990
955
|
{
|
|
991
956
|
variant: "confirmation",
|
|
992
|
-
open: !!
|
|
993
|
-
onOpenChange: () =>
|
|
957
|
+
open: !!deleteBoxId,
|
|
958
|
+
onOpenChange: () => {
|
|
959
|
+
setDeleteBoxId(null);
|
|
960
|
+
setDeleteBoxName("");
|
|
961
|
+
},
|
|
994
962
|
children: /* @__PURE__ */ jsxs(Prompt.Content, { children: [
|
|
995
963
|
/* @__PURE__ */ jsxs(Prompt.Header, { children: [
|
|
996
|
-
/* @__PURE__ */ jsx(Prompt.Title, { children: "
|
|
997
|
-
/* @__PURE__ */
|
|
964
|
+
/* @__PURE__ */ jsx(Prompt.Title, { children: "ลบกล่องพัสดุ" }),
|
|
965
|
+
/* @__PURE__ */ jsxs(Prompt.Description, { children: [
|
|
966
|
+
'คุณต้องการลบกล่อง "',
|
|
967
|
+
deleteBoxName,
|
|
968
|
+
'" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
|
|
969
|
+
] })
|
|
998
970
|
] }),
|
|
999
971
|
/* @__PURE__ */ jsxs(Prompt.Footer, { children: [
|
|
1000
972
|
/* @__PURE__ */ jsx(Prompt.Cancel, { children: "ยกเลิก" }),
|
|
@@ -1005,68 +977,126 @@ const MessengerBasePricingTable = () => {
|
|
|
1005
977
|
)
|
|
1006
978
|
] });
|
|
1007
979
|
};
|
|
1008
|
-
const
|
|
1009
|
-
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-
|
|
1010
|
-
/* @__PURE__ */
|
|
1011
|
-
|
|
1012
|
-
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "ตั้งราคาฐานสำหรับบริการ Messenger 3 ชั่วโมงตามช่วงระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
|
|
1013
|
-
] }),
|
|
1014
|
-
/* @__PURE__ */ jsx(MessengerBasePricingTable, {})
|
|
980
|
+
const BoxesPage = () => {
|
|
981
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
982
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "กล่องพัสดุ" }) }),
|
|
983
|
+
/* @__PURE__ */ jsx(ParcelBoxesTable, {})
|
|
1015
984
|
] }) });
|
|
1016
985
|
};
|
|
1017
986
|
const config$3 = defineRouteConfig({
|
|
1018
|
-
icon:
|
|
1019
|
-
label: "
|
|
987
|
+
icon: ArchiveBox,
|
|
988
|
+
label: "กล่องพัสดุ"
|
|
1020
989
|
});
|
|
1021
|
-
const
|
|
990
|
+
const TH_PROVINCES = [
|
|
991
|
+
"กรุงเทพมหานคร",
|
|
992
|
+
"กระบี่",
|
|
993
|
+
"กาญจนบุรี",
|
|
994
|
+
"กาฬสินธุ์",
|
|
995
|
+
"กำแพงเพชร",
|
|
996
|
+
"ขอนแก่น",
|
|
997
|
+
"จันทบุรี",
|
|
998
|
+
"ฉะเชิงเทรา",
|
|
999
|
+
"ชลบุรี",
|
|
1000
|
+
"ชัยนาท",
|
|
1001
|
+
"ชัยภูมิ",
|
|
1002
|
+
"ชุมพร",
|
|
1003
|
+
"เชียงราย",
|
|
1004
|
+
"เชียงใหม่",
|
|
1005
|
+
"ตรัง",
|
|
1006
|
+
"ตราด",
|
|
1007
|
+
"ตาก",
|
|
1008
|
+
"นครนายก",
|
|
1009
|
+
"นครปฐม",
|
|
1010
|
+
"นครพนม",
|
|
1011
|
+
"นครราชสีมา",
|
|
1012
|
+
"นครศรีธรรมราช",
|
|
1013
|
+
"นครสวรรค์",
|
|
1014
|
+
"นนทบุรี",
|
|
1015
|
+
"นราธิวาส",
|
|
1016
|
+
"น่าน",
|
|
1017
|
+
"บึงกาฬ",
|
|
1018
|
+
"บุรีรัมย์",
|
|
1019
|
+
"ปทุมธานี",
|
|
1020
|
+
"ประจวบคีรีขันธ์",
|
|
1021
|
+
"ปราจีนบุรี",
|
|
1022
|
+
"ปัตตานี",
|
|
1023
|
+
"พระนครศรีอยุธยา",
|
|
1024
|
+
"พะเยา",
|
|
1025
|
+
"พังงา",
|
|
1026
|
+
"พัทลุง",
|
|
1027
|
+
"พิจิตร",
|
|
1028
|
+
"พิษณุโลก",
|
|
1029
|
+
"เพชรบุรี",
|
|
1030
|
+
"เพชรบูรณ์",
|
|
1031
|
+
"แพร่",
|
|
1032
|
+
"ภูเก็ต",
|
|
1033
|
+
"มหาสารคาม",
|
|
1034
|
+
"มุกดาหาร",
|
|
1035
|
+
"แม่ฮ่องสอน",
|
|
1036
|
+
"ยโสธร",
|
|
1037
|
+
"ยะลา",
|
|
1038
|
+
"ร้อยเอ็ด",
|
|
1039
|
+
"ระนอง",
|
|
1040
|
+
"ระยอง",
|
|
1041
|
+
"ราชบุรี",
|
|
1042
|
+
"ลพบุรี",
|
|
1043
|
+
"ลำปาง",
|
|
1044
|
+
"ลำพูน",
|
|
1045
|
+
"เลย",
|
|
1046
|
+
"ศรีสะเกษ",
|
|
1047
|
+
"สกลนคร",
|
|
1048
|
+
"สงขลา",
|
|
1049
|
+
"สตูล",
|
|
1050
|
+
"สมุทรปราการ",
|
|
1051
|
+
"สมุทรสงคราม",
|
|
1052
|
+
"สมุทรสาคร",
|
|
1053
|
+
"สระแก้ว",
|
|
1054
|
+
"สระบุรี",
|
|
1055
|
+
"สิงห์บุรี",
|
|
1056
|
+
"สุโขทัย",
|
|
1057
|
+
"สุพรรณบุรี",
|
|
1058
|
+
"สุราษฎร์ธานี",
|
|
1059
|
+
"สุรินทร์",
|
|
1060
|
+
"หนองคาย",
|
|
1061
|
+
"หนองบัวลำภู",
|
|
1062
|
+
"อ่างทอง",
|
|
1063
|
+
"อำนาจเจริญ",
|
|
1064
|
+
"อุดรธานี",
|
|
1065
|
+
"อุตรดิตถ์",
|
|
1066
|
+
"อุทัยธานี",
|
|
1067
|
+
"อุบลราชธานี"
|
|
1068
|
+
];
|
|
1069
|
+
const ServiceAreaModal = ({ area, onClose }) => {
|
|
1022
1070
|
const [formData, setFormData] = useState({
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
length_cm: "",
|
|
1026
|
-
height_cm: "",
|
|
1027
|
-
max_weight_kg: "",
|
|
1028
|
-
price_thb: "",
|
|
1071
|
+
kind: "PROVINCE",
|
|
1072
|
+
value: "",
|
|
1029
1073
|
active: true
|
|
1030
1074
|
});
|
|
1031
1075
|
const [isSaving, setIsSaving] = useState(false);
|
|
1032
1076
|
useEffect(() => {
|
|
1033
|
-
if (
|
|
1077
|
+
if (area) {
|
|
1034
1078
|
setFormData({
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
height_cm: box.height_cm.toString(),
|
|
1039
|
-
max_weight_kg: box.max_weight_kg.toString(),
|
|
1040
|
-
price_thb: box.price_thb.toString(),
|
|
1041
|
-
active: box.active
|
|
1079
|
+
kind: area.kind,
|
|
1080
|
+
value: area.value,
|
|
1081
|
+
active: area.active
|
|
1042
1082
|
});
|
|
1043
1083
|
}
|
|
1044
|
-
}, [
|
|
1084
|
+
}, [area]);
|
|
1045
1085
|
const handleSubmit = async (e) => {
|
|
1046
1086
|
e.preventDefault();
|
|
1047
1087
|
const errors = [];
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
const width = parseFloat(formData.width_cm);
|
|
1052
|
-
const length = parseFloat(formData.length_cm);
|
|
1053
|
-
const height = parseFloat(formData.height_cm);
|
|
1054
|
-
const maxWeight = parseFloat(formData.max_weight_kg);
|
|
1055
|
-
const price = parseFloat(formData.price_thb);
|
|
1056
|
-
if (isNaN(width) || width <= 0) {
|
|
1057
|
-
errors.push("ความกว้างต้องเป็นตัวเลขมากกว่า 0");
|
|
1058
|
-
}
|
|
1059
|
-
if (isNaN(length) || length <= 0) {
|
|
1060
|
-
errors.push("ความยาวต้องเป็นตัวเลขมากกว่า 0");
|
|
1061
|
-
}
|
|
1062
|
-
if (isNaN(height) || height <= 0) {
|
|
1063
|
-
errors.push("ความสูงต้องเป็นตัวเลขมากกว่า 0");
|
|
1064
|
-
}
|
|
1065
|
-
if (isNaN(maxWeight) || maxWeight <= 0) {
|
|
1066
|
-
errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
|
|
1088
|
+
const trimmedValue = formData.value.trim();
|
|
1089
|
+
if (!trimmedValue) {
|
|
1090
|
+
errors.push("กรุณากรอกค่า");
|
|
1067
1091
|
}
|
|
1068
|
-
if (
|
|
1069
|
-
|
|
1092
|
+
if (formData.kind === "PROVINCE") {
|
|
1093
|
+
if (!TH_PROVINCES.includes(trimmedValue)) {
|
|
1094
|
+
errors.push("จังหวัดไม่ถูกต้อง กรุณาตรวจสอบการสะกดชื่อจังหวัด");
|
|
1095
|
+
}
|
|
1096
|
+
} else if (formData.kind === "POSTCODE_PREFIX") {
|
|
1097
|
+
if (!/^\d{1,5}$/.test(trimmedValue)) {
|
|
1098
|
+
errors.push("รหัสไปรษณีย์ต้องเป็นตัวเลข 1-5 หลัก");
|
|
1099
|
+
}
|
|
1070
1100
|
}
|
|
1071
1101
|
if (errors.length > 0) {
|
|
1072
1102
|
toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
@@ -1077,16 +1107,12 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
1077
1107
|
setIsSaving(true);
|
|
1078
1108
|
try {
|
|
1079
1109
|
const payload = {
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
length_cm: length,
|
|
1083
|
-
height_cm: height,
|
|
1084
|
-
max_weight_kg: maxWeight,
|
|
1085
|
-
price_thb: price,
|
|
1110
|
+
kind: formData.kind,
|
|
1111
|
+
value: trimmedValue,
|
|
1086
1112
|
active: formData.active
|
|
1087
1113
|
};
|
|
1088
|
-
const url =
|
|
1089
|
-
const method =
|
|
1114
|
+
const url = area ? `/admin/service-areas/${area.id}` : "/admin/service-areas";
|
|
1115
|
+
const method = area ? "PUT" : "POST";
|
|
1090
1116
|
const response = await fetch(url, {
|
|
1091
1117
|
method,
|
|
1092
1118
|
headers: { "Content-Type": "application/json" },
|
|
@@ -1095,7 +1121,7 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
1095
1121
|
});
|
|
1096
1122
|
if (response.ok) {
|
|
1097
1123
|
toast.success("สำเร็จ", {
|
|
1098
|
-
description:
|
|
1124
|
+
description: area ? "แก้ไขพื้นที่บริการแล้ว" : "สร้างพื้นที่บริการใหม่แล้ว"
|
|
1099
1125
|
});
|
|
1100
1126
|
onClose();
|
|
1101
1127
|
} else {
|
|
@@ -1111,97 +1137,53 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
1111
1137
|
}
|
|
1112
1138
|
};
|
|
1113
1139
|
return /* @__PURE__ */ jsx(Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(Drawer.Content, { children: [
|
|
1114
|
-
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children:
|
|
1115
|
-
/* @__PURE__ */ jsx(Drawer.Body, { children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
1116
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1117
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "name", children: "ชื่อกล่อง *" }),
|
|
1118
|
-
/* @__PURE__ */ jsx(
|
|
1119
|
-
Input,
|
|
1120
|
-
{
|
|
1121
|
-
id: "name",
|
|
1122
|
-
value: formData.name,
|
|
1123
|
-
onChange: (e) => setFormData({ ...formData, name: e.target.value }),
|
|
1124
|
-
placeholder: "เช่น กล่อง S, กล่อง M",
|
|
1125
|
-
required: true
|
|
1126
|
-
}
|
|
1127
|
-
)
|
|
1128
|
-
] }),
|
|
1129
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-x-4", children: [
|
|
1130
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1131
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "width", children: "ความกว้าง (cm) *" }),
|
|
1132
|
-
/* @__PURE__ */ jsx(
|
|
1133
|
-
Input,
|
|
1134
|
-
{
|
|
1135
|
-
id: "width",
|
|
1136
|
-
type: "number",
|
|
1137
|
-
step: "0.1",
|
|
1138
|
-
value: formData.width_cm,
|
|
1139
|
-
onChange: (e) => setFormData({ ...formData, width_cm: e.target.value }),
|
|
1140
|
-
placeholder: "20",
|
|
1141
|
-
required: true
|
|
1142
|
-
}
|
|
1143
|
-
)
|
|
1144
|
-
] }),
|
|
1145
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1146
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "length", children: "ความยาว (cm) *" }),
|
|
1147
|
-
/* @__PURE__ */ jsx(
|
|
1148
|
-
Input,
|
|
1149
|
-
{
|
|
1150
|
-
id: "length",
|
|
1151
|
-
type: "number",
|
|
1152
|
-
step: "0.1",
|
|
1153
|
-
value: formData.length_cm,
|
|
1154
|
-
onChange: (e) => setFormData({ ...formData, length_cm: e.target.value }),
|
|
1155
|
-
placeholder: "30",
|
|
1156
|
-
required: true
|
|
1157
|
-
}
|
|
1158
|
-
)
|
|
1159
|
-
] }),
|
|
1160
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1161
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "height", children: "ความสูง (cm) *" }),
|
|
1162
|
-
/* @__PURE__ */ jsx(
|
|
1163
|
-
Input,
|
|
1164
|
-
{
|
|
1165
|
-
id: "height",
|
|
1166
|
-
type: "number",
|
|
1167
|
-
step: "0.1",
|
|
1168
|
-
value: formData.height_cm,
|
|
1169
|
-
onChange: (e) => setFormData({ ...formData, height_cm: e.target.value }),
|
|
1170
|
-
placeholder: "15",
|
|
1171
|
-
required: true
|
|
1172
|
-
}
|
|
1173
|
-
)
|
|
1174
|
-
] })
|
|
1175
|
-
] }),
|
|
1140
|
+
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: area ? "แก้ไขพื้นที่บริการ" : "สร้างพื้นที่บริการใหม่" }) }),
|
|
1141
|
+
/* @__PURE__ */ jsx(Drawer.Body, { children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
1176
1142
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1177
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
1178
|
-
/* @__PURE__ */
|
|
1179
|
-
|
|
1143
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "kind", children: "ประเภท *" }),
|
|
1144
|
+
/* @__PURE__ */ jsxs(
|
|
1145
|
+
Select,
|
|
1180
1146
|
{
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1147
|
+
value: formData.kind,
|
|
1148
|
+
onValueChange: (value) => setFormData({ ...formData, kind: value, value: "" }),
|
|
1149
|
+
required: true,
|
|
1150
|
+
children: [
|
|
1151
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกประเภท" }) }),
|
|
1152
|
+
/* @__PURE__ */ jsxs(Select.Content, { children: [
|
|
1153
|
+
/* @__PURE__ */ jsx(Select.Item, { value: "PROVINCE", children: "จังหวัด" }),
|
|
1154
|
+
/* @__PURE__ */ jsx(Select.Item, { value: "POSTCODE_PREFIX", children: "รหัสไปรษณีย์" })
|
|
1155
|
+
] })
|
|
1156
|
+
]
|
|
1188
1157
|
}
|
|
1189
1158
|
)
|
|
1190
1159
|
] }),
|
|
1191
1160
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1192
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
1193
|
-
/* @__PURE__ */
|
|
1161
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "value", children: formData.kind === "PROVINCE" ? "ชื่อจังหวัด *" : "รหัสไปรษณีย์ (1-5 หลัก) *" }),
|
|
1162
|
+
formData.kind === "PROVINCE" ? /* @__PURE__ */ jsxs(
|
|
1163
|
+
Select,
|
|
1164
|
+
{
|
|
1165
|
+
value: formData.value,
|
|
1166
|
+
onValueChange: (value) => setFormData({ ...formData, value }),
|
|
1167
|
+
required: true,
|
|
1168
|
+
children: [
|
|
1169
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกจังหวัด" }) }),
|
|
1170
|
+
/* @__PURE__ */ jsx(Select.Content, { children: TH_PROVINCES.map((province) => /* @__PURE__ */ jsx(Select.Item, { value: province, children: province }, province)) })
|
|
1171
|
+
]
|
|
1172
|
+
}
|
|
1173
|
+
) : /* @__PURE__ */ jsx(
|
|
1194
1174
|
Input,
|
|
1195
1175
|
{
|
|
1196
|
-
id: "
|
|
1197
|
-
type: "
|
|
1198
|
-
|
|
1199
|
-
value:
|
|
1200
|
-
|
|
1201
|
-
|
|
1176
|
+
id: "value",
|
|
1177
|
+
type: "text",
|
|
1178
|
+
value: formData.value,
|
|
1179
|
+
onChange: (e) => setFormData({ ...formData, value: e.target.value }),
|
|
1180
|
+
placeholder: "เช่น 10, 102, 10200",
|
|
1181
|
+
maxLength: 5,
|
|
1182
|
+
pattern: "\\d{1,5}",
|
|
1202
1183
|
required: true
|
|
1203
1184
|
}
|
|
1204
|
-
)
|
|
1185
|
+
),
|
|
1186
|
+
formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
|
|
1205
1187
|
] }),
|
|
1206
1188
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
1207
1189
|
/* @__PURE__ */ jsx(
|
|
@@ -1215,59 +1197,81 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
1215
1197
|
/* @__PURE__ */ jsx(Label, { htmlFor: "active", children: "เปิดใช้งาน" })
|
|
1216
1198
|
] }),
|
|
1217
1199
|
/* @__PURE__ */ jsxs(Drawer.Footer, { children: [
|
|
1218
|
-
/* @__PURE__ */ jsx(
|
|
1200
|
+
/* @__PURE__ */ jsx(
|
|
1201
|
+
Button,
|
|
1202
|
+
{
|
|
1203
|
+
type: "button",
|
|
1204
|
+
variant: "secondary",
|
|
1205
|
+
onClick: onClose,
|
|
1206
|
+
disabled: isSaving,
|
|
1207
|
+
children: "ยกเลิก"
|
|
1208
|
+
}
|
|
1209
|
+
),
|
|
1219
1210
|
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
|
|
1220
1211
|
] })
|
|
1221
1212
|
] }) })
|
|
1222
1213
|
] }) });
|
|
1223
1214
|
};
|
|
1224
|
-
const
|
|
1225
|
-
|
|
1226
|
-
|
|
1215
|
+
const AREA_KINDS = [
|
|
1216
|
+
{ value: "ALL", label: "ทั้งหมด" },
|
|
1217
|
+
{ value: "PROVINCE", label: "จังหวัด" },
|
|
1218
|
+
{ value: "POSTCODE_PREFIX", label: "รหัสไปรษณีย์" }
|
|
1219
|
+
];
|
|
1220
|
+
const ServiceAreasTable = () => {
|
|
1221
|
+
const [areas, setAreas] = useState([]);
|
|
1222
|
+
const [filteredAreas, setFilteredAreas] = useState([]);
|
|
1227
1223
|
const [searchTerm, setSearchTerm] = useState("");
|
|
1224
|
+
const [kindFilter, setKindFilter] = useState("ALL");
|
|
1228
1225
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
1229
|
-
const [
|
|
1226
|
+
const [editingArea, setEditingArea] = useState(null);
|
|
1230
1227
|
const [isLoading, setIsLoading] = useState(false);
|
|
1231
|
-
const [
|
|
1232
|
-
const [
|
|
1228
|
+
const [deleteAreaId, setDeleteAreaId] = useState(null);
|
|
1229
|
+
const [deleteAreaValue, setDeleteAreaValue] = useState("");
|
|
1233
1230
|
useEffect(() => {
|
|
1234
|
-
|
|
1231
|
+
fetchAreas();
|
|
1235
1232
|
}, []);
|
|
1236
1233
|
useEffect(() => {
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1234
|
+
let filtered = areas;
|
|
1235
|
+
if (kindFilter !== "ALL") {
|
|
1236
|
+
filtered = filtered.filter((area) => area.kind === kindFilter);
|
|
1237
|
+
}
|
|
1238
|
+
if (searchTerm) {
|
|
1239
|
+
filtered = filtered.filter(
|
|
1240
|
+
(area) => area.value.toLowerCase().includes(searchTerm.toLowerCase())
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
setFilteredAreas(filtered);
|
|
1244
|
+
}, [searchTerm, kindFilter, areas]);
|
|
1245
|
+
const fetchAreas = async () => {
|
|
1243
1246
|
setIsLoading(true);
|
|
1244
1247
|
try {
|
|
1245
|
-
const response = await fetch("/admin/
|
|
1248
|
+
const response = await fetch("/admin/service-areas", {
|
|
1246
1249
|
credentials: "include"
|
|
1247
1250
|
});
|
|
1248
1251
|
const data = await response.json();
|
|
1249
|
-
|
|
1252
|
+
console.log("Fetched service areas:", data);
|
|
1253
|
+
setAreas(data.service_areas || []);
|
|
1250
1254
|
} catch (error) {
|
|
1251
1255
|
toast.error("ข้อผิดพลาด", {
|
|
1252
|
-
description: "
|
|
1256
|
+
description: "ไม่สามารถโหลดข้อมูลพื้นที่บริการได้"
|
|
1253
1257
|
});
|
|
1254
1258
|
} finally {
|
|
1255
1259
|
setIsLoading(false);
|
|
1256
1260
|
}
|
|
1257
1261
|
};
|
|
1258
|
-
const handleToggleActive = async (
|
|
1262
|
+
const handleToggleActive = async (area) => {
|
|
1259
1263
|
try {
|
|
1260
|
-
const response = await fetch(`/admin/
|
|
1264
|
+
const response = await fetch(`/admin/service-areas/${area.id}`, {
|
|
1261
1265
|
method: "PUT",
|
|
1262
1266
|
headers: { "Content-Type": "application/json" },
|
|
1263
1267
|
credentials: "include",
|
|
1264
|
-
body: JSON.stringify({ ...
|
|
1268
|
+
body: JSON.stringify({ ...area, active: !area.active })
|
|
1265
1269
|
});
|
|
1266
1270
|
if (response.ok) {
|
|
1267
1271
|
toast.success("สำเร็จ", {
|
|
1268
|
-
description: `${
|
|
1272
|
+
description: `${area.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}พื้นที่ ${area.value} แล้ว`
|
|
1269
1273
|
});
|
|
1270
|
-
|
|
1274
|
+
fetchAreas();
|
|
1271
1275
|
} else {
|
|
1272
1276
|
throw new Error("Failed to update");
|
|
1273
1277
|
}
|
|
@@ -1278,91 +1282,87 @@ const ParcelBoxesTable = () => {
|
|
|
1278
1282
|
}
|
|
1279
1283
|
};
|
|
1280
1284
|
const handleDelete = async () => {
|
|
1281
|
-
if (!
|
|
1285
|
+
if (!deleteAreaId) return;
|
|
1282
1286
|
try {
|
|
1283
|
-
const response = await fetch(`/admin/
|
|
1287
|
+
const response = await fetch(`/admin/service-areas/${deleteAreaId}`, {
|
|
1284
1288
|
method: "DELETE",
|
|
1285
1289
|
credentials: "include"
|
|
1286
1290
|
});
|
|
1287
1291
|
if (response.ok) {
|
|
1288
1292
|
toast.success("สำเร็จ", {
|
|
1289
|
-
description:
|
|
1293
|
+
description: `ลบพื้นที่ ${deleteAreaValue} แล้ว`
|
|
1290
1294
|
});
|
|
1291
|
-
|
|
1295
|
+
fetchAreas();
|
|
1292
1296
|
} else {
|
|
1293
1297
|
throw new Error("Failed to delete");
|
|
1294
1298
|
}
|
|
1295
1299
|
} catch (error) {
|
|
1296
1300
|
toast.error("ข้อผิดพลาด", {
|
|
1297
|
-
description: "
|
|
1301
|
+
description: "ไม่สามารถลบพื้นที่ได้"
|
|
1298
1302
|
});
|
|
1299
1303
|
} finally {
|
|
1300
|
-
|
|
1301
|
-
|
|
1304
|
+
setDeleteAreaId(null);
|
|
1305
|
+
setDeleteAreaValue("");
|
|
1302
1306
|
}
|
|
1303
1307
|
};
|
|
1304
|
-
const handleEdit = (
|
|
1305
|
-
|
|
1308
|
+
const handleEdit = (area) => {
|
|
1309
|
+
setEditingArea(area);
|
|
1306
1310
|
setIsModalOpen(true);
|
|
1307
1311
|
};
|
|
1308
1312
|
const handleCreateNew = () => {
|
|
1309
|
-
|
|
1313
|
+
setEditingArea(null);
|
|
1310
1314
|
setIsModalOpen(true);
|
|
1311
1315
|
};
|
|
1312
1316
|
const handleModalClose = () => {
|
|
1313
1317
|
setIsModalOpen(false);
|
|
1314
|
-
|
|
1315
|
-
|
|
1318
|
+
setEditingArea(null);
|
|
1319
|
+
fetchAreas();
|
|
1316
1320
|
};
|
|
1317
|
-
const openDeletePrompt = (id,
|
|
1318
|
-
|
|
1319
|
-
|
|
1321
|
+
const openDeletePrompt = (id, value) => {
|
|
1322
|
+
setDeleteAreaId(id);
|
|
1323
|
+
setDeleteAreaValue(value);
|
|
1320
1324
|
};
|
|
1321
1325
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1322
1326
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1323
1327
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
1324
|
-
/* @__PURE__ */
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1328
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-x-4 flex-1", children: [
|
|
1329
|
+
/* @__PURE__ */ jsx(
|
|
1330
|
+
Input,
|
|
1331
|
+
{
|
|
1332
|
+
type: "search",
|
|
1333
|
+
placeholder: "ค้นหาจังหวัดหรือรหัสไปรษณีย์...",
|
|
1334
|
+
value: searchTerm,
|
|
1335
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
1336
|
+
className: "max-w-md"
|
|
1337
|
+
}
|
|
1338
|
+
),
|
|
1339
|
+
/* @__PURE__ */ jsxs(Select, { value: kindFilter, onValueChange: setKindFilter, children: [
|
|
1340
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "ประเภท" }) }),
|
|
1341
|
+
/* @__PURE__ */ jsx(Select.Content, { children: AREA_KINDS.map((kind) => /* @__PURE__ */ jsx(Select.Item, { value: kind.value, children: kind.label }, kind.value)) })
|
|
1342
|
+
] })
|
|
1343
|
+
] }),
|
|
1334
1344
|
/* @__PURE__ */ jsxs(Button, { onClick: handleCreateNew, children: [
|
|
1335
1345
|
/* @__PURE__ */ jsx(Plus, {}),
|
|
1336
|
-
"
|
|
1346
|
+
"สร้างพื้นที่ใหม่"
|
|
1337
1347
|
] })
|
|
1338
1348
|
] }),
|
|
1339
1349
|
/* @__PURE__ */ jsxs(Table, { children: [
|
|
1340
1350
|
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1341
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "
|
|
1342
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "
|
|
1343
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "น้ำหนักสูงสุด (kg)" }),
|
|
1344
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
1351
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ประเภท" }),
|
|
1352
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ค่า" }),
|
|
1345
1353
|
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
1346
1354
|
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
1347
1355
|
] }) }),
|
|
1348
|
-
/* @__PURE__ */ jsx(Table.Body, { children: isLoading ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) :
|
|
1349
|
-
/* @__PURE__ */ jsx(Table.Cell, { children:
|
|
1350
|
-
/* @__PURE__ */
|
|
1351
|
-
box.width_cm,
|
|
1352
|
-
" × ",
|
|
1353
|
-
box.length_cm,
|
|
1354
|
-
" × ",
|
|
1355
|
-
box.height_cm
|
|
1356
|
-
] }),
|
|
1357
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: box.max_weight_kg }),
|
|
1358
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: box.price_thb.toFixed(2) }),
|
|
1356
|
+
/* @__PURE__ */ jsx(Table.Body, { children: isLoading ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredAreas.length === 0 ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลพื้นที่บริการ" }) }) : filteredAreas.map((area) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1357
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: area.kind === "PROVINCE" ? "blue" : "purple", children: area.kind === "PROVINCE" ? "จังหวัด" : "รหัสไปรษณีย์" }) }),
|
|
1358
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: area.value }),
|
|
1359
1359
|
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
|
|
1360
1360
|
Badge,
|
|
1361
1361
|
{
|
|
1362
|
-
color:
|
|
1362
|
+
color: area.active ? "green" : "grey",
|
|
1363
1363
|
className: "cursor-pointer",
|
|
1364
|
-
onClick: () => handleToggleActive(
|
|
1365
|
-
children:
|
|
1364
|
+
onClick: () => handleToggleActive(area),
|
|
1365
|
+
children: area.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
1366
1366
|
}
|
|
1367
1367
|
) }),
|
|
1368
1368
|
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
@@ -1371,7 +1371,7 @@ const ParcelBoxesTable = () => {
|
|
|
1371
1371
|
{
|
|
1372
1372
|
variant: "transparent",
|
|
1373
1373
|
size: "small",
|
|
1374
|
-
onClick: () => handleEdit(
|
|
1374
|
+
onClick: () => handleEdit(area),
|
|
1375
1375
|
children: /* @__PURE__ */ jsx(Edit, {})
|
|
1376
1376
|
}
|
|
1377
1377
|
),
|
|
@@ -1380,30 +1380,30 @@ const ParcelBoxesTable = () => {
|
|
|
1380
1380
|
{
|
|
1381
1381
|
variant: "transparent",
|
|
1382
1382
|
size: "small",
|
|
1383
|
-
onClick: () => openDeletePrompt(
|
|
1383
|
+
onClick: () => openDeletePrompt(area.id, area.value),
|
|
1384
1384
|
children: /* @__PURE__ */ jsx(Trash, {})
|
|
1385
1385
|
}
|
|
1386
1386
|
)
|
|
1387
1387
|
] }) })
|
|
1388
|
-
] },
|
|
1388
|
+
] }, area.id)) })
|
|
1389
1389
|
] })
|
|
1390
1390
|
] }),
|
|
1391
|
-
isModalOpen && /* @__PURE__ */ jsx(
|
|
1391
|
+
isModalOpen && /* @__PURE__ */ jsx(ServiceAreaModal, { area: editingArea, onClose: handleModalClose }),
|
|
1392
1392
|
/* @__PURE__ */ jsx(
|
|
1393
1393
|
Prompt,
|
|
1394
1394
|
{
|
|
1395
1395
|
variant: "confirmation",
|
|
1396
|
-
open: !!
|
|
1396
|
+
open: !!deleteAreaId,
|
|
1397
1397
|
onOpenChange: () => {
|
|
1398
|
-
|
|
1399
|
-
|
|
1398
|
+
setDeleteAreaId(null);
|
|
1399
|
+
setDeleteAreaValue("");
|
|
1400
1400
|
},
|
|
1401
1401
|
children: /* @__PURE__ */ jsxs(Prompt.Content, { children: [
|
|
1402
1402
|
/* @__PURE__ */ jsxs(Prompt.Header, { children: [
|
|
1403
|
-
/* @__PURE__ */ jsx(Prompt.Title, { children: "
|
|
1403
|
+
/* @__PURE__ */ jsx(Prompt.Title, { children: "ลบพื้นที่บริการ" }),
|
|
1404
1404
|
/* @__PURE__ */ jsxs(Prompt.Description, { children: [
|
|
1405
|
-
'
|
|
1406
|
-
|
|
1405
|
+
'คุณต้องการลบพื้นที่ "',
|
|
1406
|
+
deleteAreaValue,
|
|
1407
1407
|
'" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
|
|
1408
1408
|
] })
|
|
1409
1409
|
] }),
|
|
@@ -1416,15 +1416,15 @@ const ParcelBoxesTable = () => {
|
|
|
1416
1416
|
)
|
|
1417
1417
|
] });
|
|
1418
1418
|
};
|
|
1419
|
-
const
|
|
1419
|
+
const AreasPage = () => {
|
|
1420
1420
|
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1421
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "
|
|
1422
|
-
/* @__PURE__ */ jsx(
|
|
1421
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "พื้นที่บริการ" }) }),
|
|
1422
|
+
/* @__PURE__ */ jsx(ServiceAreasTable, {})
|
|
1423
1423
|
] }) });
|
|
1424
1424
|
};
|
|
1425
1425
|
const config$2 = defineRouteConfig({
|
|
1426
|
-
icon:
|
|
1427
|
-
label: "
|
|
1426
|
+
icon: MapPin,
|
|
1427
|
+
label: "พื้นที่บริการ"
|
|
1428
1428
|
});
|
|
1429
1429
|
const CATEGORIES$1 = [
|
|
1430
1430
|
{ value: "PACKAGING", label: "วัสดุหีบห่อ" },
|
|
@@ -2712,10 +2712,6 @@ const widgetModule = { widgets: [
|
|
|
2712
2712
|
] };
|
|
2713
2713
|
const routeModule = {
|
|
2714
2714
|
routes: [
|
|
2715
|
-
{
|
|
2716
|
-
Component: AreasPage,
|
|
2717
|
-
path: "/areas"
|
|
2718
|
-
},
|
|
2719
2715
|
{
|
|
2720
2716
|
Component: BasePricingPage,
|
|
2721
2717
|
path: "/base-pricing"
|
|
@@ -2724,6 +2720,10 @@ const routeModule = {
|
|
|
2724
2720
|
Component: BoxesPage,
|
|
2725
2721
|
path: "/boxes"
|
|
2726
2722
|
},
|
|
2723
|
+
{
|
|
2724
|
+
Component: AreasPage,
|
|
2725
|
+
path: "/areas"
|
|
2726
|
+
},
|
|
2727
2727
|
{
|
|
2728
2728
|
Component: MaterialCostsPage,
|
|
2729
2729
|
path: "/material-costs"
|
|
@@ -2737,24 +2737,24 @@ const routeModule = {
|
|
|
2737
2737
|
const menuItemModule = {
|
|
2738
2738
|
menuItems: [
|
|
2739
2739
|
{
|
|
2740
|
-
label: config$
|
|
2741
|
-
icon: config$
|
|
2740
|
+
label: config$2.label,
|
|
2741
|
+
icon: config$2.icon,
|
|
2742
2742
|
path: "/areas",
|
|
2743
2743
|
nested: void 0,
|
|
2744
2744
|
rank: void 0,
|
|
2745
2745
|
translationNs: void 0
|
|
2746
2746
|
},
|
|
2747
2747
|
{
|
|
2748
|
-
label: config$
|
|
2749
|
-
icon: config$
|
|
2748
|
+
label: config$4.label,
|
|
2749
|
+
icon: config$4.icon,
|
|
2750
2750
|
path: "/base-pricing",
|
|
2751
2751
|
nested: void 0,
|
|
2752
2752
|
rank: void 0,
|
|
2753
2753
|
translationNs: void 0
|
|
2754
2754
|
},
|
|
2755
2755
|
{
|
|
2756
|
-
label: config$
|
|
2757
|
-
icon: config$
|
|
2756
|
+
label: config$3.label,
|
|
2757
|
+
icon: config$3.icon,
|
|
2758
2758
|
path: "/boxes",
|
|
2759
2759
|
nested: void 0,
|
|
2760
2760
|
rank: void 0,
|