@lodashventure/medusa-parcel-shipping 0.4.30 → 0.4.31
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 +1497 -1497
- package/.medusa/server/src/admin/index.mjs +1499 -1499
- package/package.json +1 -1
|
@@ -236,49 +236,116 @@ const OrderShippingQuoteWidget = ({ data }) => {
|
|
|
236
236
|
adminSdk.defineWidgetConfig({
|
|
237
237
|
zone: "order.details.after"
|
|
238
238
|
});
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
239
|
+
const TH_PROVINCES = [
|
|
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
|
+
];
|
|
318
|
+
const ServiceAreaModal = ({ area, onClose }) => {
|
|
243
319
|
const [formData, setFormData] = react.useState({
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
price: "",
|
|
247
|
-
priority: "0",
|
|
320
|
+
kind: "PROVINCE",
|
|
321
|
+
value: "",
|
|
248
322
|
active: true
|
|
249
323
|
});
|
|
250
324
|
const [isSaving, setIsSaving] = react.useState(false);
|
|
251
325
|
react.useEffect(() => {
|
|
252
|
-
|
|
253
|
-
if (rate) {
|
|
326
|
+
if (area) {
|
|
254
327
|
setFormData({
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
priority: (rate.priority ?? 0).toString(),
|
|
259
|
-
active: rate.active
|
|
328
|
+
kind: area.kind,
|
|
329
|
+
value: area.value,
|
|
330
|
+
active: area.active
|
|
260
331
|
});
|
|
261
332
|
}
|
|
262
|
-
}, [
|
|
333
|
+
}, [area]);
|
|
263
334
|
const handleSubmit = async (e) => {
|
|
264
335
|
e.preventDefault();
|
|
265
336
|
const errors = [];
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const price = parseFloat(formData.price);
|
|
270
|
-
const priority = parseInt(formData.priority || "0", 10);
|
|
271
|
-
if (isNaN(minDistance) || minDistance < 0) {
|
|
272
|
-
errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
|
|
273
|
-
}
|
|
274
|
-
if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
|
|
275
|
-
errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
|
|
276
|
-
}
|
|
277
|
-
if (isNaN(price) || price < 0) {
|
|
278
|
-
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
337
|
+
const trimmedValue = formData.value.trim();
|
|
338
|
+
if (!trimmedValue) {
|
|
339
|
+
errors.push("กรุณากรอกค่า");
|
|
279
340
|
}
|
|
280
|
-
if (
|
|
281
|
-
|
|
341
|
+
if (formData.kind === "PROVINCE") {
|
|
342
|
+
if (!TH_PROVINCES.includes(trimmedValue)) {
|
|
343
|
+
errors.push("จังหวัดไม่ถูกต้อง กรุณาตรวจสอบการสะกดชื่อจังหวัด");
|
|
344
|
+
}
|
|
345
|
+
} else if (formData.kind === "POSTCODE_PREFIX") {
|
|
346
|
+
if (!/^\d{1,5}$/.test(trimmedValue)) {
|
|
347
|
+
errors.push("รหัสไปรษณีย์ต้องเป็นตัวเลข 1-5 หลัก");
|
|
348
|
+
}
|
|
282
349
|
}
|
|
283
350
|
if (errors.length > 0) {
|
|
284
351
|
ui.toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
@@ -289,32 +356,27 @@ const MessengerBaseRateModal = ({
|
|
|
289
356
|
setIsSaving(true);
|
|
290
357
|
try {
|
|
291
358
|
const payload = {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
service_code: "MESSENGER_3H",
|
|
295
|
-
min_distance_km: minDistance,
|
|
296
|
-
max_distance_km: maxDistance === void 0 || isNaN(maxDistance) ? null : maxDistance,
|
|
297
|
-
price,
|
|
298
|
-
currency: "THB",
|
|
299
|
-
priority,
|
|
359
|
+
kind: formData.kind,
|
|
360
|
+
value: trimmedValue,
|
|
300
361
|
active: formData.active
|
|
301
362
|
};
|
|
302
|
-
const url =
|
|
303
|
-
const method =
|
|
363
|
+
const url = area ? `/admin/service-areas/${area.id}` : "/admin/service-areas";
|
|
364
|
+
const method = area ? "PUT" : "POST";
|
|
304
365
|
const response = await fetch(url, {
|
|
305
366
|
method,
|
|
306
367
|
headers: { "Content-Type": "application/json" },
|
|
307
368
|
credentials: "include",
|
|
308
369
|
body: JSON.stringify(payload)
|
|
309
370
|
});
|
|
310
|
-
if (
|
|
311
|
-
|
|
312
|
-
|
|
371
|
+
if (response.ok) {
|
|
372
|
+
ui.toast.success("สำเร็จ", {
|
|
373
|
+
description: area ? "แก้ไขพื้นที่บริการแล้ว" : "สร้างพื้นที่บริการใหม่แล้ว"
|
|
374
|
+
});
|
|
375
|
+
onClose();
|
|
376
|
+
} else {
|
|
377
|
+
const error = await response.json();
|
|
378
|
+
throw new Error(error.message || "Failed to save");
|
|
313
379
|
}
|
|
314
|
-
ui.toast.success("สำเร็จ", {
|
|
315
|
-
description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
|
|
316
|
-
});
|
|
317
|
-
onClose();
|
|
318
380
|
} catch (error) {
|
|
319
381
|
ui.toast.error("ข้อผิดพลาด", {
|
|
320
382
|
description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
|
|
@@ -324,69 +386,53 @@ const MessengerBaseRateModal = ({
|
|
|
324
386
|
}
|
|
325
387
|
};
|
|
326
388
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
327
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children:
|
|
389
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: area ? "แก้ไขพื้นที่บริการ" : "สร้างพื้นที่บริการใหม่" }) }),
|
|
328
390
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
329
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
330
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
331
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "min_distance_km", children: "ระยะทางเริ่มต้น (กม.) *" }),
|
|
332
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
333
|
-
ui.Input,
|
|
334
|
-
{
|
|
335
|
-
id: "min_distance_km",
|
|
336
|
-
type: "number",
|
|
337
|
-
min: "0",
|
|
338
|
-
step: "0.1",
|
|
339
|
-
value: formData.min_distance_km,
|
|
340
|
-
onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
|
|
341
|
-
required: true
|
|
342
|
-
}
|
|
343
|
-
)
|
|
344
|
-
] }),
|
|
345
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
346
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "max_distance_km", children: "ระยะทางสูงสุด (กม.)" }),
|
|
347
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
348
|
-
ui.Input,
|
|
349
|
-
{
|
|
350
|
-
id: "max_distance_km",
|
|
351
|
-
type: "number",
|
|
352
|
-
min: "0",
|
|
353
|
-
step: "0.1",
|
|
354
|
-
placeholder: "ปล่อยว่างหากไม่มีที่สิ้นสุด",
|
|
355
|
-
value: formData.max_distance_km,
|
|
356
|
-
onChange: (e) => setFormData({ ...formData, max_distance_km: e.target.value })
|
|
357
|
-
}
|
|
358
|
-
),
|
|
359
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
|
|
360
|
-
] })
|
|
361
|
-
] }),
|
|
362
391
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
363
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
364
|
-
/* @__PURE__ */ jsxRuntime.
|
|
365
|
-
ui.
|
|
392
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "kind", children: "ประเภท *" }),
|
|
393
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
394
|
+
ui.Select,
|
|
366
395
|
{
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
396
|
+
value: formData.kind,
|
|
397
|
+
onValueChange: (value) => setFormData({ ...formData, kind: value, value: "" }),
|
|
398
|
+
required: true,
|
|
399
|
+
children: [
|
|
400
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกประเภท" }) }),
|
|
401
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
|
|
402
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "PROVINCE", children: "จังหวัด" }),
|
|
403
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "POSTCODE_PREFIX", children: "รหัสไปรษณีย์" })
|
|
404
|
+
] })
|
|
405
|
+
]
|
|
374
406
|
}
|
|
375
407
|
)
|
|
376
408
|
] }),
|
|
377
409
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
378
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
379
|
-
/* @__PURE__ */ jsxRuntime.
|
|
410
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "value", children: formData.kind === "PROVINCE" ? "ชื่อจังหวัด *" : "รหัสไปรษณีย์ (1-5 หลัก) *" }),
|
|
411
|
+
formData.kind === "PROVINCE" ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
412
|
+
ui.Select,
|
|
413
|
+
{
|
|
414
|
+
value: formData.value,
|
|
415
|
+
onValueChange: (value) => setFormData({ ...formData, value }),
|
|
416
|
+
required: true,
|
|
417
|
+
children: [
|
|
418
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกจังหวัด" }) }),
|
|
419
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: TH_PROVINCES.map((province) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: province, children: province }, province)) })
|
|
420
|
+
]
|
|
421
|
+
}
|
|
422
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
380
423
|
ui.Input,
|
|
381
424
|
{
|
|
382
|
-
id: "
|
|
383
|
-
type: "
|
|
384
|
-
value: formData.
|
|
385
|
-
onChange: (e) => setFormData({ ...formData,
|
|
386
|
-
placeholder: "
|
|
425
|
+
id: "value",
|
|
426
|
+
type: "text",
|
|
427
|
+
value: formData.value,
|
|
428
|
+
onChange: (e) => setFormData({ ...formData, value: e.target.value }),
|
|
429
|
+
placeholder: "เช่น 10, 102, 10200",
|
|
430
|
+
maxLength: 5,
|
|
431
|
+
pattern: "\\d{1,5}",
|
|
432
|
+
required: true
|
|
387
433
|
}
|
|
388
434
|
),
|
|
389
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children:
|
|
435
|
+
formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
|
|
390
436
|
] }),
|
|
391
437
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
392
438
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -415,148 +461,200 @@ const MessengerBaseRateModal = ({
|
|
|
415
461
|
] }) })
|
|
416
462
|
] }) });
|
|
417
463
|
};
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
react.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
464
|
+
const AREA_KINDS = [
|
|
465
|
+
{ value: "ALL", label: "ทั้งหมด" },
|
|
466
|
+
{ value: "PROVINCE", label: "จังหวัด" },
|
|
467
|
+
{ value: "POSTCODE_PREFIX", label: "รหัสไปรษณีย์" }
|
|
468
|
+
];
|
|
469
|
+
const ServiceAreasTable = () => {
|
|
470
|
+
const [areas, setAreas] = react.useState([]);
|
|
471
|
+
const [filteredAreas, setFilteredAreas] = react.useState([]);
|
|
472
|
+
const [searchTerm, setSearchTerm] = react.useState("");
|
|
473
|
+
const [kindFilter, setKindFilter] = react.useState("ALL");
|
|
474
|
+
const [isModalOpen, setIsModalOpen] = react.useState(false);
|
|
475
|
+
const [editingArea, setEditingArea] = react.useState(null);
|
|
476
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
477
|
+
const [deleteAreaId, setDeleteAreaId] = react.useState(null);
|
|
478
|
+
const [deleteAreaValue, setDeleteAreaValue] = react.useState("");
|
|
479
|
+
react.useEffect(() => {
|
|
480
|
+
fetchAreas();
|
|
481
|
+
}, []);
|
|
482
|
+
react.useEffect(() => {
|
|
483
|
+
let filtered = areas;
|
|
484
|
+
if (kindFilter !== "ALL") {
|
|
485
|
+
filtered = filtered.filter((area) => area.kind === kindFilter);
|
|
486
|
+
}
|
|
487
|
+
if (searchTerm) {
|
|
488
|
+
filtered = filtered.filter(
|
|
489
|
+
(area) => area.value.toLowerCase().includes(searchTerm.toLowerCase())
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
setFilteredAreas(filtered);
|
|
493
|
+
}, [searchTerm, kindFilter, areas]);
|
|
494
|
+
const fetchAreas = async () => {
|
|
438
495
|
setIsLoading(true);
|
|
439
496
|
try {
|
|
440
|
-
const response = await fetch(
|
|
441
|
-
"
|
|
442
|
-
|
|
443
|
-
credentials: "include"
|
|
444
|
-
}
|
|
445
|
-
);
|
|
446
|
-
if (!response.ok) {
|
|
447
|
-
throw new Error("failed_to_load_base_rates");
|
|
448
|
-
}
|
|
497
|
+
const response = await fetch("/admin/service-areas", {
|
|
498
|
+
credentials: "include"
|
|
499
|
+
});
|
|
449
500
|
const data = await response.json();
|
|
450
|
-
|
|
501
|
+
console.log("Fetched service areas:", data);
|
|
502
|
+
setAreas(data.service_areas || []);
|
|
451
503
|
} catch (error) {
|
|
452
504
|
ui.toast.error("ข้อผิดพลาด", {
|
|
453
|
-
description: "
|
|
505
|
+
description: "ไม่สามารถโหลดข้อมูลพื้นที่บริการได้"
|
|
454
506
|
});
|
|
455
507
|
} finally {
|
|
456
508
|
setIsLoading(false);
|
|
457
509
|
}
|
|
458
510
|
};
|
|
459
|
-
const
|
|
460
|
-
if (!deleteId) return;
|
|
511
|
+
const handleToggleActive = async (area) => {
|
|
461
512
|
try {
|
|
462
|
-
const response = await fetch(
|
|
463
|
-
|
|
464
|
-
{
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
|
|
513
|
+
const response = await fetch(`/admin/service-areas/${area.id}`, {
|
|
514
|
+
method: "PUT",
|
|
515
|
+
headers: { "Content-Type": "application/json" },
|
|
516
|
+
credentials: "include",
|
|
517
|
+
body: JSON.stringify({ ...area, active: !area.active })
|
|
518
|
+
});
|
|
519
|
+
if (response.ok) {
|
|
520
|
+
ui.toast.success("สำเร็จ", {
|
|
521
|
+
description: `${area.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}พื้นที่ ${area.value} แล้ว`
|
|
522
|
+
});
|
|
523
|
+
fetchAreas();
|
|
524
|
+
} else {
|
|
525
|
+
throw new Error("Failed to update");
|
|
471
526
|
}
|
|
472
|
-
|
|
473
|
-
|
|
527
|
+
} catch (error) {
|
|
528
|
+
ui.toast.error("ข้อผิดพลาด", {
|
|
529
|
+
description: "ไม่สามารถอัพเดทสถานะได้"
|
|
474
530
|
});
|
|
475
|
-
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
const handleDelete = async () => {
|
|
534
|
+
if (!deleteAreaId) return;
|
|
535
|
+
try {
|
|
536
|
+
const response = await fetch(`/admin/service-areas/${deleteAreaId}`, {
|
|
537
|
+
method: "DELETE",
|
|
538
|
+
credentials: "include"
|
|
539
|
+
});
|
|
540
|
+
if (response.ok) {
|
|
541
|
+
ui.toast.success("สำเร็จ", {
|
|
542
|
+
description: `ลบพื้นที่ ${deleteAreaValue} แล้ว`
|
|
543
|
+
});
|
|
544
|
+
fetchAreas();
|
|
545
|
+
} else {
|
|
546
|
+
throw new Error("Failed to delete");
|
|
547
|
+
}
|
|
476
548
|
} catch (error) {
|
|
477
549
|
ui.toast.error("ข้อผิดพลาด", {
|
|
478
|
-
description: "
|
|
550
|
+
description: "ไม่สามารถลบพื้นที่ได้"
|
|
479
551
|
});
|
|
480
552
|
} finally {
|
|
481
|
-
|
|
553
|
+
setDeleteAreaId(null);
|
|
554
|
+
setDeleteAreaValue("");
|
|
482
555
|
}
|
|
483
556
|
};
|
|
557
|
+
const handleEdit = (area) => {
|
|
558
|
+
setEditingArea(area);
|
|
559
|
+
setIsModalOpen(true);
|
|
560
|
+
};
|
|
561
|
+
const handleCreateNew = () => {
|
|
562
|
+
setEditingArea(null);
|
|
563
|
+
setIsModalOpen(true);
|
|
564
|
+
};
|
|
484
565
|
const handleModalClose = () => {
|
|
485
566
|
setIsModalOpen(false);
|
|
486
|
-
|
|
487
|
-
|
|
567
|
+
setEditingArea(null);
|
|
568
|
+
fetchAreas();
|
|
488
569
|
};
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
if (max === void 0 || max === null) {
|
|
493
|
-
return `${min}+ กม.`;
|
|
494
|
-
}
|
|
495
|
-
return `${min}-${max} กม.`;
|
|
570
|
+
const openDeletePrompt = (id, value) => {
|
|
571
|
+
setDeleteAreaId(id);
|
|
572
|
+
setDeleteAreaValue(value);
|
|
496
573
|
};
|
|
497
574
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
498
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex
|
|
499
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
500
|
-
/* @__PURE__ */ jsxRuntime.
|
|
501
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-fg-subtle text-sm", children: "กำหนดราคาเริ่มต้นตามระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
|
|
502
|
-
] }),
|
|
503
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: () => setIsModalOpen(true), children: [
|
|
504
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
|
|
505
|
-
"เพิ่มช่วงราคา"
|
|
506
|
-
] })
|
|
507
|
-
] }),
|
|
508
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { className: "mt-4", children: [
|
|
509
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
510
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ช่วงระยะทาง" }),
|
|
511
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
512
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ลำดับ" }),
|
|
513
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
|
|
514
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
515
|
-
] }) }),
|
|
516
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : orderedRates.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ยังไม่ได้ตั้งค่าฐานราคาตามระยะทาง" }) }) : orderedRates.map((rate) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
517
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatDistanceRange(rate) }),
|
|
518
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
|
|
519
|
-
"฿",
|
|
520
|
-
rate.price.toFixed(2)
|
|
521
|
-
] }),
|
|
522
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: rate.priority ?? 0 }),
|
|
523
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
524
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
575
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
576
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
577
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4 flex-1", children: [
|
|
525
578
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
526
|
-
ui.
|
|
579
|
+
ui.Input,
|
|
527
580
|
{
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
},
|
|
534
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
|
|
581
|
+
type: "search",
|
|
582
|
+
placeholder: "ค้นหาจังหวัดหรือรหัสไปรษณีย์...",
|
|
583
|
+
value: searchTerm,
|
|
584
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
585
|
+
className: "max-w-md"
|
|
535
586
|
}
|
|
536
587
|
),
|
|
537
|
-
/* @__PURE__ */ jsxRuntime.
|
|
538
|
-
ui.
|
|
588
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: kindFilter, onValueChange: setKindFilter, children: [
|
|
589
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ประเภท" }) }),
|
|
590
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: AREA_KINDS.map((kind) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: kind.value, children: kind.label }, kind.value)) })
|
|
591
|
+
] })
|
|
592
|
+
] }),
|
|
593
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
|
|
594
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
|
|
595
|
+
"สร้างพื้นที่ใหม่"
|
|
596
|
+
] })
|
|
597
|
+
] }),
|
|
598
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
599
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
600
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ประเภท" }),
|
|
601
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ค่า" }),
|
|
602
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
|
|
603
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
604
|
+
] }) }),
|
|
605
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredAreas.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลพื้นที่บริการ" }) }) : filteredAreas.map((area) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
606
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: area.kind === "PROVINCE" ? "blue" : "purple", children: area.kind === "PROVINCE" ? "จังหวัด" : "รหัสไปรษณีย์" }) }),
|
|
607
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: area.value }),
|
|
608
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
609
|
+
ui.Badge,
|
|
539
610
|
{
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
onClick: () =>
|
|
543
|
-
children:
|
|
611
|
+
color: area.active ? "green" : "grey",
|
|
612
|
+
className: "cursor-pointer",
|
|
613
|
+
onClick: () => handleToggleActive(area),
|
|
614
|
+
children: area.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
544
615
|
}
|
|
545
|
-
)
|
|
546
|
-
|
|
547
|
-
|
|
616
|
+
) }),
|
|
617
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
618
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
619
|
+
ui.Button,
|
|
620
|
+
{
|
|
621
|
+
variant: "transparent",
|
|
622
|
+
size: "small",
|
|
623
|
+
onClick: () => handleEdit(area),
|
|
624
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
|
|
625
|
+
}
|
|
626
|
+
),
|
|
627
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
628
|
+
ui.Button,
|
|
629
|
+
{
|
|
630
|
+
variant: "transparent",
|
|
631
|
+
size: "small",
|
|
632
|
+
onClick: () => openDeletePrompt(area.id, area.value),
|
|
633
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
|
|
634
|
+
}
|
|
635
|
+
)
|
|
636
|
+
] }) })
|
|
637
|
+
] }, area.id)) })
|
|
638
|
+
] })
|
|
548
639
|
] }),
|
|
549
|
-
isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(
|
|
640
|
+
isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ServiceAreaModal, { area: editingArea, onClose: handleModalClose }),
|
|
550
641
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
551
642
|
ui.Prompt,
|
|
552
643
|
{
|
|
553
644
|
variant: "confirmation",
|
|
554
|
-
open: !!
|
|
555
|
-
onOpenChange: () =>
|
|
645
|
+
open: !!deleteAreaId,
|
|
646
|
+
onOpenChange: () => {
|
|
647
|
+
setDeleteAreaId(null);
|
|
648
|
+
setDeleteAreaValue("");
|
|
649
|
+
},
|
|
556
650
|
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
|
|
557
651
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
|
|
558
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "
|
|
559
|
-
/* @__PURE__ */ jsxRuntime.
|
|
652
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบพื้นที่บริการ" }),
|
|
653
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
|
|
654
|
+
'คุณต้องการลบพื้นที่ "',
|
|
655
|
+
deleteAreaValue,
|
|
656
|
+
'" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
|
|
657
|
+
] })
|
|
560
658
|
] }),
|
|
561
659
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
|
|
562
660
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "ยกเลิก" }),
|
|
@@ -567,69 +665,60 @@ const MessengerBasePricingTable = () => {
|
|
|
567
665
|
)
|
|
568
666
|
] });
|
|
569
667
|
};
|
|
570
|
-
const
|
|
571
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-
|
|
572
|
-
/* @__PURE__ */ jsxRuntime.
|
|
573
|
-
|
|
574
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "ตั้งราคาฐานสำหรับบริการ Messenger 3 ชั่วโมงตามช่วงระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
|
|
575
|
-
] }),
|
|
576
|
-
/* @__PURE__ */ jsxRuntime.jsx(MessengerBasePricingTable, {})
|
|
668
|
+
const AreasPage = () => {
|
|
669
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
670
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "พื้นที่บริการ" }) }),
|
|
671
|
+
/* @__PURE__ */ jsxRuntime.jsx(ServiceAreasTable, {})
|
|
577
672
|
] }) });
|
|
578
673
|
};
|
|
579
674
|
const config$4 = adminSdk.defineRouteConfig({
|
|
580
|
-
icon: icons.
|
|
581
|
-
label: "
|
|
675
|
+
icon: icons.MapPin,
|
|
676
|
+
label: "พื้นที่บริการ"
|
|
582
677
|
});
|
|
583
|
-
const
|
|
678
|
+
const MessengerBaseRateModal = ({
|
|
679
|
+
rate,
|
|
680
|
+
onClose
|
|
681
|
+
}) => {
|
|
584
682
|
const [formData, setFormData] = react.useState({
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
max_weight_kg: "",
|
|
590
|
-
price_thb: "",
|
|
683
|
+
min_distance_km: "0",
|
|
684
|
+
max_distance_km: "",
|
|
685
|
+
price: "",
|
|
686
|
+
priority: "0",
|
|
591
687
|
active: true
|
|
592
688
|
});
|
|
593
689
|
const [isSaving, setIsSaving] = react.useState(false);
|
|
594
690
|
react.useEffect(() => {
|
|
595
|
-
|
|
691
|
+
var _a, _b;
|
|
692
|
+
if (rate) {
|
|
596
693
|
setFormData({
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
price_thb: box.price_thb.toString(),
|
|
603
|
-
active: box.active
|
|
694
|
+
min_distance_km: ((_a = rate.min_distance_km) == null ? void 0 : _a.toString()) ?? "0",
|
|
695
|
+
max_distance_km: ((_b = rate.max_distance_km) == null ? void 0 : _b.toString()) || "",
|
|
696
|
+
price: rate.price.toString(),
|
|
697
|
+
priority: (rate.priority ?? 0).toString(),
|
|
698
|
+
active: rate.active
|
|
604
699
|
});
|
|
605
700
|
}
|
|
606
|
-
}, [
|
|
701
|
+
}, [rate]);
|
|
607
702
|
const handleSubmit = async (e) => {
|
|
608
703
|
e.preventDefault();
|
|
609
704
|
const errors = [];
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
const
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
const price = parseFloat(formData.price_thb);
|
|
618
|
-
if (isNaN(width) || width <= 0) {
|
|
619
|
-
errors.push("ความกว้างต้องเป็นตัวเลขมากกว่า 0");
|
|
620
|
-
}
|
|
621
|
-
if (isNaN(length) || length <= 0) {
|
|
622
|
-
errors.push("ความยาวต้องเป็นตัวเลขมากกว่า 0");
|
|
623
|
-
}
|
|
624
|
-
if (isNaN(height) || height <= 0) {
|
|
625
|
-
errors.push("ความสูงต้องเป็นตัวเลขมากกว่า 0");
|
|
705
|
+
const minDistance = parseFloat(formData.min_distance_km || "0");
|
|
706
|
+
const hasMaxDistance = formData.max_distance_km !== "";
|
|
707
|
+
const maxDistance = hasMaxDistance ? parseFloat(formData.max_distance_km) : void 0;
|
|
708
|
+
const price = parseFloat(formData.price);
|
|
709
|
+
const priority = parseInt(formData.priority || "0", 10);
|
|
710
|
+
if (isNaN(minDistance) || minDistance < 0) {
|
|
711
|
+
errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
|
|
626
712
|
}
|
|
627
|
-
if (isNaN(
|
|
628
|
-
errors.push("
|
|
713
|
+
if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
|
|
714
|
+
errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
|
|
629
715
|
}
|
|
630
716
|
if (isNaN(price) || price < 0) {
|
|
631
717
|
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
632
718
|
}
|
|
719
|
+
if (isNaN(priority)) {
|
|
720
|
+
errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
|
|
721
|
+
}
|
|
633
722
|
if (errors.length > 0) {
|
|
634
723
|
ui.toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
635
724
|
description: errors.join(", ")
|
|
@@ -639,31 +728,32 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
639
728
|
setIsSaving(true);
|
|
640
729
|
try {
|
|
641
730
|
const payload = {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
731
|
+
carrier_type: "COMPANY_FLEET",
|
|
732
|
+
carrier: "COMPANY_FLEET",
|
|
733
|
+
service_code: "MESSENGER_3H",
|
|
734
|
+
min_distance_km: minDistance,
|
|
735
|
+
max_distance_km: maxDistance === void 0 || isNaN(maxDistance) ? null : maxDistance,
|
|
736
|
+
price,
|
|
737
|
+
currency: "THB",
|
|
738
|
+
priority,
|
|
648
739
|
active: formData.active
|
|
649
740
|
};
|
|
650
|
-
const url =
|
|
651
|
-
const method =
|
|
741
|
+
const url = rate ? `/admin/base-shipping-prices/${rate.id}` : "/admin/base-shipping-prices";
|
|
742
|
+
const method = rate ? "PUT" : "POST";
|
|
652
743
|
const response = await fetch(url, {
|
|
653
744
|
method,
|
|
654
745
|
headers: { "Content-Type": "application/json" },
|
|
655
746
|
credentials: "include",
|
|
656
747
|
body: JSON.stringify(payload)
|
|
657
748
|
});
|
|
658
|
-
if (response.ok) {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
});
|
|
662
|
-
onClose();
|
|
663
|
-
} else {
|
|
664
|
-
const error = await response.json();
|
|
665
|
-
throw new Error(error.message || "Failed to save");
|
|
749
|
+
if (!response.ok) {
|
|
750
|
+
const error = await response.json().catch(() => ({}));
|
|
751
|
+
throw new Error(error.message || "ไม่สามารถบันทึกข้อมูลได้");
|
|
666
752
|
}
|
|
753
|
+
ui.toast.success("สำเร็จ", {
|
|
754
|
+
description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
|
|
755
|
+
});
|
|
756
|
+
onClose();
|
|
667
757
|
} catch (error) {
|
|
668
758
|
ui.toast.error("ข้อผิดพลาด", {
|
|
669
759
|
description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
|
|
@@ -673,82 +763,40 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
673
763
|
}
|
|
674
764
|
};
|
|
675
765
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
676
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children:
|
|
766
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: rate ? "แก้ไขฐานราคาตามระยะทาง" : "สร้างฐานราคาตามระยะทาง" }) }),
|
|
677
767
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
678
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
679
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "name", children: "ชื่อกล่อง *" }),
|
|
680
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
681
|
-
ui.Input,
|
|
682
|
-
{
|
|
683
|
-
id: "name",
|
|
684
|
-
value: formData.name,
|
|
685
|
-
onChange: (e) => setFormData({ ...formData, name: e.target.value }),
|
|
686
|
-
placeholder: "เช่น กล่อง S, กล่อง M",
|
|
687
|
-
required: true
|
|
688
|
-
}
|
|
689
|
-
)
|
|
690
|
-
] }),
|
|
691
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-x-4", children: [
|
|
692
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
693
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "width", children: "ความกว้าง (cm) *" }),
|
|
694
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
695
|
-
ui.Input,
|
|
696
|
-
{
|
|
697
|
-
id: "width",
|
|
698
|
-
type: "number",
|
|
699
|
-
step: "0.1",
|
|
700
|
-
value: formData.width_cm,
|
|
701
|
-
onChange: (e) => setFormData({ ...formData, width_cm: e.target.value }),
|
|
702
|
-
placeholder: "20",
|
|
703
|
-
required: true
|
|
704
|
-
}
|
|
705
|
-
)
|
|
706
|
-
] }),
|
|
768
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
707
769
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
708
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
770
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "min_distance_km", children: "ระยะทางเริ่มต้น (กม.) *" }),
|
|
709
771
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
710
772
|
ui.Input,
|
|
711
773
|
{
|
|
712
|
-
id: "
|
|
774
|
+
id: "min_distance_km",
|
|
713
775
|
type: "number",
|
|
776
|
+
min: "0",
|
|
714
777
|
step: "0.1",
|
|
715
|
-
value: formData.
|
|
716
|
-
onChange: (e) => setFormData({ ...formData,
|
|
717
|
-
placeholder: "30",
|
|
778
|
+
value: formData.min_distance_km,
|
|
779
|
+
onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
|
|
718
780
|
required: true
|
|
719
781
|
}
|
|
720
782
|
)
|
|
721
783
|
] }),
|
|
722
784
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
723
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
785
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "max_distance_km", children: "ระยะทางสูงสุด (กม.)" }),
|
|
724
786
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
725
787
|
ui.Input,
|
|
726
788
|
{
|
|
727
|
-
id: "
|
|
789
|
+
id: "max_distance_km",
|
|
728
790
|
type: "number",
|
|
791
|
+
min: "0",
|
|
729
792
|
step: "0.1",
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
required: true
|
|
793
|
+
placeholder: "ปล่อยว่างหากไม่มีที่สิ้นสุด",
|
|
794
|
+
value: formData.max_distance_km,
|
|
795
|
+
onChange: (e) => setFormData({ ...formData, max_distance_km: e.target.value })
|
|
734
796
|
}
|
|
735
|
-
)
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
739
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
|
|
740
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
741
|
-
ui.Input,
|
|
742
|
-
{
|
|
743
|
-
id: "maxWeight",
|
|
744
|
-
type: "number",
|
|
745
|
-
step: "0.1",
|
|
746
|
-
value: formData.max_weight_kg,
|
|
747
|
-
onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
|
|
748
|
-
placeholder: "0.5",
|
|
749
|
-
required: true
|
|
750
|
-
}
|
|
751
|
-
)
|
|
797
|
+
),
|
|
798
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
|
|
799
|
+
] })
|
|
752
800
|
] }),
|
|
753
801
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
754
802
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
|
|
@@ -757,14 +805,28 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
757
805
|
{
|
|
758
806
|
id: "price",
|
|
759
807
|
type: "number",
|
|
808
|
+
min: "0",
|
|
760
809
|
step: "0.01",
|
|
761
|
-
value: formData.
|
|
762
|
-
onChange: (e) => setFormData({ ...formData,
|
|
763
|
-
placeholder: "10.00",
|
|
810
|
+
value: formData.price,
|
|
811
|
+
onChange: (e) => setFormData({ ...formData, price: e.target.value }),
|
|
764
812
|
required: true
|
|
765
813
|
}
|
|
766
814
|
)
|
|
767
815
|
] }),
|
|
816
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
817
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
|
|
818
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
819
|
+
ui.Input,
|
|
820
|
+
{
|
|
821
|
+
id: "priority",
|
|
822
|
+
type: "number",
|
|
823
|
+
value: formData.priority,
|
|
824
|
+
onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
|
|
825
|
+
placeholder: "0"
|
|
826
|
+
}
|
|
827
|
+
),
|
|
828
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
|
|
829
|
+
] }),
|
|
768
830
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
769
831
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
770
832
|
ui.Switch,
|
|
@@ -777,197 +839,163 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
777
839
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
|
|
778
840
|
] }),
|
|
779
841
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
780
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
842
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
843
|
+
ui.Button,
|
|
844
|
+
{
|
|
845
|
+
type: "button",
|
|
846
|
+
variant: "secondary",
|
|
847
|
+
onClick: onClose,
|
|
848
|
+
disabled: isSaving,
|
|
849
|
+
children: "ยกเลิก"
|
|
850
|
+
}
|
|
851
|
+
),
|
|
781
852
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
|
|
782
853
|
] })
|
|
783
854
|
] }) })
|
|
784
855
|
] }) });
|
|
785
856
|
};
|
|
786
|
-
const
|
|
787
|
-
const [
|
|
788
|
-
const [filteredBoxes, setFilteredBoxes] = react.useState([]);
|
|
789
|
-
const [searchTerm, setSearchTerm] = react.useState("");
|
|
790
|
-
const [isModalOpen, setIsModalOpen] = react.useState(false);
|
|
791
|
-
const [editingBox, setEditingBox] = react.useState(null);
|
|
857
|
+
const MessengerBasePricingTable = () => {
|
|
858
|
+
const [rates, setRates] = react.useState([]);
|
|
792
859
|
const [isLoading, setIsLoading] = react.useState(false);
|
|
793
|
-
const [
|
|
794
|
-
const [
|
|
860
|
+
const [isModalOpen, setIsModalOpen] = react.useState(false);
|
|
861
|
+
const [editingRate, setEditingRate] = react.useState(null);
|
|
862
|
+
const [deleteId, setDeleteId] = react.useState(null);
|
|
795
863
|
react.useEffect(() => {
|
|
796
|
-
|
|
864
|
+
fetchRates();
|
|
797
865
|
}, []);
|
|
798
|
-
react.
|
|
799
|
-
|
|
800
|
-
(
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
866
|
+
const orderedRates = react.useMemo(() => {
|
|
867
|
+
return [...rates].sort((a, b) => {
|
|
868
|
+
if (a.min_distance_km !== b.min_distance_km) {
|
|
869
|
+
return a.min_distance_km - b.min_distance_km;
|
|
870
|
+
}
|
|
871
|
+
const aMax = a.max_distance_km ?? Number.POSITIVE_INFINITY;
|
|
872
|
+
const bMax = b.max_distance_km ?? Number.POSITIVE_INFINITY;
|
|
873
|
+
return aMax - bMax;
|
|
874
|
+
});
|
|
875
|
+
}, [rates]);
|
|
876
|
+
const fetchRates = async () => {
|
|
805
877
|
setIsLoading(true);
|
|
806
878
|
try {
|
|
807
|
-
const response = await fetch(
|
|
808
|
-
|
|
809
|
-
|
|
879
|
+
const response = await fetch(
|
|
880
|
+
"/admin/base-shipping-prices?carrier_type=COMPANY_FLEET&service_code=MESSENGER_3H",
|
|
881
|
+
{
|
|
882
|
+
credentials: "include"
|
|
883
|
+
}
|
|
884
|
+
);
|
|
885
|
+
if (!response.ok) {
|
|
886
|
+
throw new Error("failed_to_load_base_rates");
|
|
887
|
+
}
|
|
810
888
|
const data = await response.json();
|
|
811
|
-
|
|
889
|
+
setRates(data.base_rates || []);
|
|
812
890
|
} catch (error) {
|
|
813
891
|
ui.toast.error("ข้อผิดพลาด", {
|
|
814
|
-
description: "
|
|
892
|
+
description: "ไม่สามารถโหลดฐานราคาตามระยะทางได้"
|
|
815
893
|
});
|
|
816
894
|
} finally {
|
|
817
895
|
setIsLoading(false);
|
|
818
896
|
}
|
|
819
897
|
};
|
|
820
|
-
const handleToggleActive = async (box) => {
|
|
821
|
-
try {
|
|
822
|
-
const response = await fetch(`/admin/boxes/${box.id}`, {
|
|
823
|
-
method: "PUT",
|
|
824
|
-
headers: { "Content-Type": "application/json" },
|
|
825
|
-
credentials: "include",
|
|
826
|
-
body: JSON.stringify({ ...box, active: !box.active })
|
|
827
|
-
});
|
|
828
|
-
if (response.ok) {
|
|
829
|
-
ui.toast.success("สำเร็จ", {
|
|
830
|
-
description: `${box.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}กล่อง ${box.name} แล้ว`
|
|
831
|
-
});
|
|
832
|
-
fetchBoxes();
|
|
833
|
-
} else {
|
|
834
|
-
throw new Error("Failed to update");
|
|
835
|
-
}
|
|
836
|
-
} catch (error) {
|
|
837
|
-
ui.toast.error("ข้อผิดพลาด", {
|
|
838
|
-
description: "ไม่สามารถอัพเดทสถานะได้"
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
};
|
|
842
898
|
const handleDelete = async () => {
|
|
843
|
-
if (!
|
|
899
|
+
if (!deleteId) return;
|
|
844
900
|
try {
|
|
845
|
-
const response = await fetch(
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
} else {
|
|
855
|
-
throw new Error("Failed to delete");
|
|
901
|
+
const response = await fetch(
|
|
902
|
+
`/admin/base-shipping-prices/${deleteId}`,
|
|
903
|
+
{
|
|
904
|
+
method: "DELETE",
|
|
905
|
+
credentials: "include"
|
|
906
|
+
}
|
|
907
|
+
);
|
|
908
|
+
if (!response.ok) {
|
|
909
|
+
throw new Error("failed_to_delete");
|
|
856
910
|
}
|
|
911
|
+
ui.toast.success("สำเร็จ", {
|
|
912
|
+
description: "ลบฐานราคาสำเร็จ"
|
|
913
|
+
});
|
|
914
|
+
fetchRates();
|
|
857
915
|
} catch (error) {
|
|
858
916
|
ui.toast.error("ข้อผิดพลาด", {
|
|
859
|
-
description: "
|
|
917
|
+
description: "ไม่สามารถลบฐานราคาได้"
|
|
860
918
|
});
|
|
861
919
|
} finally {
|
|
862
|
-
|
|
863
|
-
setDeleteBoxName("");
|
|
920
|
+
setDeleteId(null);
|
|
864
921
|
}
|
|
865
922
|
};
|
|
866
|
-
const handleEdit = (box) => {
|
|
867
|
-
setEditingBox(box);
|
|
868
|
-
setIsModalOpen(true);
|
|
869
|
-
};
|
|
870
|
-
const handleCreateNew = () => {
|
|
871
|
-
setEditingBox(null);
|
|
872
|
-
setIsModalOpen(true);
|
|
873
|
-
};
|
|
874
923
|
const handleModalClose = () => {
|
|
875
924
|
setIsModalOpen(false);
|
|
876
|
-
|
|
877
|
-
|
|
925
|
+
setEditingRate(null);
|
|
926
|
+
fetchRates();
|
|
878
927
|
};
|
|
879
|
-
const
|
|
880
|
-
|
|
881
|
-
|
|
928
|
+
const formatDistanceRange = (rate) => {
|
|
929
|
+
const min = rate.min_distance_km ?? 0;
|
|
930
|
+
const max = rate.max_distance_km;
|
|
931
|
+
if (max === void 0 || max === null) {
|
|
932
|
+
return `${min}+ กม.`;
|
|
933
|
+
}
|
|
934
|
+
return `${min}-${max} กม.`;
|
|
882
935
|
};
|
|
883
936
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
884
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex
|
|
885
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
886
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
887
|
-
|
|
888
|
-
{
|
|
889
|
-
type: "search",
|
|
890
|
-
placeholder: "ค้นหาชื่อกล่อง...",
|
|
891
|
-
value: searchTerm,
|
|
892
|
-
onChange: (e) => setSearchTerm(e.target.value),
|
|
893
|
-
className: "max-w-md"
|
|
894
|
-
}
|
|
895
|
-
),
|
|
896
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
|
|
897
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
|
|
898
|
-
"สร้างกล่องใหม่"
|
|
899
|
-
] })
|
|
937
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
938
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
939
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: "ฐานราคาตามระยะทาง (Messenger 3H)" }),
|
|
940
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-fg-subtle text-sm", children: "กำหนดราคาเริ่มต้นตามระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
|
|
900
941
|
] }),
|
|
901
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.
|
|
902
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
903
|
-
|
|
904
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ขนาด (กว้าง×ยาว×สูง cm)" }),
|
|
905
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "น้ำหนักสูงสุด (kg)" }),
|
|
906
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
907
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
|
|
908
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
909
|
-
] }) }),
|
|
910
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredBoxes.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลกล่องพัสดุ" }) }) : filteredBoxes.map((box) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
911
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.name }),
|
|
912
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
|
|
913
|
-
box.width_cm,
|
|
914
|
-
" × ",
|
|
915
|
-
box.length_cm,
|
|
916
|
-
" × ",
|
|
917
|
-
box.height_cm
|
|
918
|
-
] }),
|
|
919
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.max_weight_kg }),
|
|
920
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.price_thb.toFixed(2) }),
|
|
921
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
922
|
-
ui.Badge,
|
|
923
|
-
{
|
|
924
|
-
color: box.active ? "green" : "grey",
|
|
925
|
-
className: "cursor-pointer",
|
|
926
|
-
onClick: () => handleToggleActive(box),
|
|
927
|
-
children: box.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
928
|
-
}
|
|
929
|
-
) }),
|
|
930
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
931
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
932
|
-
ui.Button,
|
|
933
|
-
{
|
|
934
|
-
variant: "transparent",
|
|
935
|
-
size: "small",
|
|
936
|
-
onClick: () => handleEdit(box),
|
|
937
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
|
|
938
|
-
}
|
|
939
|
-
),
|
|
940
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
941
|
-
ui.Button,
|
|
942
|
-
{
|
|
943
|
-
variant: "transparent",
|
|
944
|
-
size: "small",
|
|
945
|
-
onClick: () => openDeletePrompt(box.id, box.name),
|
|
946
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
|
|
947
|
-
}
|
|
948
|
-
)
|
|
949
|
-
] }) })
|
|
950
|
-
] }, box.id)) })
|
|
942
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: () => setIsModalOpen(true), children: [
|
|
943
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
|
|
944
|
+
"เพิ่มช่วงราคา"
|
|
951
945
|
] })
|
|
952
946
|
] }),
|
|
953
|
-
|
|
947
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { className: "mt-4", children: [
|
|
948
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
949
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ช่วงระยะทาง" }),
|
|
950
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
951
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ลำดับ" }),
|
|
952
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
|
|
953
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
954
|
+
] }) }),
|
|
955
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : orderedRates.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ยังไม่ได้ตั้งค่าฐานราคาตามระยะทาง" }) }) : orderedRates.map((rate) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
956
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatDistanceRange(rate) }),
|
|
957
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
|
|
958
|
+
"฿",
|
|
959
|
+
rate.price.toFixed(2)
|
|
960
|
+
] }),
|
|
961
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: rate.priority ?? 0 }),
|
|
962
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
963
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
964
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
965
|
+
ui.Button,
|
|
966
|
+
{
|
|
967
|
+
variant: "transparent",
|
|
968
|
+
size: "small",
|
|
969
|
+
onClick: () => {
|
|
970
|
+
setEditingRate(rate);
|
|
971
|
+
setIsModalOpen(true);
|
|
972
|
+
},
|
|
973
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
|
|
974
|
+
}
|
|
975
|
+
),
|
|
976
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
977
|
+
ui.Button,
|
|
978
|
+
{
|
|
979
|
+
variant: "transparent",
|
|
980
|
+
size: "small",
|
|
981
|
+
onClick: () => setDeleteId(rate.id),
|
|
982
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
|
|
983
|
+
}
|
|
984
|
+
)
|
|
985
|
+
] }) })
|
|
986
|
+
] }, rate.id)) })
|
|
987
|
+
] }),
|
|
988
|
+
isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(MessengerBaseRateModal, { rate: editingRate, onClose: handleModalClose }),
|
|
954
989
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
955
990
|
ui.Prompt,
|
|
956
991
|
{
|
|
957
992
|
variant: "confirmation",
|
|
958
|
-
open: !!
|
|
959
|
-
onOpenChange: () =>
|
|
960
|
-
setDeleteBoxId(null);
|
|
961
|
-
setDeleteBoxName("");
|
|
962
|
-
},
|
|
993
|
+
open: !!deleteId,
|
|
994
|
+
onOpenChange: () => setDeleteId(null),
|
|
963
995
|
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
|
|
964
996
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
|
|
965
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "
|
|
966
|
-
/* @__PURE__ */ jsxRuntime.
|
|
967
|
-
'คุณต้องการลบกล่อง "',
|
|
968
|
-
deleteBoxName,
|
|
969
|
-
'" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
|
|
970
|
-
] })
|
|
997
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบฐานราคา" }),
|
|
998
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: "คุณต้องการลบช่วงราคานี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
|
|
971
999
|
] }),
|
|
972
1000
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
|
|
973
1001
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "ยกเลิก" }),
|
|
@@ -978,126 +1006,68 @@ const ParcelBoxesTable = () => {
|
|
|
978
1006
|
)
|
|
979
1007
|
] });
|
|
980
1008
|
};
|
|
981
|
-
const
|
|
982
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-
|
|
983
|
-
/* @__PURE__ */ jsxRuntime.
|
|
984
|
-
|
|
1009
|
+
const BasePricingPage = () => {
|
|
1010
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-6", children: [
|
|
1011
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
1012
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "ฐานราคาตามระยะทาง (Messenger)" }),
|
|
1013
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "ตั้งราคาฐานสำหรับบริการ Messenger 3 ชั่วโมงตามช่วงระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
|
|
1014
|
+
] }),
|
|
1015
|
+
/* @__PURE__ */ jsxRuntime.jsx(MessengerBasePricingTable, {})
|
|
985
1016
|
] }) });
|
|
986
1017
|
};
|
|
987
1018
|
const config$3 = adminSdk.defineRouteConfig({
|
|
988
|
-
icon: icons.
|
|
989
|
-
label: "
|
|
1019
|
+
icon: icons.Directions,
|
|
1020
|
+
label: "ฐานราคาตามระยะทาง"
|
|
990
1021
|
});
|
|
991
|
-
const
|
|
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
|
-
];
|
|
1070
|
-
const ServiceAreaModal = ({ area, onClose }) => {
|
|
1022
|
+
const ParcelBoxModal = ({ box, onClose }) => {
|
|
1071
1023
|
const [formData, setFormData] = react.useState({
|
|
1072
|
-
|
|
1073
|
-
|
|
1024
|
+
name: "",
|
|
1025
|
+
width_cm: "",
|
|
1026
|
+
length_cm: "",
|
|
1027
|
+
height_cm: "",
|
|
1028
|
+
max_weight_kg: "",
|
|
1029
|
+
price_thb: "",
|
|
1074
1030
|
active: true
|
|
1075
1031
|
});
|
|
1076
1032
|
const [isSaving, setIsSaving] = react.useState(false);
|
|
1077
1033
|
react.useEffect(() => {
|
|
1078
|
-
if (
|
|
1034
|
+
if (box) {
|
|
1079
1035
|
setFormData({
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1036
|
+
name: box.name,
|
|
1037
|
+
width_cm: box.width_cm.toString(),
|
|
1038
|
+
length_cm: box.length_cm.toString(),
|
|
1039
|
+
height_cm: box.height_cm.toString(),
|
|
1040
|
+
max_weight_kg: box.max_weight_kg.toString(),
|
|
1041
|
+
price_thb: box.price_thb.toString(),
|
|
1042
|
+
active: box.active
|
|
1083
1043
|
});
|
|
1084
1044
|
}
|
|
1085
|
-
}, [
|
|
1045
|
+
}, [box]);
|
|
1086
1046
|
const handleSubmit = async (e) => {
|
|
1087
1047
|
e.preventDefault();
|
|
1088
1048
|
const errors = [];
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
errors.push("กรุณากรอกค่า");
|
|
1049
|
+
if (!formData.name.trim()) {
|
|
1050
|
+
errors.push("กรุณากรอกชื่อกล่อง");
|
|
1092
1051
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1052
|
+
const width = parseFloat(formData.width_cm);
|
|
1053
|
+
const length = parseFloat(formData.length_cm);
|
|
1054
|
+
const height = parseFloat(formData.height_cm);
|
|
1055
|
+
const maxWeight = parseFloat(formData.max_weight_kg);
|
|
1056
|
+
const price = parseFloat(formData.price_thb);
|
|
1057
|
+
if (isNaN(width) || width <= 0) {
|
|
1058
|
+
errors.push("ความกว้างต้องเป็นตัวเลขมากกว่า 0");
|
|
1059
|
+
}
|
|
1060
|
+
if (isNaN(length) || length <= 0) {
|
|
1061
|
+
errors.push("ความยาวต้องเป็นตัวเลขมากกว่า 0");
|
|
1062
|
+
}
|
|
1063
|
+
if (isNaN(height) || height <= 0) {
|
|
1064
|
+
errors.push("ความสูงต้องเป็นตัวเลขมากกว่า 0");
|
|
1065
|
+
}
|
|
1066
|
+
if (isNaN(maxWeight) || maxWeight <= 0) {
|
|
1067
|
+
errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
|
|
1068
|
+
}
|
|
1069
|
+
if (isNaN(price) || price < 0) {
|
|
1070
|
+
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
1101
1071
|
}
|
|
1102
1072
|
if (errors.length > 0) {
|
|
1103
1073
|
ui.toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
@@ -1108,12 +1078,16 @@ const ServiceAreaModal = ({ area, onClose }) => {
|
|
|
1108
1078
|
setIsSaving(true);
|
|
1109
1079
|
try {
|
|
1110
1080
|
const payload = {
|
|
1111
|
-
|
|
1112
|
-
|
|
1081
|
+
name: formData.name.trim(),
|
|
1082
|
+
width_cm: width,
|
|
1083
|
+
length_cm: length,
|
|
1084
|
+
height_cm: height,
|
|
1085
|
+
max_weight_kg: maxWeight,
|
|
1086
|
+
price_thb: price,
|
|
1113
1087
|
active: formData.active
|
|
1114
1088
|
};
|
|
1115
|
-
const url =
|
|
1116
|
-
const method =
|
|
1089
|
+
const url = box ? `/admin/boxes/${box.id}` : "/admin/boxes";
|
|
1090
|
+
const method = box ? "PUT" : "POST";
|
|
1117
1091
|
const response = await fetch(url, {
|
|
1118
1092
|
method,
|
|
1119
1093
|
headers: { "Content-Type": "application/json" },
|
|
@@ -1122,7 +1096,7 @@ const ServiceAreaModal = ({ area, onClose }) => {
|
|
|
1122
1096
|
});
|
|
1123
1097
|
if (response.ok) {
|
|
1124
1098
|
ui.toast.success("สำเร็จ", {
|
|
1125
|
-
description:
|
|
1099
|
+
description: box ? "แก้ไขกล่องพัสดุแล้ว" : "สร้างกล่องพัสดุใหม่แล้ว"
|
|
1126
1100
|
});
|
|
1127
1101
|
onClose();
|
|
1128
1102
|
} else {
|
|
@@ -1138,53 +1112,97 @@ const ServiceAreaModal = ({ area, onClose }) => {
|
|
|
1138
1112
|
}
|
|
1139
1113
|
};
|
|
1140
1114
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
1141
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children:
|
|
1115
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: box ? "แก้ไขกล่องพัสดุ" : "สร้างกล่องพัสดุใหม่" }) }),
|
|
1142
1116
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
1143
1117
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1144
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
1145
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1146
|
-
ui.
|
|
1118
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "name", children: "ชื่อกล่อง *" }),
|
|
1119
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1120
|
+
ui.Input,
|
|
1121
|
+
{
|
|
1122
|
+
id: "name",
|
|
1123
|
+
value: formData.name,
|
|
1124
|
+
onChange: (e) => setFormData({ ...formData, name: e.target.value }),
|
|
1125
|
+
placeholder: "เช่น กล่อง S, กล่อง M",
|
|
1126
|
+
required: true
|
|
1127
|
+
}
|
|
1128
|
+
)
|
|
1129
|
+
] }),
|
|
1130
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-x-4", children: [
|
|
1131
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1132
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "width", children: "ความกว้าง (cm) *" }),
|
|
1133
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1134
|
+
ui.Input,
|
|
1135
|
+
{
|
|
1136
|
+
id: "width",
|
|
1137
|
+
type: "number",
|
|
1138
|
+
step: "0.1",
|
|
1139
|
+
value: formData.width_cm,
|
|
1140
|
+
onChange: (e) => setFormData({ ...formData, width_cm: e.target.value }),
|
|
1141
|
+
placeholder: "20",
|
|
1142
|
+
required: true
|
|
1143
|
+
}
|
|
1144
|
+
)
|
|
1145
|
+
] }),
|
|
1146
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1147
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "length", children: "ความยาว (cm) *" }),
|
|
1148
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1149
|
+
ui.Input,
|
|
1150
|
+
{
|
|
1151
|
+
id: "length",
|
|
1152
|
+
type: "number",
|
|
1153
|
+
step: "0.1",
|
|
1154
|
+
value: formData.length_cm,
|
|
1155
|
+
onChange: (e) => setFormData({ ...formData, length_cm: e.target.value }),
|
|
1156
|
+
placeholder: "30",
|
|
1157
|
+
required: true
|
|
1158
|
+
}
|
|
1159
|
+
)
|
|
1160
|
+
] }),
|
|
1161
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1162
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "height", children: "ความสูง (cm) *" }),
|
|
1163
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1164
|
+
ui.Input,
|
|
1165
|
+
{
|
|
1166
|
+
id: "height",
|
|
1167
|
+
type: "number",
|
|
1168
|
+
step: "0.1",
|
|
1169
|
+
value: formData.height_cm,
|
|
1170
|
+
onChange: (e) => setFormData({ ...formData, height_cm: e.target.value }),
|
|
1171
|
+
placeholder: "15",
|
|
1172
|
+
required: true
|
|
1173
|
+
}
|
|
1174
|
+
)
|
|
1175
|
+
] })
|
|
1176
|
+
] }),
|
|
1177
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1178
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
|
|
1179
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1180
|
+
ui.Input,
|
|
1147
1181
|
{
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "POSTCODE_PREFIX", children: "รหัสไปรษณีย์" })
|
|
1156
|
-
] })
|
|
1157
|
-
]
|
|
1182
|
+
id: "maxWeight",
|
|
1183
|
+
type: "number",
|
|
1184
|
+
step: "0.1",
|
|
1185
|
+
value: formData.max_weight_kg,
|
|
1186
|
+
onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
|
|
1187
|
+
placeholder: "0.5",
|
|
1188
|
+
required: true
|
|
1158
1189
|
}
|
|
1159
1190
|
)
|
|
1160
1191
|
] }),
|
|
1161
1192
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1162
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
1163
|
-
|
|
1164
|
-
ui.Select,
|
|
1165
|
-
{
|
|
1166
|
-
value: formData.value,
|
|
1167
|
-
onValueChange: (value) => setFormData({ ...formData, value }),
|
|
1168
|
-
required: true,
|
|
1169
|
-
children: [
|
|
1170
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกจังหวัด" }) }),
|
|
1171
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: TH_PROVINCES.map((province) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: province, children: province }, province)) })
|
|
1172
|
-
]
|
|
1173
|
-
}
|
|
1174
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1193
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
|
|
1194
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1175
1195
|
ui.Input,
|
|
1176
1196
|
{
|
|
1177
|
-
id: "
|
|
1178
|
-
type: "
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
pattern: "\\d{1,5}",
|
|
1197
|
+
id: "price",
|
|
1198
|
+
type: "number",
|
|
1199
|
+
step: "0.01",
|
|
1200
|
+
value: formData.price_thb,
|
|
1201
|
+
onChange: (e) => setFormData({ ...formData, price_thb: e.target.value }),
|
|
1202
|
+
placeholder: "10.00",
|
|
1184
1203
|
required: true
|
|
1185
1204
|
}
|
|
1186
|
-
)
|
|
1187
|
-
formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
|
|
1205
|
+
)
|
|
1188
1206
|
] }),
|
|
1189
1207
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
1190
1208
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1198,81 +1216,59 @@ const ServiceAreaModal = ({ area, onClose }) => {
|
|
|
1198
1216
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
|
|
1199
1217
|
] }),
|
|
1200
1218
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
1201
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1202
|
-
ui.Button,
|
|
1203
|
-
{
|
|
1204
|
-
type: "button",
|
|
1205
|
-
variant: "secondary",
|
|
1206
|
-
onClick: onClose,
|
|
1207
|
-
disabled: isSaving,
|
|
1208
|
-
children: "ยกเลิก"
|
|
1209
|
-
}
|
|
1210
|
-
),
|
|
1219
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
|
|
1211
1220
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
|
|
1212
1221
|
] })
|
|
1213
1222
|
] }) })
|
|
1214
1223
|
] }) });
|
|
1215
1224
|
};
|
|
1216
|
-
const
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
{ value: "POSTCODE_PREFIX", label: "รหัสไปรษณีย์" }
|
|
1220
|
-
];
|
|
1221
|
-
const ServiceAreasTable = () => {
|
|
1222
|
-
const [areas, setAreas] = react.useState([]);
|
|
1223
|
-
const [filteredAreas, setFilteredAreas] = react.useState([]);
|
|
1225
|
+
const ParcelBoxesTable = () => {
|
|
1226
|
+
const [boxes, setBoxes] = react.useState([]);
|
|
1227
|
+
const [filteredBoxes, setFilteredBoxes] = react.useState([]);
|
|
1224
1228
|
const [searchTerm, setSearchTerm] = react.useState("");
|
|
1225
|
-
const [kindFilter, setKindFilter] = react.useState("ALL");
|
|
1226
1229
|
const [isModalOpen, setIsModalOpen] = react.useState(false);
|
|
1227
|
-
const [
|
|
1230
|
+
const [editingBox, setEditingBox] = react.useState(null);
|
|
1228
1231
|
const [isLoading, setIsLoading] = react.useState(false);
|
|
1229
|
-
const [
|
|
1230
|
-
const [
|
|
1232
|
+
const [deleteBoxId, setDeleteBoxId] = react.useState(null);
|
|
1233
|
+
const [deleteBoxName, setDeleteBoxName] = react.useState("");
|
|
1231
1234
|
react.useEffect(() => {
|
|
1232
|
-
|
|
1235
|
+
fetchBoxes();
|
|
1233
1236
|
}, []);
|
|
1234
1237
|
react.useEffect(() => {
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
(area) => area.value.toLowerCase().includes(searchTerm.toLowerCase())
|
|
1242
|
-
);
|
|
1243
|
-
}
|
|
1244
|
-
setFilteredAreas(filtered);
|
|
1245
|
-
}, [searchTerm, kindFilter, areas]);
|
|
1246
|
-
const fetchAreas = async () => {
|
|
1238
|
+
const filtered = boxes.filter(
|
|
1239
|
+
(box) => box.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
1240
|
+
);
|
|
1241
|
+
setFilteredBoxes(filtered);
|
|
1242
|
+
}, [searchTerm, boxes]);
|
|
1243
|
+
const fetchBoxes = async () => {
|
|
1247
1244
|
setIsLoading(true);
|
|
1248
1245
|
try {
|
|
1249
|
-
const response = await fetch("/admin/
|
|
1246
|
+
const response = await fetch("/admin/boxes", {
|
|
1250
1247
|
credentials: "include"
|
|
1251
1248
|
});
|
|
1252
1249
|
const data = await response.json();
|
|
1253
|
-
|
|
1254
|
-
setAreas(data.service_areas || []);
|
|
1250
|
+
setBoxes(data.boxes || []);
|
|
1255
1251
|
} catch (error) {
|
|
1256
1252
|
ui.toast.error("ข้อผิดพลาด", {
|
|
1257
|
-
description: "
|
|
1253
|
+
description: "ไม่สามารถโหลดข้อมูลกล่องพัสดุได้"
|
|
1258
1254
|
});
|
|
1259
1255
|
} finally {
|
|
1260
1256
|
setIsLoading(false);
|
|
1261
1257
|
}
|
|
1262
1258
|
};
|
|
1263
|
-
const handleToggleActive = async (
|
|
1259
|
+
const handleToggleActive = async (box) => {
|
|
1264
1260
|
try {
|
|
1265
|
-
const response = await fetch(`/admin/
|
|
1261
|
+
const response = await fetch(`/admin/boxes/${box.id}`, {
|
|
1266
1262
|
method: "PUT",
|
|
1267
1263
|
headers: { "Content-Type": "application/json" },
|
|
1268
1264
|
credentials: "include",
|
|
1269
|
-
body: JSON.stringify({ ...
|
|
1265
|
+
body: JSON.stringify({ ...box, active: !box.active })
|
|
1270
1266
|
});
|
|
1271
1267
|
if (response.ok) {
|
|
1272
1268
|
ui.toast.success("สำเร็จ", {
|
|
1273
|
-
description: `${
|
|
1269
|
+
description: `${box.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}กล่อง ${box.name} แล้ว`
|
|
1274
1270
|
});
|
|
1275
|
-
|
|
1271
|
+
fetchBoxes();
|
|
1276
1272
|
} else {
|
|
1277
1273
|
throw new Error("Failed to update");
|
|
1278
1274
|
}
|
|
@@ -1283,87 +1279,91 @@ const ServiceAreasTable = () => {
|
|
|
1283
1279
|
}
|
|
1284
1280
|
};
|
|
1285
1281
|
const handleDelete = async () => {
|
|
1286
|
-
if (!
|
|
1282
|
+
if (!deleteBoxId) return;
|
|
1287
1283
|
try {
|
|
1288
|
-
const response = await fetch(`/admin/
|
|
1284
|
+
const response = await fetch(`/admin/boxes/${deleteBoxId}`, {
|
|
1289
1285
|
method: "DELETE",
|
|
1290
1286
|
credentials: "include"
|
|
1291
1287
|
});
|
|
1292
1288
|
if (response.ok) {
|
|
1293
1289
|
ui.toast.success("สำเร็จ", {
|
|
1294
|
-
description:
|
|
1290
|
+
description: `ลบกล่อง ${deleteBoxName} แล้ว`
|
|
1295
1291
|
});
|
|
1296
|
-
|
|
1292
|
+
fetchBoxes();
|
|
1297
1293
|
} else {
|
|
1298
1294
|
throw new Error("Failed to delete");
|
|
1299
1295
|
}
|
|
1300
1296
|
} catch (error) {
|
|
1301
1297
|
ui.toast.error("ข้อผิดพลาด", {
|
|
1302
|
-
description: "
|
|
1298
|
+
description: "ไม่สามารถลบกล่องได้"
|
|
1303
1299
|
});
|
|
1304
1300
|
} finally {
|
|
1305
|
-
|
|
1306
|
-
|
|
1301
|
+
setDeleteBoxId(null);
|
|
1302
|
+
setDeleteBoxName("");
|
|
1307
1303
|
}
|
|
1308
1304
|
};
|
|
1309
|
-
const handleEdit = (
|
|
1310
|
-
|
|
1305
|
+
const handleEdit = (box) => {
|
|
1306
|
+
setEditingBox(box);
|
|
1311
1307
|
setIsModalOpen(true);
|
|
1312
1308
|
};
|
|
1313
1309
|
const handleCreateNew = () => {
|
|
1314
|
-
|
|
1310
|
+
setEditingBox(null);
|
|
1315
1311
|
setIsModalOpen(true);
|
|
1316
1312
|
};
|
|
1317
1313
|
const handleModalClose = () => {
|
|
1318
1314
|
setIsModalOpen(false);
|
|
1319
|
-
|
|
1320
|
-
|
|
1315
|
+
setEditingBox(null);
|
|
1316
|
+
fetchBoxes();
|
|
1321
1317
|
};
|
|
1322
|
-
const openDeletePrompt = (id,
|
|
1323
|
-
|
|
1324
|
-
|
|
1318
|
+
const openDeletePrompt = (id, name) => {
|
|
1319
|
+
setDeleteBoxId(id);
|
|
1320
|
+
setDeleteBoxName(name);
|
|
1325
1321
|
};
|
|
1326
1322
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1327
1323
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1328
1324
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
1329
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
),
|
|
1340
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: kindFilter, onValueChange: setKindFilter, children: [
|
|
1341
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ประเภท" }) }),
|
|
1342
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: AREA_KINDS.map((kind) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: kind.value, children: kind.label }, kind.value)) })
|
|
1343
|
-
] })
|
|
1344
|
-
] }),
|
|
1325
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1326
|
+
ui.Input,
|
|
1327
|
+
{
|
|
1328
|
+
type: "search",
|
|
1329
|
+
placeholder: "ค้นหาชื่อกล่อง...",
|
|
1330
|
+
value: searchTerm,
|
|
1331
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
1332
|
+
className: "max-w-md"
|
|
1333
|
+
}
|
|
1334
|
+
),
|
|
1345
1335
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
|
|
1346
1336
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
|
|
1347
|
-
"
|
|
1337
|
+
"สร้างกล่องใหม่"
|
|
1348
1338
|
] })
|
|
1349
1339
|
] }),
|
|
1350
1340
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
1351
1341
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
1352
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "
|
|
1353
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "
|
|
1342
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ชื่อ" }),
|
|
1343
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ขนาด (กว้าง×ยาว×สูง cm)" }),
|
|
1344
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "น้ำหนักสูงสุด (kg)" }),
|
|
1345
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
1354
1346
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
|
|
1355
1347
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
1356
1348
|
] }) }),
|
|
1357
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) :
|
|
1358
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children:
|
|
1359
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1349
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredBoxes.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลกล่องพัสดุ" }) }) : filteredBoxes.map((box) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
1350
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.name }),
|
|
1351
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
|
|
1352
|
+
box.width_cm,
|
|
1353
|
+
" × ",
|
|
1354
|
+
box.length_cm,
|
|
1355
|
+
" × ",
|
|
1356
|
+
box.height_cm
|
|
1357
|
+
] }),
|
|
1358
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.max_weight_kg }),
|
|
1359
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.price_thb.toFixed(2) }),
|
|
1360
1360
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1361
1361
|
ui.Badge,
|
|
1362
1362
|
{
|
|
1363
|
-
color:
|
|
1363
|
+
color: box.active ? "green" : "grey",
|
|
1364
1364
|
className: "cursor-pointer",
|
|
1365
|
-
onClick: () => handleToggleActive(
|
|
1366
|
-
children:
|
|
1365
|
+
onClick: () => handleToggleActive(box),
|
|
1366
|
+
children: box.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
1367
1367
|
}
|
|
1368
1368
|
) }),
|
|
1369
1369
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
@@ -1372,7 +1372,7 @@ const ServiceAreasTable = () => {
|
|
|
1372
1372
|
{
|
|
1373
1373
|
variant: "transparent",
|
|
1374
1374
|
size: "small",
|
|
1375
|
-
onClick: () => handleEdit(
|
|
1375
|
+
onClick: () => handleEdit(box),
|
|
1376
1376
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
|
|
1377
1377
|
}
|
|
1378
1378
|
),
|
|
@@ -1381,30 +1381,30 @@ const ServiceAreasTable = () => {
|
|
|
1381
1381
|
{
|
|
1382
1382
|
variant: "transparent",
|
|
1383
1383
|
size: "small",
|
|
1384
|
-
onClick: () => openDeletePrompt(
|
|
1384
|
+
onClick: () => openDeletePrompt(box.id, box.name),
|
|
1385
1385
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
|
|
1386
1386
|
}
|
|
1387
1387
|
)
|
|
1388
1388
|
] }) })
|
|
1389
|
-
] },
|
|
1389
|
+
] }, box.id)) })
|
|
1390
1390
|
] })
|
|
1391
1391
|
] }),
|
|
1392
|
-
isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1392
|
+
isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ParcelBoxModal, { box: editingBox, onClose: handleModalClose }),
|
|
1393
1393
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1394
1394
|
ui.Prompt,
|
|
1395
1395
|
{
|
|
1396
1396
|
variant: "confirmation",
|
|
1397
|
-
open: !!
|
|
1397
|
+
open: !!deleteBoxId,
|
|
1398
1398
|
onOpenChange: () => {
|
|
1399
|
-
|
|
1400
|
-
|
|
1399
|
+
setDeleteBoxId(null);
|
|
1400
|
+
setDeleteBoxName("");
|
|
1401
1401
|
},
|
|
1402
1402
|
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
|
|
1403
1403
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
|
|
1404
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "
|
|
1404
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบกล่องพัสดุ" }),
|
|
1405
1405
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
|
|
1406
|
-
'
|
|
1407
|
-
|
|
1406
|
+
'คุณต้องการลบกล่อง "',
|
|
1407
|
+
deleteBoxName,
|
|
1408
1408
|
'" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
|
|
1409
1409
|
] })
|
|
1410
1410
|
] }),
|
|
@@ -1417,63 +1417,98 @@ const ServiceAreasTable = () => {
|
|
|
1417
1417
|
)
|
|
1418
1418
|
] });
|
|
1419
1419
|
};
|
|
1420
|
-
const
|
|
1420
|
+
const BoxesPage = () => {
|
|
1421
1421
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1422
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "
|
|
1423
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1422
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "กล่องพัสดุ" }) }),
|
|
1423
|
+
/* @__PURE__ */ jsxRuntime.jsx(ParcelBoxesTable, {})
|
|
1424
1424
|
] }) });
|
|
1425
1425
|
};
|
|
1426
1426
|
const config$2 = adminSdk.defineRouteConfig({
|
|
1427
|
-
icon: icons.
|
|
1428
|
-
label: "
|
|
1427
|
+
icon: icons.ArchiveBox,
|
|
1428
|
+
label: "กล่องพัสดุ"
|
|
1429
1429
|
});
|
|
1430
|
-
const
|
|
1431
|
-
{ value: "
|
|
1432
|
-
{ value: "
|
|
1433
|
-
{ value: "
|
|
1434
|
-
{ value: "TAPE", label: "เทป/กาว" },
|
|
1435
|
-
{ value: "LABEL", label: "ฉลาก/สติกเกอร์" },
|
|
1436
|
-
{ value: "OTHER", label: "อื่นๆ" }
|
|
1430
|
+
const CARRIER_TYPES$1 = [
|
|
1431
|
+
{ value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
|
|
1432
|
+
{ value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
|
|
1433
|
+
{ value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
|
|
1437
1434
|
];
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1435
|
+
const SERVICE_CODES$1 = [
|
|
1436
|
+
{ value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม.", usesHours: true },
|
|
1437
|
+
{ value: "SAME_DAY", label: "ส่งด่วนภายในวัน", usesHours: true },
|
|
1438
|
+
{ value: "STANDARD_3_5D", label: "EMS 3-5 วัน", usesHours: false },
|
|
1439
|
+
{ value: "EXPRESS_1_2D", label: "Express 1-2 วัน", usesHours: false }
|
|
1440
|
+
];
|
|
1441
|
+
const ShippingRateModal = ({ rate, onClose }) => {
|
|
1442
1442
|
const [formData, setFormData] = react.useState({
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1443
|
+
carrier_type: "COMPANY_FLEET",
|
|
1444
|
+
service_code: "MESSENGER_3H",
|
|
1445
|
+
max_weight_kg: "",
|
|
1446
|
+
price: "",
|
|
1446
1447
|
currency: "THB",
|
|
1447
|
-
|
|
1448
|
-
|
|
1448
|
+
priority: "0",
|
|
1449
|
+
eta_hours_min: "",
|
|
1450
|
+
eta_hours_max: "",
|
|
1451
|
+
eta_days_min: "",
|
|
1452
|
+
eta_days_max: "",
|
|
1449
1453
|
active: true
|
|
1450
1454
|
});
|
|
1451
1455
|
const [isSaving, setIsSaving] = react.useState(false);
|
|
1456
|
+
const selectedService = SERVICE_CODES$1.find((s) => s.value === formData.service_code);
|
|
1457
|
+
const usesHours = (selectedService == null ? void 0 : selectedService.usesHours) ?? false;
|
|
1458
|
+
const isMessenger = formData.carrier_type === "COMPANY_FLEET" && formData.service_code === "MESSENGER_3H";
|
|
1452
1459
|
react.useEffect(() => {
|
|
1453
|
-
|
|
1460
|
+
var _a, _b, _c, _d;
|
|
1461
|
+
if (rate) {
|
|
1454
1462
|
setFormData({
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1463
|
+
carrier_type: rate.carrier_type,
|
|
1464
|
+
service_code: rate.service_code,
|
|
1465
|
+
max_weight_kg: rate.max_weight_kg.toString(),
|
|
1466
|
+
price: rate.price.toString(),
|
|
1467
|
+
currency: rate.currency,
|
|
1468
|
+
priority: (rate.priority ?? 0).toString(),
|
|
1469
|
+
eta_hours_min: ((_a = rate.eta_hours_min) == null ? void 0 : _a.toString()) || "",
|
|
1470
|
+
eta_hours_max: ((_b = rate.eta_hours_max) == null ? void 0 : _b.toString()) || "",
|
|
1471
|
+
eta_days_min: ((_c = rate.eta_days_min) == null ? void 0 : _c.toString()) || "",
|
|
1472
|
+
eta_days_max: ((_d = rate.eta_days_max) == null ? void 0 : _d.toString()) || "",
|
|
1473
|
+
active: rate.active
|
|
1462
1474
|
});
|
|
1463
1475
|
}
|
|
1464
|
-
}, [
|
|
1476
|
+
}, [rate]);
|
|
1465
1477
|
const handleSubmit = async (e) => {
|
|
1466
1478
|
e.preventDefault();
|
|
1467
1479
|
const errors = [];
|
|
1468
|
-
|
|
1469
|
-
|
|
1480
|
+
const maxWeight = parseFloat(formData.max_weight_kg);
|
|
1481
|
+
const price = parseFloat(formData.price);
|
|
1482
|
+
const priority = parseInt(formData.priority);
|
|
1483
|
+
if (isNaN(maxWeight) || maxWeight <= 0) {
|
|
1484
|
+
errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
|
|
1470
1485
|
}
|
|
1471
|
-
if (
|
|
1472
|
-
errors.push("
|
|
1486
|
+
if (isNaN(price) || price < 0) {
|
|
1487
|
+
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
1473
1488
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1489
|
+
if (isNaN(priority)) {
|
|
1490
|
+
errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
|
|
1491
|
+
}
|
|
1492
|
+
let etaHoursMin;
|
|
1493
|
+
let etaHoursMax;
|
|
1494
|
+
let etaDaysMin;
|
|
1495
|
+
let etaDaysMax;
|
|
1496
|
+
if (usesHours) {
|
|
1497
|
+
etaHoursMin = formData.eta_hours_min ? parseFloat(formData.eta_hours_min) : void 0;
|
|
1498
|
+
etaHoursMax = formData.eta_hours_max ? parseFloat(formData.eta_hours_max) : void 0;
|
|
1499
|
+
if (etaHoursMin === void 0 || etaHoursMax === void 0) {
|
|
1500
|
+
errors.push("กรุณากรอก ETA เป็นชั่วโมงให้ครบ");
|
|
1501
|
+
} else if (etaHoursMin > etaHoursMax) {
|
|
1502
|
+
errors.push("ETA ชั่วโมงต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
|
|
1503
|
+
}
|
|
1504
|
+
} else {
|
|
1505
|
+
etaDaysMin = formData.eta_days_min ? parseFloat(formData.eta_days_min) : void 0;
|
|
1506
|
+
etaDaysMax = formData.eta_days_max ? parseFloat(formData.eta_days_max) : void 0;
|
|
1507
|
+
if (etaDaysMin === void 0 || etaDaysMax === void 0) {
|
|
1508
|
+
errors.push("กรุณากรอก ETA เป็นวันให้ครบ");
|
|
1509
|
+
} else if (etaDaysMin > etaDaysMax) {
|
|
1510
|
+
errors.push("ETA วันต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
|
|
1511
|
+
}
|
|
1477
1512
|
}
|
|
1478
1513
|
if (errors.length > 0) {
|
|
1479
1514
|
ui.toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
@@ -1484,16 +1519,23 @@ const MaterialCostModal = ({
|
|
|
1484
1519
|
setIsSaving(true);
|
|
1485
1520
|
try {
|
|
1486
1521
|
const payload = {
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1522
|
+
carrier_type: formData.carrier_type,
|
|
1523
|
+
service_code: formData.service_code,
|
|
1524
|
+
max_weight_kg: maxWeight,
|
|
1525
|
+
price,
|
|
1490
1526
|
currency: formData.currency,
|
|
1491
|
-
|
|
1492
|
-
description: formData.description.trim() || void 0,
|
|
1527
|
+
priority,
|
|
1493
1528
|
active: formData.active
|
|
1494
1529
|
};
|
|
1495
|
-
|
|
1496
|
-
|
|
1530
|
+
if (usesHours) {
|
|
1531
|
+
payload.eta_hours_min = etaHoursMin;
|
|
1532
|
+
payload.eta_hours_max = etaHoursMax;
|
|
1533
|
+
} else {
|
|
1534
|
+
payload.eta_days_min = etaDaysMin;
|
|
1535
|
+
payload.eta_days_max = etaDaysMax;
|
|
1536
|
+
}
|
|
1537
|
+
const url = rate ? `/admin/shipping-rates/${rate.id}` : "/admin/shipping-rates";
|
|
1538
|
+
const method = rate ? "PUT" : "POST";
|
|
1497
1539
|
const response = await fetch(url, {
|
|
1498
1540
|
method,
|
|
1499
1541
|
headers: { "Content-Type": "application/json" },
|
|
@@ -1502,7 +1544,7 @@ const MaterialCostModal = ({
|
|
|
1502
1544
|
});
|
|
1503
1545
|
if (response.ok) {
|
|
1504
1546
|
ui.toast.success("สำเร็จ", {
|
|
1505
|
-
description:
|
|
1547
|
+
description: rate ? "แก้ไขอัตราค่าขนส่งแล้ว" : "สร้างอัตราค่าขนส่งใหม่แล้ว"
|
|
1506
1548
|
});
|
|
1507
1549
|
onClose();
|
|
1508
1550
|
} else {
|
|
@@ -1518,89 +1560,147 @@ const MaterialCostModal = ({
|
|
|
1518
1560
|
}
|
|
1519
1561
|
};
|
|
1520
1562
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
1521
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children:
|
|
1563
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: rate ? "แก้ไขอัตราค่าขนส่ง" : "สร้างอัตราค่าขนส่งใหม่" }) }),
|
|
1522
1564
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
1523
1565
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1524
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
1525
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1526
|
-
ui.
|
|
1566
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "carrier", children: "ประเภทขนส่ง *" }),
|
|
1567
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1568
|
+
ui.Select,
|
|
1527
1569
|
{
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1570
|
+
value: formData.carrier_type,
|
|
1571
|
+
onValueChange: (value) => setFormData({ ...formData, carrier_type: value }),
|
|
1572
|
+
children: [
|
|
1573
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกประเภทขนส่ง" }) }),
|
|
1574
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CARRIER_TYPES$1.map((carrier) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: carrier.value, children: carrier.label }, carrier.value)) })
|
|
1575
|
+
]
|
|
1533
1576
|
}
|
|
1534
1577
|
)
|
|
1535
1578
|
] }),
|
|
1536
1579
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1537
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
1580
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "service", children: "บริการจัดส่ง *" }),
|
|
1538
1581
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1539
1582
|
ui.Select,
|
|
1540
1583
|
{
|
|
1541
|
-
value: formData.
|
|
1542
|
-
onValueChange: (value) => setFormData({
|
|
1584
|
+
value: formData.service_code,
|
|
1585
|
+
onValueChange: (value) => setFormData({
|
|
1586
|
+
...formData,
|
|
1587
|
+
service_code: value,
|
|
1588
|
+
eta_hours_min: "",
|
|
1589
|
+
eta_hours_max: "",
|
|
1590
|
+
eta_days_min: "",
|
|
1591
|
+
eta_days_max: ""
|
|
1592
|
+
}),
|
|
1543
1593
|
children: [
|
|
1544
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "
|
|
1545
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children:
|
|
1594
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกบริการ" }) }),
|
|
1595
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: SERVICE_CODES$1.map((service) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: service.value, children: service.label }, service.value)) })
|
|
1546
1596
|
]
|
|
1547
1597
|
}
|
|
1548
1598
|
)
|
|
1549
1599
|
] }),
|
|
1600
|
+
isMessenger && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3 text-sm space-y-1", children: [
|
|
1601
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-ui-fg-base", children: "ฐานราคาตามระยะทาง (Messenger)" }),
|
|
1602
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle", children: "ตั้งช่วงราคาตามระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม. สำหรับบริการส่งด่วน 3 ชม." }),
|
|
1603
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { asChild: true, size: "small", variant: "secondary", children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: "/base-pricing", target: "_blank", rel: "noreferrer", children: [
|
|
1604
|
+
"จัดการฐานราคา",
|
|
1605
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "ml-1 h-4 w-4" })
|
|
1606
|
+
] }) }) })
|
|
1607
|
+
] }),
|
|
1550
1608
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
1551
1609
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1552
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
1610
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
|
|
1553
1611
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1554
1612
|
ui.Input,
|
|
1555
1613
|
{
|
|
1556
|
-
id: "
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1614
|
+
id: "maxWeight",
|
|
1615
|
+
type: "number",
|
|
1616
|
+
step: "0.1",
|
|
1617
|
+
min: "0",
|
|
1618
|
+
value: formData.max_weight_kg,
|
|
1619
|
+
onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
|
|
1620
|
+
placeholder: "เช่น 3",
|
|
1560
1621
|
required: true
|
|
1561
1622
|
}
|
|
1562
1623
|
)
|
|
1563
1624
|
] }),
|
|
1564
1625
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1565
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
1626
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
|
|
1566
1627
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1567
1628
|
ui.Input,
|
|
1568
1629
|
{
|
|
1569
|
-
id: "
|
|
1630
|
+
id: "price",
|
|
1570
1631
|
type: "number",
|
|
1571
1632
|
step: "0.01",
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1633
|
+
min: "0",
|
|
1634
|
+
value: formData.price,
|
|
1635
|
+
onChange: (e) => setFormData({ ...formData, price: e.target.value }),
|
|
1636
|
+
placeholder: "เช่น 80",
|
|
1575
1637
|
required: true
|
|
1576
1638
|
}
|
|
1577
1639
|
)
|
|
1578
1640
|
] })
|
|
1579
1641
|
] }),
|
|
1580
1642
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1581
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
1643
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
|
|
1582
1644
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1583
1645
|
ui.Input,
|
|
1584
1646
|
{
|
|
1585
|
-
id: "
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1647
|
+
id: "priority",
|
|
1648
|
+
type: "number",
|
|
1649
|
+
value: formData.priority,
|
|
1650
|
+
onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
|
|
1651
|
+
placeholder: "0"
|
|
1589
1652
|
}
|
|
1590
|
-
)
|
|
1653
|
+
),
|
|
1654
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ตัวเลขสูงกว่าจะถูกเสนอให้เลือกก่อน (ค่าเริ่มต้น 0)" })
|
|
1591
1655
|
] }),
|
|
1592
1656
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1593
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1657
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { children: [
|
|
1658
|
+
usesHours ? "ETA (ชั่วโมง)" : "ETA (วัน)",
|
|
1659
|
+
" *"
|
|
1660
|
+
] }),
|
|
1661
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-x-4 mt-2", children: usesHours ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1662
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1663
|
+
ui.Input,
|
|
1664
|
+
{
|
|
1665
|
+
type: "number",
|
|
1666
|
+
placeholder: "ต่ำสุด",
|
|
1667
|
+
value: formData.eta_hours_min,
|
|
1668
|
+
onChange: (e) => setFormData({ ...formData, eta_hours_min: e.target.value }),
|
|
1669
|
+
required: true
|
|
1670
|
+
}
|
|
1671
|
+
),
|
|
1672
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1673
|
+
ui.Input,
|
|
1674
|
+
{
|
|
1675
|
+
type: "number",
|
|
1676
|
+
placeholder: "สูงสุด",
|
|
1677
|
+
value: formData.eta_hours_max,
|
|
1678
|
+
onChange: (e) => setFormData({ ...formData, eta_hours_max: e.target.value }),
|
|
1679
|
+
required: true
|
|
1680
|
+
}
|
|
1681
|
+
)
|
|
1682
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1683
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1684
|
+
ui.Input,
|
|
1685
|
+
{
|
|
1686
|
+
type: "number",
|
|
1687
|
+
placeholder: "ต่ำสุด",
|
|
1688
|
+
value: formData.eta_days_min,
|
|
1689
|
+
onChange: (e) => setFormData({ ...formData, eta_days_min: e.target.value }),
|
|
1690
|
+
required: true
|
|
1691
|
+
}
|
|
1692
|
+
),
|
|
1693
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1694
|
+
ui.Input,
|
|
1695
|
+
{
|
|
1696
|
+
type: "number",
|
|
1697
|
+
placeholder: "สูงสุด",
|
|
1698
|
+
value: formData.eta_days_max,
|
|
1699
|
+
onChange: (e) => setFormData({ ...formData, eta_days_max: e.target.value }),
|
|
1700
|
+
required: true
|
|
1701
|
+
}
|
|
1702
|
+
)
|
|
1703
|
+
] }) })
|
|
1604
1704
|
] }),
|
|
1605
1705
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
1606
1706
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1614,202 +1714,168 @@ const MaterialCostModal = ({
|
|
|
1614
1714
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
|
|
1615
1715
|
] }),
|
|
1616
1716
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
1617
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1618
|
-
ui.Button,
|
|
1619
|
-
{
|
|
1620
|
-
type: "button",
|
|
1621
|
-
variant: "secondary",
|
|
1622
|
-
onClick: onClose,
|
|
1623
|
-
disabled: isSaving,
|
|
1624
|
-
children: "ยกเลิก"
|
|
1625
|
-
}
|
|
1626
|
-
),
|
|
1717
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
|
|
1627
1718
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
|
|
1628
1719
|
] })
|
|
1629
1720
|
] }) })
|
|
1630
1721
|
] }) });
|
|
1631
1722
|
};
|
|
1632
|
-
const
|
|
1723
|
+
const CARRIER_TYPES = [
|
|
1724
|
+
{ value: "ALL", label: "ทั้งหมด" },
|
|
1725
|
+
{ value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
|
|
1726
|
+
{ value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
|
|
1727
|
+
{ value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
|
|
1728
|
+
];
|
|
1729
|
+
const SERVICE_CODES = [
|
|
1633
1730
|
{ value: "ALL", label: "ทั้งหมด" },
|
|
1634
|
-
{ value: "
|
|
1635
|
-
{ value: "
|
|
1636
|
-
{ value: "
|
|
1637
|
-
{ value: "
|
|
1638
|
-
{ value: "LABEL", label: "ฉลาก/สติกเกอร์" },
|
|
1639
|
-
{ value: "OTHER", label: "อื่นๆ" }
|
|
1731
|
+
{ value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม." },
|
|
1732
|
+
{ value: "SAME_DAY", label: "ส่งด่วนภายในวัน" },
|
|
1733
|
+
{ value: "STANDARD_3_5D", label: "EMS 3-5 วัน" },
|
|
1734
|
+
{ value: "EXPRESS_1_2D", label: "Express 1-2 วัน" }
|
|
1640
1735
|
];
|
|
1641
|
-
const
|
|
1642
|
-
const [
|
|
1643
|
-
const [
|
|
1644
|
-
const [
|
|
1645
|
-
const [
|
|
1736
|
+
const ShippingRatesTable = () => {
|
|
1737
|
+
const [rates, setRates] = react.useState([]);
|
|
1738
|
+
const [filteredRates, setFilteredRates] = react.useState([]);
|
|
1739
|
+
const [carrierFilter, setCarrierFilter] = react.useState("ALL");
|
|
1740
|
+
const [serviceFilter, setServiceFilter] = react.useState("ALL");
|
|
1646
1741
|
const [isModalOpen, setIsModalOpen] = react.useState(false);
|
|
1647
|
-
const [
|
|
1742
|
+
const [editingRate, setEditingRate] = react.useState(null);
|
|
1648
1743
|
const [isLoading, setIsLoading] = react.useState(false);
|
|
1649
|
-
const [
|
|
1650
|
-
const [deleteMaterialCostName, setDeleteMaterialCostName] = react.useState("");
|
|
1744
|
+
const [deleteRateId, setDeleteRateId] = react.useState(null);
|
|
1651
1745
|
react.useEffect(() => {
|
|
1652
|
-
|
|
1746
|
+
fetchRates();
|
|
1653
1747
|
}, []);
|
|
1654
1748
|
react.useEffect(() => {
|
|
1655
|
-
let filtered =
|
|
1656
|
-
if (
|
|
1657
|
-
filtered = filtered.filter((
|
|
1749
|
+
let filtered = rates;
|
|
1750
|
+
if (carrierFilter !== "ALL") {
|
|
1751
|
+
filtered = filtered.filter((rate) => rate.carrier_type === carrierFilter);
|
|
1658
1752
|
}
|
|
1659
|
-
if (
|
|
1660
|
-
filtered = filtered.filter(
|
|
1661
|
-
(mc) => mc.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
1662
|
-
);
|
|
1753
|
+
if (serviceFilter !== "ALL") {
|
|
1754
|
+
filtered = filtered.filter((rate) => rate.service_code === serviceFilter);
|
|
1663
1755
|
}
|
|
1664
|
-
|
|
1665
|
-
}, [
|
|
1666
|
-
const
|
|
1756
|
+
setFilteredRates(filtered);
|
|
1757
|
+
}, [carrierFilter, serviceFilter, rates]);
|
|
1758
|
+
const fetchRates = async () => {
|
|
1667
1759
|
setIsLoading(true);
|
|
1668
1760
|
try {
|
|
1669
|
-
const response = await fetch("/admin/
|
|
1761
|
+
const response = await fetch("/admin/shipping-rates", {
|
|
1670
1762
|
credentials: "include"
|
|
1671
1763
|
});
|
|
1672
1764
|
const data = await response.json();
|
|
1673
|
-
|
|
1765
|
+
setRates(data.rates || []);
|
|
1674
1766
|
} catch (error) {
|
|
1675
1767
|
ui.toast.error("ข้อผิดพลาด", {
|
|
1676
|
-
description: "
|
|
1768
|
+
description: "ไม่สามารถโหลดข้อมูลอัตราค่าขนส่งได้"
|
|
1677
1769
|
});
|
|
1678
1770
|
} finally {
|
|
1679
1771
|
setIsLoading(false);
|
|
1680
1772
|
}
|
|
1681
1773
|
};
|
|
1682
|
-
const handleToggleActive = async (materialCost) => {
|
|
1683
|
-
try {
|
|
1684
|
-
const response = await fetch(`/admin/material-costs/${materialCost.id}`, {
|
|
1685
|
-
method: "PUT",
|
|
1686
|
-
headers: { "Content-Type": "application/json" },
|
|
1687
|
-
credentials: "include",
|
|
1688
|
-
body: JSON.stringify({ ...materialCost, active: !materialCost.active })
|
|
1689
|
-
});
|
|
1690
|
-
if (response.ok) {
|
|
1691
|
-
ui.toast.success("สำเร็จ", {
|
|
1692
|
-
description: `${materialCost.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}วัสดุ ${materialCost.name} แล้ว`
|
|
1693
|
-
});
|
|
1694
|
-
fetchMaterialCosts();
|
|
1695
|
-
} else {
|
|
1696
|
-
throw new Error("Failed to update");
|
|
1697
|
-
}
|
|
1698
|
-
} catch (error) {
|
|
1699
|
-
ui.toast.error("ข้อผิดพลาด", {
|
|
1700
|
-
description: "ไม่สามารถอัพเดทสถานะได้"
|
|
1701
|
-
});
|
|
1702
|
-
}
|
|
1703
|
-
};
|
|
1704
1774
|
const handleDelete = async () => {
|
|
1705
|
-
if (!
|
|
1775
|
+
if (!deleteRateId) return;
|
|
1706
1776
|
try {
|
|
1707
|
-
const response = await fetch(
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
credentials: "include"
|
|
1712
|
-
}
|
|
1713
|
-
);
|
|
1777
|
+
const response = await fetch(`/admin/shipping-rates/${deleteRateId}`, {
|
|
1778
|
+
method: "DELETE",
|
|
1779
|
+
credentials: "include"
|
|
1780
|
+
});
|
|
1714
1781
|
if (response.ok) {
|
|
1715
1782
|
ui.toast.success("สำเร็จ", {
|
|
1716
|
-
description:
|
|
1783
|
+
description: "ลบอัตราค่าขนส่งแล้ว"
|
|
1717
1784
|
});
|
|
1718
|
-
|
|
1785
|
+
fetchRates();
|
|
1719
1786
|
} else {
|
|
1720
1787
|
throw new Error("Failed to delete");
|
|
1721
1788
|
}
|
|
1722
1789
|
} catch (error) {
|
|
1723
1790
|
ui.toast.error("ข้อผิดพลาด", {
|
|
1724
|
-
description: "
|
|
1791
|
+
description: "ไม่สามารถลบอัตราค่าขนส่งได้"
|
|
1725
1792
|
});
|
|
1726
1793
|
} finally {
|
|
1727
|
-
|
|
1728
|
-
setDeleteMaterialCostName("");
|
|
1794
|
+
setDeleteRateId(null);
|
|
1729
1795
|
}
|
|
1730
1796
|
};
|
|
1731
|
-
const handleEdit = (
|
|
1732
|
-
|
|
1797
|
+
const handleEdit = (rate) => {
|
|
1798
|
+
setEditingRate(rate);
|
|
1733
1799
|
setIsModalOpen(true);
|
|
1734
1800
|
};
|
|
1735
1801
|
const handleCreateNew = () => {
|
|
1736
|
-
|
|
1802
|
+
setEditingRate(null);
|
|
1737
1803
|
setIsModalOpen(true);
|
|
1738
1804
|
};
|
|
1739
1805
|
const handleModalClose = () => {
|
|
1740
1806
|
setIsModalOpen(false);
|
|
1741
|
-
|
|
1742
|
-
|
|
1807
|
+
setEditingRate(null);
|
|
1808
|
+
fetchRates();
|
|
1743
1809
|
};
|
|
1744
|
-
const
|
|
1745
|
-
|
|
1746
|
-
|
|
1810
|
+
const getCarrierLabel = (type) => {
|
|
1811
|
+
var _a;
|
|
1812
|
+
return ((_a = CARRIER_TYPES.find((c) => c.value === type)) == null ? void 0 : _a.label) || type;
|
|
1747
1813
|
};
|
|
1748
|
-
const
|
|
1814
|
+
const getServiceLabel = (code) => {
|
|
1749
1815
|
var _a;
|
|
1750
|
-
|
|
1751
|
-
|
|
1816
|
+
return ((_a = SERVICE_CODES.find((s) => s.value === code)) == null ? void 0 : _a.label) || code;
|
|
1817
|
+
};
|
|
1818
|
+
const formatETA = (rate) => {
|
|
1819
|
+
if (rate.eta_hours_min !== null && rate.eta_hours_min !== void 0 && rate.eta_hours_max !== null && rate.eta_hours_max !== void 0) {
|
|
1820
|
+
return `${rate.eta_hours_min}-${rate.eta_hours_max} ชม.`;
|
|
1821
|
+
}
|
|
1822
|
+
if (rate.eta_days_min !== null && rate.eta_days_min !== void 0 && rate.eta_days_max !== null && rate.eta_days_max !== void 0) {
|
|
1823
|
+
return `${rate.eta_days_min}-${rate.eta_days_max} วัน`;
|
|
1824
|
+
}
|
|
1825
|
+
return "-";
|
|
1752
1826
|
};
|
|
1753
1827
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1754
1828
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1755
1829
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
1756
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4
|
|
1757
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1758
|
-
|
|
1759
|
-
{
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1830
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4 items-end", children: [
|
|
1831
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
1832
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "ประเภทขนส่ง" }),
|
|
1833
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: carrierFilter, onValueChange: setCarrierFilter, children: [
|
|
1834
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ทั้งหมด" }) }),
|
|
1835
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CARRIER_TYPES.map((type) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: type.value, children: type.label }, type.value)) })
|
|
1836
|
+
] })
|
|
1837
|
+
] }),
|
|
1838
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
1839
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "บริการจัดส่ง" }),
|
|
1840
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: serviceFilter, onValueChange: setServiceFilter, children: [
|
|
1841
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ทั้งหมด" }) }),
|
|
1842
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: SERVICE_CODES.map((service) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: service.value, children: service.label }, service.value)) })
|
|
1843
|
+
] })
|
|
1770
1844
|
] })
|
|
1771
1845
|
] }),
|
|
1772
1846
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
|
|
1773
1847
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
|
|
1774
|
-
"
|
|
1848
|
+
"สร้างอัตราใหม่"
|
|
1775
1849
|
] })
|
|
1776
1850
|
] }),
|
|
1777
1851
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
1778
1852
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
1779
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "
|
|
1780
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "
|
|
1781
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "
|
|
1782
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "
|
|
1783
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "
|
|
1853
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ประเภทขนส่ง" }),
|
|
1854
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "บริการ" }),
|
|
1855
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "น้ำหนัก ≤ (kg)" }),
|
|
1856
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
1857
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ETA" }),
|
|
1784
1858
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
|
|
1785
1859
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
1786
1860
|
] }) }),
|
|
1787
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) :
|
|
1788
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children:
|
|
1789
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children:
|
|
1790
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: materialCost.unit }),
|
|
1861
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredRates.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลอัตราค่าขนส่ง" }) }) : filteredRates.map((rate) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
1862
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: getCarrierLabel(rate.carrier_type) }),
|
|
1863
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: getServiceLabel(rate.service_code) }),
|
|
1791
1864
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1865
|
+
"≤ ",
|
|
1866
|
+
rate.max_weight_kg,
|
|
1867
|
+
" kg"
|
|
1795
1868
|
] }),
|
|
1796
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {
|
|
1797
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1798
|
-
|
|
1799
|
-
{
|
|
1800
|
-
color: materialCost.active ? "green" : "grey",
|
|
1801
|
-
className: "cursor-pointer",
|
|
1802
|
-
onClick: () => handleToggleActive(materialCost),
|
|
1803
|
-
children: materialCost.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
1804
|
-
}
|
|
1805
|
-
) }),
|
|
1869
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: rate.price.toFixed(2) }),
|
|
1870
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "blue", children: formatETA(rate) }) }),
|
|
1871
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
1806
1872
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
1807
1873
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1808
1874
|
ui.Button,
|
|
1809
1875
|
{
|
|
1810
1876
|
variant: "transparent",
|
|
1811
1877
|
size: "small",
|
|
1812
|
-
onClick: () => handleEdit(
|
|
1878
|
+
onClick: () => handleEdit(rate),
|
|
1813
1879
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
|
|
1814
1880
|
}
|
|
1815
1881
|
),
|
|
@@ -1818,101 +1884,55 @@ const MaterialCostsTable = () => {
|
|
|
1818
1884
|
{
|
|
1819
1885
|
variant: "transparent",
|
|
1820
1886
|
size: "small",
|
|
1821
|
-
onClick: () =>
|
|
1887
|
+
onClick: () => setDeleteRateId(rate.id),
|
|
1822
1888
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
|
|
1823
1889
|
}
|
|
1824
1890
|
)
|
|
1825
1891
|
] }) })
|
|
1826
|
-
] },
|
|
1892
|
+
] }, rate.id)) })
|
|
1827
1893
|
] })
|
|
1828
1894
|
] }),
|
|
1829
|
-
isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1830
|
-
MaterialCostModal,
|
|
1831
|
-
{
|
|
1832
|
-
materialCost: editingMaterialCost,
|
|
1833
|
-
onClose: handleModalClose
|
|
1834
|
-
}
|
|
1835
|
-
),
|
|
1895
|
+
isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ShippingRateModal, { rate: editingRate, onClose: handleModalClose }),
|
|
1836
1896
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1837
1897
|
ui.Prompt,
|
|
1838
1898
|
{
|
|
1839
1899
|
variant: "confirmation",
|
|
1840
|
-
open: !!
|
|
1841
|
-
onOpenChange: () =>
|
|
1842
|
-
setDeleteMaterialCostId(null);
|
|
1843
|
-
setDeleteMaterialCostName("");
|
|
1844
|
-
},
|
|
1900
|
+
open: !!deleteRateId,
|
|
1901
|
+
onOpenChange: () => setDeleteRateId(null),
|
|
1845
1902
|
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
|
|
1846
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
|
|
1847
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "
|
|
1848
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
] })
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
}
|
|
1863
|
-
const MaterialCostsPage = () => {
|
|
1864
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1865
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "ต้นทุนวัสดุ" }) }),
|
|
1866
|
-
/* @__PURE__ */ jsxRuntime.jsx(MaterialCostsTable, {})
|
|
1867
|
-
] }) });
|
|
1868
|
-
};
|
|
1869
|
-
const config$1 = adminSdk.defineRouteConfig({
|
|
1870
|
-
icon: icons.CurrencyDollarSolid,
|
|
1871
|
-
label: "ต้นทุนวัสดุ"
|
|
1872
|
-
});
|
|
1873
|
-
const CARRIER_TYPES$1 = [
|
|
1874
|
-
{ value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
|
|
1875
|
-
{ value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
|
|
1876
|
-
{ value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
|
|
1877
|
-
];
|
|
1878
|
-
const SERVICE_CODES$1 = [
|
|
1879
|
-
{ value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม.", usesHours: true },
|
|
1880
|
-
{ value: "SAME_DAY", label: "ส่งด่วนภายในวัน", usesHours: true },
|
|
1881
|
-
{ value: "STANDARD_3_5D", label: "EMS 3-5 วัน", usesHours: false },
|
|
1882
|
-
{ value: "EXPRESS_1_2D", label: "Express 1-2 วัน", usesHours: false }
|
|
1883
|
-
];
|
|
1884
|
-
const ShippingRateModal = ({ rate, onClose }) => {
|
|
1903
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
|
|
1904
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบอัตราค่าขนส่ง" }),
|
|
1905
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: "คุณต้องการลบอัตราค่าขนส่งนี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
|
|
1906
|
+
] }),
|
|
1907
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
|
|
1908
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "ยกเลิก" }),
|
|
1909
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Action, { onClick: handleDelete, children: "ลบ" })
|
|
1910
|
+
] })
|
|
1911
|
+
] })
|
|
1912
|
+
}
|
|
1913
|
+
)
|
|
1914
|
+
] });
|
|
1915
|
+
};
|
|
1916
|
+
const CompanyTruckBaseRateModal = ({
|
|
1917
|
+
rate,
|
|
1918
|
+
onClose
|
|
1919
|
+
}) => {
|
|
1885
1920
|
const [formData, setFormData] = react.useState({
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
max_weight_kg: "",
|
|
1921
|
+
min_distance_km: "0",
|
|
1922
|
+
max_distance_km: "",
|
|
1889
1923
|
price: "",
|
|
1890
|
-
currency: "THB",
|
|
1891
1924
|
priority: "0",
|
|
1892
|
-
eta_hours_min: "",
|
|
1893
|
-
eta_hours_max: "",
|
|
1894
|
-
eta_days_min: "",
|
|
1895
|
-
eta_days_max: "",
|
|
1896
1925
|
active: true
|
|
1897
1926
|
});
|
|
1898
1927
|
const [isSaving, setIsSaving] = react.useState(false);
|
|
1899
|
-
const selectedService = SERVICE_CODES$1.find((s) => s.value === formData.service_code);
|
|
1900
|
-
const usesHours = (selectedService == null ? void 0 : selectedService.usesHours) ?? false;
|
|
1901
|
-
const isMessenger = formData.carrier_type === "COMPANY_FLEET" && formData.service_code === "MESSENGER_3H";
|
|
1902
1928
|
react.useEffect(() => {
|
|
1903
|
-
var _a, _b
|
|
1929
|
+
var _a, _b;
|
|
1904
1930
|
if (rate) {
|
|
1905
1931
|
setFormData({
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
max_weight_kg: rate.max_weight_kg.toString(),
|
|
1932
|
+
min_distance_km: ((_a = rate.min_distance_km) == null ? void 0 : _a.toString()) ?? "0",
|
|
1933
|
+
max_distance_km: ((_b = rate.max_distance_km) == null ? void 0 : _b.toString()) || "",
|
|
1909
1934
|
price: rate.price.toString(),
|
|
1910
|
-
currency: rate.currency,
|
|
1911
1935
|
priority: (rate.priority ?? 0).toString(),
|
|
1912
|
-
eta_hours_min: ((_a = rate.eta_hours_min) == null ? void 0 : _a.toString()) || "",
|
|
1913
|
-
eta_hours_max: ((_b = rate.eta_hours_max) == null ? void 0 : _b.toString()) || "",
|
|
1914
|
-
eta_days_min: ((_c = rate.eta_days_min) == null ? void 0 : _c.toString()) || "",
|
|
1915
|
-
eta_days_max: ((_d = rate.eta_days_max) == null ? void 0 : _d.toString()) || "",
|
|
1916
1936
|
active: rate.active
|
|
1917
1937
|
});
|
|
1918
1938
|
}
|
|
@@ -1920,11 +1940,16 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
1920
1940
|
const handleSubmit = async (e) => {
|
|
1921
1941
|
e.preventDefault();
|
|
1922
1942
|
const errors = [];
|
|
1923
|
-
const
|
|
1943
|
+
const minDistance = parseFloat(formData.min_distance_km || "0");
|
|
1944
|
+
const hasMaxDistance = formData.max_distance_km !== "";
|
|
1945
|
+
const maxDistance = hasMaxDistance ? parseFloat(formData.max_distance_km) : void 0;
|
|
1924
1946
|
const price = parseFloat(formData.price);
|
|
1925
|
-
const priority = parseInt(formData.priority);
|
|
1926
|
-
if (isNaN(
|
|
1927
|
-
errors.push("
|
|
1947
|
+
const priority = parseInt(formData.priority || "0", 10);
|
|
1948
|
+
if (isNaN(minDistance) || minDistance < 0) {
|
|
1949
|
+
errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
|
|
1950
|
+
}
|
|
1951
|
+
if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
|
|
1952
|
+
errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
|
|
1928
1953
|
}
|
|
1929
1954
|
if (isNaN(price) || price < 0) {
|
|
1930
1955
|
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
@@ -1932,27 +1957,6 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
1932
1957
|
if (isNaN(priority)) {
|
|
1933
1958
|
errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
|
|
1934
1959
|
}
|
|
1935
|
-
let etaHoursMin;
|
|
1936
|
-
let etaHoursMax;
|
|
1937
|
-
let etaDaysMin;
|
|
1938
|
-
let etaDaysMax;
|
|
1939
|
-
if (usesHours) {
|
|
1940
|
-
etaHoursMin = formData.eta_hours_min ? parseFloat(formData.eta_hours_min) : void 0;
|
|
1941
|
-
etaHoursMax = formData.eta_hours_max ? parseFloat(formData.eta_hours_max) : void 0;
|
|
1942
|
-
if (etaHoursMin === void 0 || etaHoursMax === void 0) {
|
|
1943
|
-
errors.push("กรุณากรอก ETA เป็นชั่วโมงให้ครบ");
|
|
1944
|
-
} else if (etaHoursMin > etaHoursMax) {
|
|
1945
|
-
errors.push("ETA ชั่วโมงต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
|
|
1946
|
-
}
|
|
1947
|
-
} else {
|
|
1948
|
-
etaDaysMin = formData.eta_days_min ? parseFloat(formData.eta_days_min) : void 0;
|
|
1949
|
-
etaDaysMax = formData.eta_days_max ? parseFloat(formData.eta_days_max) : void 0;
|
|
1950
|
-
if (etaDaysMin === void 0 || etaDaysMax === void 0) {
|
|
1951
|
-
errors.push("กรุณากรอก ETA เป็นวันให้ครบ");
|
|
1952
|
-
} else if (etaDaysMin > etaDaysMax) {
|
|
1953
|
-
errors.push("ETA วันต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
1960
|
if (errors.length > 0) {
|
|
1957
1961
|
ui.toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
1958
1962
|
description: errors.join(", ")
|
|
@@ -1962,22 +1966,17 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
1962
1966
|
setIsSaving(true);
|
|
1963
1967
|
try {
|
|
1964
1968
|
const payload = {
|
|
1965
|
-
carrier_type:
|
|
1966
|
-
|
|
1967
|
-
|
|
1969
|
+
carrier_type: "COMPANY_TRUCK",
|
|
1970
|
+
carrier: "COMPANY_TRUCK",
|
|
1971
|
+
service_code: "SAME_DAY",
|
|
1972
|
+
min_distance_km: minDistance,
|
|
1973
|
+
max_distance_km: maxDistance === void 0 || isNaN(maxDistance) ? null : maxDistance,
|
|
1968
1974
|
price,
|
|
1969
|
-
currency:
|
|
1975
|
+
currency: "THB",
|
|
1970
1976
|
priority,
|
|
1971
1977
|
active: formData.active
|
|
1972
1978
|
};
|
|
1973
|
-
|
|
1974
|
-
payload.eta_hours_min = etaHoursMin;
|
|
1975
|
-
payload.eta_hours_max = etaHoursMax;
|
|
1976
|
-
} else {
|
|
1977
|
-
payload.eta_days_min = etaDaysMin;
|
|
1978
|
-
payload.eta_days_max = etaDaysMax;
|
|
1979
|
-
}
|
|
1980
|
-
const url = rate ? `/admin/shipping-rates/${rate.id}` : "/admin/shipping-rates";
|
|
1979
|
+
const url = rate ? `/admin/base-shipping-prices/${rate.id}` : "/admin/base-shipping-prices";
|
|
1981
1980
|
const method = rate ? "PUT" : "POST";
|
|
1982
1981
|
const response = await fetch(url, {
|
|
1983
1982
|
method,
|
|
@@ -1985,15 +1984,14 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
1985
1984
|
credentials: "include",
|
|
1986
1985
|
body: JSON.stringify(payload)
|
|
1987
1986
|
});
|
|
1988
|
-
if (response.ok) {
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
});
|
|
1992
|
-
onClose();
|
|
1993
|
-
} else {
|
|
1994
|
-
const error = await response.json();
|
|
1995
|
-
throw new Error(error.message || "Failed to save");
|
|
1987
|
+
if (!response.ok) {
|
|
1988
|
+
const error = await response.json().catch(() => ({}));
|
|
1989
|
+
throw new Error(error.message || "ไม่สามารถบันทึกข้อมูลได้");
|
|
1996
1990
|
}
|
|
1991
|
+
ui.toast.success("สำเร็จ", {
|
|
1992
|
+
description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
|
|
1993
|
+
});
|
|
1994
|
+
onClose();
|
|
1997
1995
|
} catch (error) {
|
|
1998
1996
|
ui.toast.error("ข้อผิดพลาด", {
|
|
1999
1997
|
description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
|
|
@@ -2003,85 +2001,56 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
2003
2001
|
}
|
|
2004
2002
|
};
|
|
2005
2003
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
2006
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: rate ? "
|
|
2004
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: rate ? "แก้ไขฐานราคาตามระยะทาง (รถบริษัท)" : "สร้างฐานราคาตามระยะทาง (รถบริษัท)" }) }),
|
|
2007
2005
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
2008
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2009
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "carrier", children: "ประเภทขนส่ง *" }),
|
|
2010
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2011
|
-
ui.Select,
|
|
2012
|
-
{
|
|
2013
|
-
value: formData.carrier_type,
|
|
2014
|
-
onValueChange: (value) => setFormData({ ...formData, carrier_type: value }),
|
|
2015
|
-
children: [
|
|
2016
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกประเภทขนส่ง" }) }),
|
|
2017
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CARRIER_TYPES$1.map((carrier) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: carrier.value, children: carrier.label }, carrier.value)) })
|
|
2018
|
-
]
|
|
2019
|
-
}
|
|
2020
|
-
)
|
|
2021
|
-
] }),
|
|
2022
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2023
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "service", children: "บริการจัดส่ง *" }),
|
|
2024
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2025
|
-
ui.Select,
|
|
2026
|
-
{
|
|
2027
|
-
value: formData.service_code,
|
|
2028
|
-
onValueChange: (value) => setFormData({
|
|
2029
|
-
...formData,
|
|
2030
|
-
service_code: value,
|
|
2031
|
-
eta_hours_min: "",
|
|
2032
|
-
eta_hours_max: "",
|
|
2033
|
-
eta_days_min: "",
|
|
2034
|
-
eta_days_max: ""
|
|
2035
|
-
}),
|
|
2036
|
-
children: [
|
|
2037
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกบริการ" }) }),
|
|
2038
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: SERVICE_CODES$1.map((service) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: service.value, children: service.label }, service.value)) })
|
|
2039
|
-
]
|
|
2040
|
-
}
|
|
2041
|
-
)
|
|
2042
|
-
] }),
|
|
2043
|
-
isMessenger && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3 text-sm space-y-1", children: [
|
|
2044
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-ui-fg-base", children: "ฐานราคาตามระยะทาง (Messenger)" }),
|
|
2045
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-fg-subtle", children: "ตั้งช่วงราคาตามระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม. สำหรับบริการส่งด่วน 3 ชม." }),
|
|
2046
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { asChild: true, size: "small", variant: "secondary", children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: "/base-pricing", target: "_blank", rel: "noreferrer", children: [
|
|
2047
|
-
"จัดการฐานราคา",
|
|
2048
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "ml-1 h-4 w-4" })
|
|
2049
|
-
] }) }) })
|
|
2050
|
-
] }),
|
|
2051
2006
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
2052
2007
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2053
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
2008
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "min_distance_km", children: "ระยะทางเริ่มต้น (กม.) *" }),
|
|
2054
2009
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2055
2010
|
ui.Input,
|
|
2056
2011
|
{
|
|
2057
|
-
id: "
|
|
2012
|
+
id: "min_distance_km",
|
|
2058
2013
|
type: "number",
|
|
2059
|
-
step: "0.1",
|
|
2060
2014
|
min: "0",
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2015
|
+
step: "0.1",
|
|
2016
|
+
value: formData.min_distance_km,
|
|
2017
|
+
onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
|
|
2064
2018
|
required: true
|
|
2065
2019
|
}
|
|
2066
2020
|
)
|
|
2067
2021
|
] }),
|
|
2068
2022
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2069
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
2023
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "max_distance_km", children: "ระยะทางสูงสุด (กม.)" }),
|
|
2070
2024
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2071
2025
|
ui.Input,
|
|
2072
2026
|
{
|
|
2073
|
-
id: "
|
|
2027
|
+
id: "max_distance_km",
|
|
2074
2028
|
type: "number",
|
|
2075
|
-
step: "0.01",
|
|
2076
2029
|
min: "0",
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2030
|
+
step: "0.1",
|
|
2031
|
+
placeholder: "ปล่อยว่างหากไม่มีที่สิ้นสุด",
|
|
2032
|
+
value: formData.max_distance_km,
|
|
2033
|
+
onChange: (e) => setFormData({ ...formData, max_distance_km: e.target.value })
|
|
2081
2034
|
}
|
|
2082
|
-
)
|
|
2035
|
+
),
|
|
2036
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
|
|
2083
2037
|
] })
|
|
2084
2038
|
] }),
|
|
2039
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2040
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
|
|
2041
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2042
|
+
ui.Input,
|
|
2043
|
+
{
|
|
2044
|
+
id: "price",
|
|
2045
|
+
type: "number",
|
|
2046
|
+
min: "0",
|
|
2047
|
+
step: "0.01",
|
|
2048
|
+
value: formData.price,
|
|
2049
|
+
onChange: (e) => setFormData({ ...formData, price: e.target.value }),
|
|
2050
|
+
required: true
|
|
2051
|
+
}
|
|
2052
|
+
)
|
|
2053
|
+
] }),
|
|
2085
2054
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2086
2055
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
|
|
2087
2056
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2094,56 +2063,7 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
2094
2063
|
placeholder: "0"
|
|
2095
2064
|
}
|
|
2096
2065
|
),
|
|
2097
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "
|
|
2098
|
-
] }),
|
|
2099
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2100
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { children: [
|
|
2101
|
-
usesHours ? "ETA (ชั่วโมง)" : "ETA (วัน)",
|
|
2102
|
-
" *"
|
|
2103
|
-
] }),
|
|
2104
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-x-4 mt-2", children: usesHours ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2105
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2106
|
-
ui.Input,
|
|
2107
|
-
{
|
|
2108
|
-
type: "number",
|
|
2109
|
-
placeholder: "ต่ำสุด",
|
|
2110
|
-
value: formData.eta_hours_min,
|
|
2111
|
-
onChange: (e) => setFormData({ ...formData, eta_hours_min: e.target.value }),
|
|
2112
|
-
required: true
|
|
2113
|
-
}
|
|
2114
|
-
),
|
|
2115
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2116
|
-
ui.Input,
|
|
2117
|
-
{
|
|
2118
|
-
type: "number",
|
|
2119
|
-
placeholder: "สูงสุด",
|
|
2120
|
-
value: formData.eta_hours_max,
|
|
2121
|
-
onChange: (e) => setFormData({ ...formData, eta_hours_max: e.target.value }),
|
|
2122
|
-
required: true
|
|
2123
|
-
}
|
|
2124
|
-
)
|
|
2125
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2126
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2127
|
-
ui.Input,
|
|
2128
|
-
{
|
|
2129
|
-
type: "number",
|
|
2130
|
-
placeholder: "ต่ำสุด",
|
|
2131
|
-
value: formData.eta_days_min,
|
|
2132
|
-
onChange: (e) => setFormData({ ...formData, eta_days_min: e.target.value }),
|
|
2133
|
-
required: true
|
|
2134
|
-
}
|
|
2135
|
-
),
|
|
2136
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2137
|
-
ui.Input,
|
|
2138
|
-
{
|
|
2139
|
-
type: "number",
|
|
2140
|
-
placeholder: "สูงสุด",
|
|
2141
|
-
value: formData.eta_days_max,
|
|
2142
|
-
onChange: (e) => setFormData({ ...formData, eta_days_max: e.target.value }),
|
|
2143
|
-
required: true
|
|
2144
|
-
}
|
|
2145
|
-
)
|
|
2146
|
-
] }) })
|
|
2066
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
|
|
2147
2067
|
] }),
|
|
2148
2068
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
2149
2069
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2157,195 +2077,163 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
2157
2077
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
|
|
2158
2078
|
] }),
|
|
2159
2079
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
2160
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2080
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2081
|
+
ui.Button,
|
|
2082
|
+
{
|
|
2083
|
+
type: "button",
|
|
2084
|
+
variant: "secondary",
|
|
2085
|
+
onClick: onClose,
|
|
2086
|
+
disabled: isSaving,
|
|
2087
|
+
children: "ยกเลิก"
|
|
2088
|
+
}
|
|
2089
|
+
),
|
|
2161
2090
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
|
|
2162
2091
|
] })
|
|
2163
2092
|
] }) })
|
|
2164
2093
|
] }) });
|
|
2165
2094
|
};
|
|
2166
|
-
const
|
|
2167
|
-
{ value: "ALL", label: "ทั้งหมด" },
|
|
2168
|
-
{ value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
|
|
2169
|
-
{ value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
|
|
2170
|
-
{ value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
|
|
2171
|
-
];
|
|
2172
|
-
const SERVICE_CODES = [
|
|
2173
|
-
{ value: "ALL", label: "ทั้งหมด" },
|
|
2174
|
-
{ value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม." },
|
|
2175
|
-
{ value: "SAME_DAY", label: "ส่งด่วนภายในวัน" },
|
|
2176
|
-
{ value: "STANDARD_3_5D", label: "EMS 3-5 วัน" },
|
|
2177
|
-
{ value: "EXPRESS_1_2D", label: "Express 1-2 วัน" }
|
|
2178
|
-
];
|
|
2179
|
-
const ShippingRatesTable = () => {
|
|
2095
|
+
const CompanyTruckBasePricingTable = () => {
|
|
2180
2096
|
const [rates, setRates] = react.useState([]);
|
|
2181
|
-
const [
|
|
2182
|
-
const [carrierFilter, setCarrierFilter] = react.useState("ALL");
|
|
2183
|
-
const [serviceFilter, setServiceFilter] = react.useState("ALL");
|
|
2097
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
2184
2098
|
const [isModalOpen, setIsModalOpen] = react.useState(false);
|
|
2185
2099
|
const [editingRate, setEditingRate] = react.useState(null);
|
|
2186
|
-
const [
|
|
2187
|
-
const [deleteRateId, setDeleteRateId] = react.useState(null);
|
|
2100
|
+
const [deleteId, setDeleteId] = react.useState(null);
|
|
2188
2101
|
react.useEffect(() => {
|
|
2189
2102
|
fetchRates();
|
|
2190
2103
|
}, []);
|
|
2191
|
-
react.
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
}, [
|
|
2104
|
+
const orderedRates = react.useMemo(() => {
|
|
2105
|
+
return [...rates].sort((a, b) => {
|
|
2106
|
+
if (a.min_distance_km !== b.min_distance_km) {
|
|
2107
|
+
return a.min_distance_km - b.min_distance_km;
|
|
2108
|
+
}
|
|
2109
|
+
const aMax = a.max_distance_km ?? Number.POSITIVE_INFINITY;
|
|
2110
|
+
const bMax = b.max_distance_km ?? Number.POSITIVE_INFINITY;
|
|
2111
|
+
return aMax - bMax;
|
|
2112
|
+
});
|
|
2113
|
+
}, [rates]);
|
|
2201
2114
|
const fetchRates = async () => {
|
|
2202
2115
|
setIsLoading(true);
|
|
2203
2116
|
try {
|
|
2204
|
-
const response = await fetch(
|
|
2205
|
-
|
|
2206
|
-
|
|
2117
|
+
const response = await fetch(
|
|
2118
|
+
"/admin/base-shipping-prices?carrier_type=COMPANY_TRUCK&service_code=SAME_DAY",
|
|
2119
|
+
{
|
|
2120
|
+
credentials: "include"
|
|
2121
|
+
}
|
|
2122
|
+
);
|
|
2123
|
+
if (!response.ok) {
|
|
2124
|
+
throw new Error("failed_to_load_base_rates");
|
|
2125
|
+
}
|
|
2207
2126
|
const data = await response.json();
|
|
2208
|
-
setRates(data.
|
|
2127
|
+
setRates(data.base_rates || []);
|
|
2209
2128
|
} catch (error) {
|
|
2210
2129
|
ui.toast.error("ข้อผิดพลาด", {
|
|
2211
|
-
description: "
|
|
2130
|
+
description: "ไม่สามารถโหลดฐานราคาตามระยะทางได้"
|
|
2212
2131
|
});
|
|
2213
2132
|
} finally {
|
|
2214
2133
|
setIsLoading(false);
|
|
2215
2134
|
}
|
|
2216
2135
|
};
|
|
2217
2136
|
const handleDelete = async () => {
|
|
2218
|
-
if (!
|
|
2137
|
+
if (!deleteId) return;
|
|
2219
2138
|
try {
|
|
2220
|
-
const response = await fetch(
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
} else {
|
|
2230
|
-
throw new Error("Failed to delete");
|
|
2139
|
+
const response = await fetch(
|
|
2140
|
+
`/admin/base-shipping-prices/${deleteId}`,
|
|
2141
|
+
{
|
|
2142
|
+
method: "DELETE",
|
|
2143
|
+
credentials: "include"
|
|
2144
|
+
}
|
|
2145
|
+
);
|
|
2146
|
+
if (!response.ok) {
|
|
2147
|
+
throw new Error("failed_to_delete");
|
|
2231
2148
|
}
|
|
2149
|
+
ui.toast.success("สำเร็จ", {
|
|
2150
|
+
description: "ลบฐานราคาสำเร็จ"
|
|
2151
|
+
});
|
|
2152
|
+
fetchRates();
|
|
2232
2153
|
} catch (error) {
|
|
2233
2154
|
ui.toast.error("ข้อผิดพลาด", {
|
|
2234
|
-
description: "
|
|
2155
|
+
description: "ไม่สามารถลบฐานราคาได้"
|
|
2235
2156
|
});
|
|
2236
2157
|
} finally {
|
|
2237
|
-
|
|
2158
|
+
setDeleteId(null);
|
|
2238
2159
|
}
|
|
2239
2160
|
};
|
|
2240
|
-
const handleEdit = (rate) => {
|
|
2241
|
-
setEditingRate(rate);
|
|
2242
|
-
setIsModalOpen(true);
|
|
2243
|
-
};
|
|
2244
|
-
const handleCreateNew = () => {
|
|
2245
|
-
setEditingRate(null);
|
|
2246
|
-
setIsModalOpen(true);
|
|
2247
|
-
};
|
|
2248
2161
|
const handleModalClose = () => {
|
|
2249
2162
|
setIsModalOpen(false);
|
|
2250
2163
|
setEditingRate(null);
|
|
2251
2164
|
fetchRates();
|
|
2252
2165
|
};
|
|
2253
|
-
const
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
var _a;
|
|
2259
|
-
return ((_a = SERVICE_CODES.find((s) => s.value === code)) == null ? void 0 : _a.label) || code;
|
|
2260
|
-
};
|
|
2261
|
-
const formatETA = (rate) => {
|
|
2262
|
-
if (rate.eta_hours_min !== null && rate.eta_hours_min !== void 0 && rate.eta_hours_max !== null && rate.eta_hours_max !== void 0) {
|
|
2263
|
-
return `${rate.eta_hours_min}-${rate.eta_hours_max} ชม.`;
|
|
2264
|
-
}
|
|
2265
|
-
if (rate.eta_days_min !== null && rate.eta_days_min !== void 0 && rate.eta_days_max !== null && rate.eta_days_max !== void 0) {
|
|
2266
|
-
return `${rate.eta_days_min}-${rate.eta_days_max} วัน`;
|
|
2166
|
+
const formatDistanceRange = (rate) => {
|
|
2167
|
+
const min = rate.min_distance_km ?? 0;
|
|
2168
|
+
const max = rate.max_distance_km;
|
|
2169
|
+
if (max === void 0 || max === null) {
|
|
2170
|
+
return `${min}+ กม.`;
|
|
2267
2171
|
}
|
|
2268
|
-
return
|
|
2172
|
+
return `${min}-${max} กม.`;
|
|
2269
2173
|
};
|
|
2270
2174
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2271
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex
|
|
2272
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2273
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2274
|
-
|
|
2275
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "ประเภทขนส่ง" }),
|
|
2276
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: carrierFilter, onValueChange: setCarrierFilter, children: [
|
|
2277
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ทั้งหมด" }) }),
|
|
2278
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CARRIER_TYPES.map((type) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: type.value, children: type.label }, type.value)) })
|
|
2279
|
-
] })
|
|
2280
|
-
] }),
|
|
2281
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
2282
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "บริการจัดส่ง" }),
|
|
2283
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: serviceFilter, onValueChange: setServiceFilter, children: [
|
|
2284
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ทั้งหมด" }) }),
|
|
2285
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: SERVICE_CODES.map((service) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: service.value, children: service.label }, service.value)) })
|
|
2286
|
-
] })
|
|
2287
|
-
] })
|
|
2288
|
-
] }),
|
|
2289
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
|
|
2290
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
|
|
2291
|
-
"สร้างอัตราใหม่"
|
|
2292
|
-
] })
|
|
2175
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
2176
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2177
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: "ฐานราคาตามระยะทาง (รถบริษัท - Same Day)" }),
|
|
2178
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-fg-subtle text-sm", children: "กำหนดราคาเริ่มต้นตามระยะทาง เช่น 0-10, 10-30, 30-50, 50+ กม." })
|
|
2293
2179
|
] }),
|
|
2294
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.
|
|
2295
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2296
|
-
|
|
2297
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "บริการ" }),
|
|
2298
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "น้ำหนัก ≤ (kg)" }),
|
|
2299
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
2300
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ETA" }),
|
|
2301
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
|
|
2302
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
2303
|
-
] }) }),
|
|
2304
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredRates.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลอัตราค่าขนส่ง" }) }) : filteredRates.map((rate) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
2305
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: getCarrierLabel(rate.carrier_type) }),
|
|
2306
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: getServiceLabel(rate.service_code) }),
|
|
2307
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
|
|
2308
|
-
"≤ ",
|
|
2309
|
-
rate.max_weight_kg,
|
|
2310
|
-
" kg"
|
|
2311
|
-
] }),
|
|
2312
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: rate.price.toFixed(2) }),
|
|
2313
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "blue", children: formatETA(rate) }) }),
|
|
2314
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
2315
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
2316
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2317
|
-
ui.Button,
|
|
2318
|
-
{
|
|
2319
|
-
variant: "transparent",
|
|
2320
|
-
size: "small",
|
|
2321
|
-
onClick: () => handleEdit(rate),
|
|
2322
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
|
|
2323
|
-
}
|
|
2324
|
-
),
|
|
2325
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2326
|
-
ui.Button,
|
|
2327
|
-
{
|
|
2328
|
-
variant: "transparent",
|
|
2329
|
-
size: "small",
|
|
2330
|
-
onClick: () => setDeleteRateId(rate.id),
|
|
2331
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
|
|
2332
|
-
}
|
|
2333
|
-
)
|
|
2334
|
-
] }) })
|
|
2335
|
-
] }, rate.id)) })
|
|
2180
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: () => setIsModalOpen(true), children: [
|
|
2181
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
|
|
2182
|
+
"เพิ่มช่วงราคา"
|
|
2336
2183
|
] })
|
|
2337
2184
|
] }),
|
|
2338
|
-
|
|
2185
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { className: "mt-4", children: [
|
|
2186
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
2187
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ช่วงระยะทาง" }),
|
|
2188
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
2189
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ลำดับ" }),
|
|
2190
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
|
|
2191
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
2192
|
+
] }) }),
|
|
2193
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { colSpan: 5, className: "text-center", children: "กำลังโหลด..." }) }) : orderedRates.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { colSpan: 5, className: "text-center", children: "ยังไม่ได้ตั้งค่าฐานราคาตามระยะทาง (รถบริษัทจะใช้ราคาตามน้ำหนัก)" }) }) : orderedRates.map((rate) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
2194
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatDistanceRange(rate) }),
|
|
2195
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
|
|
2196
|
+
"฿",
|
|
2197
|
+
rate.price.toFixed(2)
|
|
2198
|
+
] }),
|
|
2199
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: rate.priority ?? 0 }),
|
|
2200
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
2201
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
2202
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2203
|
+
ui.Button,
|
|
2204
|
+
{
|
|
2205
|
+
variant: "transparent",
|
|
2206
|
+
size: "small",
|
|
2207
|
+
onClick: () => {
|
|
2208
|
+
setEditingRate(rate);
|
|
2209
|
+
setIsModalOpen(true);
|
|
2210
|
+
},
|
|
2211
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
|
|
2212
|
+
}
|
|
2213
|
+
),
|
|
2214
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2215
|
+
ui.Button,
|
|
2216
|
+
{
|
|
2217
|
+
variant: "transparent",
|
|
2218
|
+
size: "small",
|
|
2219
|
+
onClick: () => setDeleteId(rate.id),
|
|
2220
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
|
|
2221
|
+
}
|
|
2222
|
+
)
|
|
2223
|
+
] }) })
|
|
2224
|
+
] }, rate.id)) })
|
|
2225
|
+
] }),
|
|
2226
|
+
isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(CompanyTruckBaseRateModal, { rate: editingRate, onClose: handleModalClose }),
|
|
2339
2227
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2340
2228
|
ui.Prompt,
|
|
2341
2229
|
{
|
|
2342
2230
|
variant: "confirmation",
|
|
2343
|
-
open: !!
|
|
2344
|
-
onOpenChange: () =>
|
|
2231
|
+
open: !!deleteId,
|
|
2232
|
+
onOpenChange: () => setDeleteId(null),
|
|
2345
2233
|
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
|
|
2346
2234
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
|
|
2347
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "
|
|
2348
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: "
|
|
2235
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบฐานราคา" }),
|
|
2236
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: "คุณต้องการลบช่วงราคานี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
|
|
2349
2237
|
] }),
|
|
2350
2238
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
|
|
2351
2239
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "ยกเลิก" }),
|
|
@@ -2356,49 +2244,71 @@ const ShippingRatesTable = () => {
|
|
|
2356
2244
|
)
|
|
2357
2245
|
] });
|
|
2358
2246
|
};
|
|
2359
|
-
const
|
|
2360
|
-
|
|
2247
|
+
const RatesPage = () => {
|
|
2248
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-8", children: [
|
|
2249
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "อัตราค่าขนส่ง" }) }),
|
|
2250
|
+
/* @__PURE__ */ jsxRuntime.jsx(ShippingRatesTable, {}),
|
|
2251
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t pt-4", children: [
|
|
2252
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-2 text-lg", children: "ฐานราคาตามระยะทาง (Messenger 3H)" }),
|
|
2253
|
+
/* @__PURE__ */ jsxRuntime.jsx(MessengerBasePricingTable, {})
|
|
2254
|
+
] }),
|
|
2255
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t pt-4", children: [
|
|
2256
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-2 text-lg", children: "ฐานราคาตามระยะทาง (รถบริษัท - Same Day)" }),
|
|
2257
|
+
/* @__PURE__ */ jsxRuntime.jsx(CompanyTruckBasePricingTable, {})
|
|
2258
|
+
] })
|
|
2259
|
+
] }) });
|
|
2260
|
+
};
|
|
2261
|
+
const config$1 = adminSdk.defineRouteConfig({
|
|
2262
|
+
icon: icons.HandTruck,
|
|
2263
|
+
label: "อัตราค่าขนส่ง"
|
|
2264
|
+
});
|
|
2265
|
+
const CATEGORIES$1 = [
|
|
2266
|
+
{ value: "PACKAGING", label: "วัสดุหีบห่อ" },
|
|
2267
|
+
{ value: "PROTECTION", label: "วัสดุป้องกัน" },
|
|
2268
|
+
{ value: "FILLING", label: "วัสดุเติมช่องว่าง" },
|
|
2269
|
+
{ value: "TAPE", label: "เทป/กาว" },
|
|
2270
|
+
{ value: "LABEL", label: "ฉลาก/สติกเกอร์" },
|
|
2271
|
+
{ value: "OTHER", label: "อื่นๆ" }
|
|
2272
|
+
];
|
|
2273
|
+
const MaterialCostModal = ({
|
|
2274
|
+
materialCost,
|
|
2361
2275
|
onClose
|
|
2362
2276
|
}) => {
|
|
2363
2277
|
const [formData, setFormData] = react.useState({
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2278
|
+
name: "",
|
|
2279
|
+
unit: "",
|
|
2280
|
+
cost_per_unit: "",
|
|
2281
|
+
currency: "THB",
|
|
2282
|
+
category: "",
|
|
2283
|
+
description: "",
|
|
2368
2284
|
active: true
|
|
2369
2285
|
});
|
|
2370
2286
|
const [isSaving, setIsSaving] = react.useState(false);
|
|
2371
2287
|
react.useEffect(() => {
|
|
2372
|
-
|
|
2373
|
-
if (rate) {
|
|
2288
|
+
if (materialCost) {
|
|
2374
2289
|
setFormData({
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2290
|
+
name: materialCost.name,
|
|
2291
|
+
unit: materialCost.unit,
|
|
2292
|
+
cost_per_unit: materialCost.cost_per_unit.toString(),
|
|
2293
|
+
currency: materialCost.currency,
|
|
2294
|
+
category: materialCost.category || "",
|
|
2295
|
+
description: materialCost.description || "",
|
|
2296
|
+
active: materialCost.active
|
|
2380
2297
|
});
|
|
2381
2298
|
}
|
|
2382
|
-
}, [
|
|
2299
|
+
}, [materialCost]);
|
|
2383
2300
|
const handleSubmit = async (e) => {
|
|
2384
2301
|
e.preventDefault();
|
|
2385
2302
|
const errors = [];
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
const maxDistance = hasMaxDistance ? parseFloat(formData.max_distance_km) : void 0;
|
|
2389
|
-
const price = parseFloat(formData.price);
|
|
2390
|
-
const priority = parseInt(formData.priority || "0", 10);
|
|
2391
|
-
if (isNaN(minDistance) || minDistance < 0) {
|
|
2392
|
-
errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
|
|
2393
|
-
}
|
|
2394
|
-
if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
|
|
2395
|
-
errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
|
|
2303
|
+
if (!formData.name.trim()) {
|
|
2304
|
+
errors.push("กรุณากรอกชื่อวัสดุ");
|
|
2396
2305
|
}
|
|
2397
|
-
if (
|
|
2398
|
-
errors.push("
|
|
2306
|
+
if (!formData.unit.trim()) {
|
|
2307
|
+
errors.push("กรุณากรอกหน่วย");
|
|
2399
2308
|
}
|
|
2400
|
-
|
|
2401
|
-
|
|
2309
|
+
const costPerUnit = parseFloat(formData.cost_per_unit);
|
|
2310
|
+
if (isNaN(costPerUnit) || costPerUnit < 0) {
|
|
2311
|
+
errors.push("ต้นทุนต่อหน่วยต้องเป็นตัวเลขไม่ติดลบ");
|
|
2402
2312
|
}
|
|
2403
2313
|
if (errors.length > 0) {
|
|
2404
2314
|
ui.toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
@@ -2409,32 +2319,31 @@ const CompanyTruckBaseRateModal = ({
|
|
|
2409
2319
|
setIsSaving(true);
|
|
2410
2320
|
try {
|
|
2411
2321
|
const payload = {
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
currency: "THB",
|
|
2419
|
-
priority,
|
|
2322
|
+
name: formData.name.trim(),
|
|
2323
|
+
unit: formData.unit.trim(),
|
|
2324
|
+
cost_per_unit: costPerUnit,
|
|
2325
|
+
currency: formData.currency,
|
|
2326
|
+
category: formData.category || void 0,
|
|
2327
|
+
description: formData.description.trim() || void 0,
|
|
2420
2328
|
active: formData.active
|
|
2421
2329
|
};
|
|
2422
|
-
const url =
|
|
2423
|
-
const method =
|
|
2330
|
+
const url = materialCost ? `/admin/material-costs/${materialCost.id}` : "/admin/material-costs";
|
|
2331
|
+
const method = materialCost ? "PUT" : "POST";
|
|
2424
2332
|
const response = await fetch(url, {
|
|
2425
2333
|
method,
|
|
2426
2334
|
headers: { "Content-Type": "application/json" },
|
|
2427
2335
|
credentials: "include",
|
|
2428
2336
|
body: JSON.stringify(payload)
|
|
2429
2337
|
});
|
|
2430
|
-
if (
|
|
2431
|
-
|
|
2432
|
-
|
|
2338
|
+
if (response.ok) {
|
|
2339
|
+
ui.toast.success("สำเร็จ", {
|
|
2340
|
+
description: materialCost ? "แก้ไขรายการต้นทุนวัสดุแล้ว" : "สร้างรายการต้นทุนวัสดุใหม่แล้ว"
|
|
2341
|
+
});
|
|
2342
|
+
onClose();
|
|
2343
|
+
} else {
|
|
2344
|
+
const error = await response.json();
|
|
2345
|
+
throw new Error(error.message || "Failed to save");
|
|
2433
2346
|
}
|
|
2434
|
-
ui.toast.success("สำเร็จ", {
|
|
2435
|
-
description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
|
|
2436
|
-
});
|
|
2437
|
-
onClose();
|
|
2438
2347
|
} catch (error) {
|
|
2439
2348
|
ui.toast.error("ข้อผิดพลาด", {
|
|
2440
2349
|
description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
|
|
@@ -2444,69 +2353,89 @@ const CompanyTruckBaseRateModal = ({
|
|
|
2444
2353
|
}
|
|
2445
2354
|
};
|
|
2446
2355
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
2447
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children:
|
|
2356
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: materialCost ? "แก้ไขรายการต้นทุนวัสดุ" : "สร้างรายการต้นทุนวัสดุใหม่" }) }),
|
|
2448
2357
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
2358
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2359
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "name", children: "ชื่อวัสดุ *" }),
|
|
2360
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2361
|
+
ui.Input,
|
|
2362
|
+
{
|
|
2363
|
+
id: "name",
|
|
2364
|
+
value: formData.name,
|
|
2365
|
+
onChange: (e) => setFormData({ ...formData, name: e.target.value }),
|
|
2366
|
+
placeholder: "เช่น พลาสติกกันกระแทก, กล่องกระดาษ",
|
|
2367
|
+
required: true
|
|
2368
|
+
}
|
|
2369
|
+
)
|
|
2370
|
+
] }),
|
|
2371
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2372
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "category", children: "หมวดหมู่ (ไม่บังคับ)" }),
|
|
2373
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2374
|
+
ui.Select,
|
|
2375
|
+
{
|
|
2376
|
+
value: formData.category || void 0,
|
|
2377
|
+
onValueChange: (value) => setFormData({ ...formData, category: value }),
|
|
2378
|
+
children: [
|
|
2379
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกหมวดหมู่ (ไม่บังคับ)" }) }),
|
|
2380
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CATEGORIES$1.map((category) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: category.value, children: category.label }, category.value)) })
|
|
2381
|
+
]
|
|
2382
|
+
}
|
|
2383
|
+
)
|
|
2384
|
+
] }),
|
|
2449
2385
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
2450
2386
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2451
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
2387
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "unit", children: "หน่วย *" }),
|
|
2452
2388
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2453
2389
|
ui.Input,
|
|
2454
2390
|
{
|
|
2455
|
-
id: "
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
value: formData.min_distance_km,
|
|
2460
|
-
onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
|
|
2391
|
+
id: "unit",
|
|
2392
|
+
value: formData.unit,
|
|
2393
|
+
onChange: (e) => setFormData({ ...formData, unit: e.target.value }),
|
|
2394
|
+
placeholder: "เช่น ชิ้น, เมตร, กิโลกรัม",
|
|
2461
2395
|
required: true
|
|
2462
2396
|
}
|
|
2463
2397
|
)
|
|
2464
2398
|
] }),
|
|
2465
2399
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2466
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
2400
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "costPerUnit", children: "ต้นทุนต่อหน่วย *" }),
|
|
2467
2401
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2468
2402
|
ui.Input,
|
|
2469
2403
|
{
|
|
2470
|
-
id: "
|
|
2404
|
+
id: "costPerUnit",
|
|
2471
2405
|
type: "number",
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2406
|
+
step: "0.01",
|
|
2407
|
+
value: formData.cost_per_unit,
|
|
2408
|
+
onChange: (e) => setFormData({ ...formData, cost_per_unit: e.target.value }),
|
|
2409
|
+
placeholder: "0.00",
|
|
2410
|
+
required: true
|
|
2477
2411
|
}
|
|
2478
|
-
)
|
|
2479
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
|
|
2412
|
+
)
|
|
2480
2413
|
] })
|
|
2481
2414
|
] }),
|
|
2482
2415
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2483
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
2416
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "currency", children: "สกุลเงิน" }),
|
|
2484
2417
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2485
2418
|
ui.Input,
|
|
2486
2419
|
{
|
|
2487
|
-
id: "
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
value: formData.price,
|
|
2492
|
-
onChange: (e) => setFormData({ ...formData, price: e.target.value }),
|
|
2493
|
-
required: true
|
|
2420
|
+
id: "currency",
|
|
2421
|
+
value: formData.currency,
|
|
2422
|
+
onChange: (e) => setFormData({ ...formData, currency: e.target.value }),
|
|
2423
|
+
placeholder: "THB"
|
|
2494
2424
|
}
|
|
2495
2425
|
)
|
|
2496
2426
|
] }),
|
|
2497
2427
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2498
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "
|
|
2428
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "description", children: "คำอธิบาย" }),
|
|
2499
2429
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2500
|
-
ui.
|
|
2430
|
+
ui.Textarea,
|
|
2501
2431
|
{
|
|
2502
|
-
id: "
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2432
|
+
id: "description",
|
|
2433
|
+
value: formData.description,
|
|
2434
|
+
onChange: (e) => setFormData({ ...formData, description: e.target.value }),
|
|
2435
|
+
placeholder: "คำอธิบายเพิ่มเติมเกี่ยวกับวัสดุนี้...",
|
|
2436
|
+
rows: 3
|
|
2507
2437
|
}
|
|
2508
|
-
)
|
|
2509
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
|
|
2438
|
+
)
|
|
2510
2439
|
] }),
|
|
2511
2440
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
2512
2441
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2535,148 +2464,227 @@ const CompanyTruckBaseRateModal = ({
|
|
|
2535
2464
|
] }) })
|
|
2536
2465
|
] }) });
|
|
2537
2466
|
};
|
|
2538
|
-
const
|
|
2539
|
-
|
|
2540
|
-
|
|
2467
|
+
const CATEGORIES = [
|
|
2468
|
+
{ value: "ALL", label: "ทั้งหมด" },
|
|
2469
|
+
{ value: "PACKAGING", label: "วัสดุหีบห่อ" },
|
|
2470
|
+
{ value: "PROTECTION", label: "วัสดุป้องกัน" },
|
|
2471
|
+
{ value: "FILLING", label: "วัสดุเติมช่องว่าง" },
|
|
2472
|
+
{ value: "TAPE", label: "เทป/กาว" },
|
|
2473
|
+
{ value: "LABEL", label: "ฉลาก/สติกเกอร์" },
|
|
2474
|
+
{ value: "OTHER", label: "อื่นๆ" }
|
|
2475
|
+
];
|
|
2476
|
+
const MaterialCostsTable = () => {
|
|
2477
|
+
const [materialCosts, setMaterialCosts] = react.useState([]);
|
|
2478
|
+
const [filteredMaterialCosts, setFilteredMaterialCosts] = react.useState([]);
|
|
2479
|
+
const [searchTerm, setSearchTerm] = react.useState("");
|
|
2480
|
+
const [categoryFilter, setCategoryFilter] = react.useState("ALL");
|
|
2541
2481
|
const [isModalOpen, setIsModalOpen] = react.useState(false);
|
|
2542
|
-
const [
|
|
2543
|
-
const [
|
|
2482
|
+
const [editingMaterialCost, setEditingMaterialCost] = react.useState(null);
|
|
2483
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
2484
|
+
const [deleteMaterialCostId, setDeleteMaterialCostId] = react.useState(null);
|
|
2485
|
+
const [deleteMaterialCostName, setDeleteMaterialCostName] = react.useState("");
|
|
2544
2486
|
react.useEffect(() => {
|
|
2545
|
-
|
|
2487
|
+
fetchMaterialCosts();
|
|
2546
2488
|
}, []);
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2489
|
+
react.useEffect(() => {
|
|
2490
|
+
let filtered = materialCosts;
|
|
2491
|
+
if (categoryFilter !== "ALL") {
|
|
2492
|
+
filtered = filtered.filter((mc) => mc.category === categoryFilter);
|
|
2493
|
+
}
|
|
2494
|
+
if (searchTerm) {
|
|
2495
|
+
filtered = filtered.filter(
|
|
2496
|
+
(mc) => mc.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
2497
|
+
);
|
|
2498
|
+
}
|
|
2499
|
+
setFilteredMaterialCosts(filtered);
|
|
2500
|
+
}, [searchTerm, categoryFilter, materialCosts]);
|
|
2501
|
+
const fetchMaterialCosts = async () => {
|
|
2558
2502
|
setIsLoading(true);
|
|
2559
2503
|
try {
|
|
2560
|
-
const response = await fetch(
|
|
2561
|
-
"
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2504
|
+
const response = await fetch("/admin/material-costs", {
|
|
2505
|
+
credentials: "include"
|
|
2506
|
+
});
|
|
2507
|
+
const data = await response.json();
|
|
2508
|
+
setMaterialCosts(data.material_costs || []);
|
|
2509
|
+
} catch (error) {
|
|
2510
|
+
ui.toast.error("ข้อผิดพลาด", {
|
|
2511
|
+
description: "ไม่สามารถโหลดข้อมูลต้นทุนวัสดุได้"
|
|
2512
|
+
});
|
|
2513
|
+
} finally {
|
|
2514
|
+
setIsLoading(false);
|
|
2515
|
+
}
|
|
2516
|
+
};
|
|
2517
|
+
const handleToggleActive = async (materialCost) => {
|
|
2518
|
+
try {
|
|
2519
|
+
const response = await fetch(`/admin/material-costs/${materialCost.id}`, {
|
|
2520
|
+
method: "PUT",
|
|
2521
|
+
headers: { "Content-Type": "application/json" },
|
|
2522
|
+
credentials: "include",
|
|
2523
|
+
body: JSON.stringify({ ...materialCost, active: !materialCost.active })
|
|
2524
|
+
});
|
|
2525
|
+
if (response.ok) {
|
|
2526
|
+
ui.toast.success("สำเร็จ", {
|
|
2527
|
+
description: `${materialCost.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}วัสดุ ${materialCost.name} แล้ว`
|
|
2528
|
+
});
|
|
2529
|
+
fetchMaterialCosts();
|
|
2530
|
+
} else {
|
|
2531
|
+
throw new Error("Failed to update");
|
|
2568
2532
|
}
|
|
2569
|
-
const data = await response.json();
|
|
2570
|
-
setRates(data.base_rates || []);
|
|
2571
2533
|
} catch (error) {
|
|
2572
2534
|
ui.toast.error("ข้อผิดพลาด", {
|
|
2573
|
-
description: "
|
|
2535
|
+
description: "ไม่สามารถอัพเดทสถานะได้"
|
|
2574
2536
|
});
|
|
2575
|
-
} finally {
|
|
2576
|
-
setIsLoading(false);
|
|
2577
2537
|
}
|
|
2578
2538
|
};
|
|
2579
2539
|
const handleDelete = async () => {
|
|
2580
|
-
if (!
|
|
2540
|
+
if (!deleteMaterialCostId) return;
|
|
2581
2541
|
try {
|
|
2582
2542
|
const response = await fetch(
|
|
2583
|
-
`/admin/
|
|
2543
|
+
`/admin/material-costs/${deleteMaterialCostId}`,
|
|
2584
2544
|
{
|
|
2585
2545
|
method: "DELETE",
|
|
2586
2546
|
credentials: "include"
|
|
2587
2547
|
}
|
|
2588
2548
|
);
|
|
2589
|
-
if (
|
|
2590
|
-
|
|
2549
|
+
if (response.ok) {
|
|
2550
|
+
ui.toast.success("สำเร็จ", {
|
|
2551
|
+
description: `ลบวัสดุ ${deleteMaterialCostName} แล้ว`
|
|
2552
|
+
});
|
|
2553
|
+
fetchMaterialCosts();
|
|
2554
|
+
} else {
|
|
2555
|
+
throw new Error("Failed to delete");
|
|
2591
2556
|
}
|
|
2592
|
-
ui.toast.success("สำเร็จ", {
|
|
2593
|
-
description: "ลบฐานราคาสำเร็จ"
|
|
2594
|
-
});
|
|
2595
|
-
fetchRates();
|
|
2596
2557
|
} catch (error) {
|
|
2597
2558
|
ui.toast.error("ข้อผิดพลาด", {
|
|
2598
|
-
description: "
|
|
2559
|
+
description: "ไม่สามารถลบวัสดุได้"
|
|
2599
2560
|
});
|
|
2600
2561
|
} finally {
|
|
2601
|
-
|
|
2562
|
+
setDeleteMaterialCostId(null);
|
|
2563
|
+
setDeleteMaterialCostName("");
|
|
2602
2564
|
}
|
|
2603
2565
|
};
|
|
2566
|
+
const handleEdit = (materialCost) => {
|
|
2567
|
+
setEditingMaterialCost(materialCost);
|
|
2568
|
+
setIsModalOpen(true);
|
|
2569
|
+
};
|
|
2570
|
+
const handleCreateNew = () => {
|
|
2571
|
+
setEditingMaterialCost(null);
|
|
2572
|
+
setIsModalOpen(true);
|
|
2573
|
+
};
|
|
2604
2574
|
const handleModalClose = () => {
|
|
2605
2575
|
setIsModalOpen(false);
|
|
2606
|
-
|
|
2607
|
-
|
|
2576
|
+
setEditingMaterialCost(null);
|
|
2577
|
+
fetchMaterialCosts();
|
|
2608
2578
|
};
|
|
2609
|
-
const
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
return
|
|
2579
|
+
const openDeletePrompt = (id, name) => {
|
|
2580
|
+
setDeleteMaterialCostId(id);
|
|
2581
|
+
setDeleteMaterialCostName(name);
|
|
2582
|
+
};
|
|
2583
|
+
const getCategoryLabel = (category) => {
|
|
2584
|
+
var _a;
|
|
2585
|
+
if (!category) return "-";
|
|
2586
|
+
return ((_a = CATEGORIES.find((c) => c.value === category)) == null ? void 0 : _a.label) || category;
|
|
2616
2587
|
};
|
|
2617
2588
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2618
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex
|
|
2619
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2620
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2621
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-fg-subtle text-sm", children: "กำหนดราคาเริ่มต้นตามระยะทาง เช่น 0-10, 10-30, 30-50, 50+ กม." })
|
|
2622
|
-
] }),
|
|
2623
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: () => setIsModalOpen(true), children: [
|
|
2624
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
|
|
2625
|
-
"เพิ่มช่วงราคา"
|
|
2626
|
-
] })
|
|
2627
|
-
] }),
|
|
2628
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { className: "mt-4", children: [
|
|
2629
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
2630
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ช่วงระยะทาง" }),
|
|
2631
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
2632
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ลำดับ" }),
|
|
2633
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
|
|
2634
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
2635
|
-
] }) }),
|
|
2636
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { colSpan: 5, className: "text-center", children: "กำลังโหลด..." }) }) : orderedRates.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { colSpan: 5, className: "text-center", children: "ยังไม่ได้ตั้งค่าฐานราคาตามระยะทาง (รถบริษัทจะใช้ราคาตามน้ำหนัก)" }) }) : orderedRates.map((rate) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
2637
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatDistanceRange(rate) }),
|
|
2638
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
|
|
2639
|
-
"฿",
|
|
2640
|
-
rate.price.toFixed(2)
|
|
2641
|
-
] }),
|
|
2642
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: rate.priority ?? 0 }),
|
|
2643
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
2644
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
2589
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
2590
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
2591
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4 flex-1", children: [
|
|
2645
2592
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2646
|
-
ui.
|
|
2593
|
+
ui.Input,
|
|
2647
2594
|
{
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
},
|
|
2654
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
|
|
2595
|
+
type: "search",
|
|
2596
|
+
placeholder: "ค้นหาชื่อวัสดุ...",
|
|
2597
|
+
value: searchTerm,
|
|
2598
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
2599
|
+
className: "max-w-md"
|
|
2655
2600
|
}
|
|
2656
2601
|
),
|
|
2657
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2658
|
-
ui.
|
|
2602
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: categoryFilter, onValueChange: setCategoryFilter, children: [
|
|
2603
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "หมวดหมู่" }) }),
|
|
2604
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CATEGORIES.map((category) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: category.value, children: category.label }, category.value)) })
|
|
2605
|
+
] })
|
|
2606
|
+
] }),
|
|
2607
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
|
|
2608
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
|
|
2609
|
+
"สร้างรายการใหม่"
|
|
2610
|
+
] })
|
|
2611
|
+
] }),
|
|
2612
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
2613
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
2614
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ชื่อ" }),
|
|
2615
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "หมวดหมู่" }),
|
|
2616
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "หน่วย" }),
|
|
2617
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ต้นทุนต่อหน่วย" }),
|
|
2618
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "คำอธิบาย" }),
|
|
2619
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
|
|
2620
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
2621
|
+
] }) }),
|
|
2622
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredMaterialCosts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลต้นทุนวัสดุ" }) }) : filteredMaterialCosts.map((materialCost) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
2623
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: materialCost.name }),
|
|
2624
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "blue", children: getCategoryLabel(materialCost.category) }) }),
|
|
2625
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: materialCost.unit }),
|
|
2626
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
|
|
2627
|
+
materialCost.cost_per_unit.toFixed(2),
|
|
2628
|
+
" ",
|
|
2629
|
+
materialCost.currency
|
|
2630
|
+
] }),
|
|
2631
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "max-w-xs truncate", children: materialCost.description || "-" }),
|
|
2632
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2633
|
+
ui.Badge,
|
|
2659
2634
|
{
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
onClick: () =>
|
|
2663
|
-
children:
|
|
2635
|
+
color: materialCost.active ? "green" : "grey",
|
|
2636
|
+
className: "cursor-pointer",
|
|
2637
|
+
onClick: () => handleToggleActive(materialCost),
|
|
2638
|
+
children: materialCost.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
2664
2639
|
}
|
|
2665
|
-
)
|
|
2666
|
-
|
|
2667
|
-
|
|
2640
|
+
) }),
|
|
2641
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
2642
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2643
|
+
ui.Button,
|
|
2644
|
+
{
|
|
2645
|
+
variant: "transparent",
|
|
2646
|
+
size: "small",
|
|
2647
|
+
onClick: () => handleEdit(materialCost),
|
|
2648
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
|
|
2649
|
+
}
|
|
2650
|
+
),
|
|
2651
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2652
|
+
ui.Button,
|
|
2653
|
+
{
|
|
2654
|
+
variant: "transparent",
|
|
2655
|
+
size: "small",
|
|
2656
|
+
onClick: () => openDeletePrompt(materialCost.id, materialCost.name),
|
|
2657
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
|
|
2658
|
+
}
|
|
2659
|
+
)
|
|
2660
|
+
] }) })
|
|
2661
|
+
] }, materialCost.id)) })
|
|
2662
|
+
] })
|
|
2668
2663
|
] }),
|
|
2669
|
-
isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2664
|
+
isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2665
|
+
MaterialCostModal,
|
|
2666
|
+
{
|
|
2667
|
+
materialCost: editingMaterialCost,
|
|
2668
|
+
onClose: handleModalClose
|
|
2669
|
+
}
|
|
2670
|
+
),
|
|
2670
2671
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2671
2672
|
ui.Prompt,
|
|
2672
2673
|
{
|
|
2673
2674
|
variant: "confirmation",
|
|
2674
|
-
open: !!
|
|
2675
|
-
onOpenChange: () =>
|
|
2675
|
+
open: !!deleteMaterialCostId,
|
|
2676
|
+
onOpenChange: () => {
|
|
2677
|
+
setDeleteMaterialCostId(null);
|
|
2678
|
+
setDeleteMaterialCostName("");
|
|
2679
|
+
},
|
|
2676
2680
|
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
|
|
2677
2681
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
|
|
2678
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "
|
|
2679
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2682
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบรายการต้นทุนวัสดุ" }),
|
|
2683
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
|
|
2684
|
+
'คุณต้องการลบวัสดุ "',
|
|
2685
|
+
deleteMaterialCostName,
|
|
2686
|
+
'" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
|
|
2687
|
+
] })
|
|
2680
2688
|
] }),
|
|
2681
2689
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
|
|
2682
2690
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "ยกเลิก" }),
|
|
@@ -2687,23 +2695,15 @@ const CompanyTruckBasePricingTable = () => {
|
|
|
2687
2695
|
)
|
|
2688
2696
|
] });
|
|
2689
2697
|
};
|
|
2690
|
-
const
|
|
2691
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-
|
|
2692
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "
|
|
2693
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2694
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t pt-4", children: [
|
|
2695
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-2 text-lg", children: "ฐานราคาตามระยะทาง (Messenger 3H)" }),
|
|
2696
|
-
/* @__PURE__ */ jsxRuntime.jsx(MessengerBasePricingTable, {})
|
|
2697
|
-
] }),
|
|
2698
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t pt-4", children: [
|
|
2699
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-2 text-lg", children: "ฐานราคาตามระยะทาง (รถบริษัท - Same Day)" }),
|
|
2700
|
-
/* @__PURE__ */ jsxRuntime.jsx(CompanyTruckBasePricingTable, {})
|
|
2701
|
-
] })
|
|
2698
|
+
const MaterialCostsPage = () => {
|
|
2699
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
2700
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "ต้นทุนวัสดุ" }) }),
|
|
2701
|
+
/* @__PURE__ */ jsxRuntime.jsx(MaterialCostsTable, {})
|
|
2702
2702
|
] }) });
|
|
2703
2703
|
};
|
|
2704
2704
|
const config = adminSdk.defineRouteConfig({
|
|
2705
|
-
icon: icons.
|
|
2706
|
-
label: "
|
|
2705
|
+
icon: icons.CurrencyDollarSolid,
|
|
2706
|
+
label: "ต้นทุนวัสดุ"
|
|
2707
2707
|
});
|
|
2708
2708
|
const widgetModule = { widgets: [
|
|
2709
2709
|
{
|
|
@@ -2713,6 +2713,10 @@ const widgetModule = { widgets: [
|
|
|
2713
2713
|
] };
|
|
2714
2714
|
const routeModule = {
|
|
2715
2715
|
routes: [
|
|
2716
|
+
{
|
|
2717
|
+
Component: AreasPage,
|
|
2718
|
+
path: "/areas"
|
|
2719
|
+
},
|
|
2716
2720
|
{
|
|
2717
2721
|
Component: BasePricingPage,
|
|
2718
2722
|
path: "/base-pricing"
|
|
@@ -2722,56 +2726,52 @@ const routeModule = {
|
|
|
2722
2726
|
path: "/boxes"
|
|
2723
2727
|
},
|
|
2724
2728
|
{
|
|
2725
|
-
Component:
|
|
2726
|
-
path: "/
|
|
2729
|
+
Component: RatesPage,
|
|
2730
|
+
path: "/rates"
|
|
2727
2731
|
},
|
|
2728
2732
|
{
|
|
2729
2733
|
Component: MaterialCostsPage,
|
|
2730
2734
|
path: "/material-costs"
|
|
2731
|
-
},
|
|
2732
|
-
{
|
|
2733
|
-
Component: RatesPage,
|
|
2734
|
-
path: "/rates"
|
|
2735
2735
|
}
|
|
2736
2736
|
]
|
|
2737
2737
|
};
|
|
2738
2738
|
const menuItemModule = {
|
|
2739
2739
|
menuItems: [
|
|
2740
2740
|
{
|
|
2741
|
-
label: config$
|
|
2742
|
-
icon: config$
|
|
2741
|
+
label: config$4.label,
|
|
2742
|
+
icon: config$4.icon,
|
|
2743
2743
|
path: "/areas",
|
|
2744
2744
|
nested: void 0,
|
|
2745
2745
|
rank: void 0,
|
|
2746
2746
|
translationNs: void 0
|
|
2747
2747
|
},
|
|
2748
2748
|
{
|
|
2749
|
-
label: config$
|
|
2750
|
-
icon: config$
|
|
2749
|
+
label: config$3.label,
|
|
2750
|
+
icon: config$3.icon,
|
|
2751
2751
|
path: "/base-pricing",
|
|
2752
2752
|
nested: void 0,
|
|
2753
2753
|
rank: void 0,
|
|
2754
2754
|
translationNs: void 0
|
|
2755
2755
|
},
|
|
2756
2756
|
{
|
|
2757
|
-
label: config$
|
|
2758
|
-
icon: config$
|
|
2757
|
+
label: config$2.label,
|
|
2758
|
+
icon: config$2.icon,
|
|
2759
2759
|
path: "/boxes",
|
|
2760
2760
|
nested: void 0,
|
|
2761
2761
|
rank: void 0,
|
|
2762
2762
|
translationNs: void 0
|
|
2763
2763
|
},
|
|
2764
2764
|
{
|
|
2765
|
-
label: config
|
|
2766
|
-
icon: config
|
|
2765
|
+
label: config.label,
|
|
2766
|
+
icon: config.icon,
|
|
2767
2767
|
path: "/material-costs",
|
|
2768
2768
|
nested: void 0,
|
|
2769
2769
|
rank: void 0,
|
|
2770
2770
|
translationNs: void 0
|
|
2771
2771
|
},
|
|
2772
2772
|
{
|
|
2773
|
-
label: config.label,
|
|
2774
|
-
icon: config.icon,
|
|
2773
|
+
label: config$1.label,
|
|
2774
|
+
icon: config$1.icon,
|
|
2775
2775
|
path: "/rates",
|
|
2776
2776
|
nested: void 0,
|
|
2777
2777
|
rank: void 0,
|