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