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