@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, Select, Textarea } from "@medusajs/ui";
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, MapPin, CurrencyDollarSolid, HandTruck } from "@medusajs/icons";
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$4 = defineRouteConfig({
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$3 = defineRouteConfig({
1425
+ const config$2 = defineRouteConfig({
987
1426
  icon: ArchiveBox,
988
1427
  label: "กล่องพัสดุ"
989
1428
  });
990
- const TH_PROVINCES = [
991
- "กรุงเทพมหานคร",
992
- "กระบี่",
993
- "กาญจนบุรี",
994
- "กาฬสินธุ์",
995
- "กำแพงเพชร",
996
- "ขอนแก่น",
997
- "จันทบุรี",
998
- "ฉะเชิงเทรา",
999
- "ชลบุรี",
1000
- "ชัยนาท",
1001
- "ชัยภูมิ",
1002
- "ชุมพร",
1003
- "เชียงราย",
1004
- "เชียงใหม่",
1005
- "ตรัง",
1006
- "ตราด",
1007
- "ตาก",
1008
- "นครนายก",
1009
- "นครปฐม",
1010
- "นครพนม",
1011
- "นครราชสีมา",
1012
- "นครศรีธรรมราช",
1013
- "นครสวรรค์",
1014
- "นนทบุรี",
1015
- "นราธิวาส",
1016
- "น่าน",
1017
- "บึงกาฬ",
1018
- "บุรีรัมย์",
1019
- "ปทุมธานี",
1020
- "ประจวบคีรีขันธ์",
1021
- "ปราจีนบุรี",
1022
- "ปัตตานี",
1023
- "พระนครศรีอยุธยา",
1024
- "พะเยา",
1025
- "พังงา",
1026
- "พัทลุง",
1027
- "พิจิตร",
1028
- "พิษณุโลก",
1029
- "เพชรบุรี",
1030
- "เพชรบูรณ์",
1031
- "แพร่",
1032
- "ภูเก็ต",
1033
- "มหาสารคาม",
1034
- "มุกดาหาร",
1035
- "แม่ฮ่องสอน",
1036
- "ยโสธร",
1037
- "ยะลา",
1038
- "ร้อยเอ็ด",
1039
- "ระนอง",
1040
- "ระยอง",
1041
- "ราชบุรี",
1042
- "ลพบุรี",
1043
- "ลำปาง",
1044
- "ลำพูน",
1045
- "เลย",
1046
- "ศรีสะเกษ",
1047
- "สกลนคร",
1048
- "สงขลา",
1049
- "สตูล",
1050
- "สมุทรปราการ",
1051
- "สมุทรสงคราม",
1052
- "สมุทรสาคร",
1053
- "สระแก้ว",
1054
- "สระบุรี",
1055
- "สิงห์บุรี",
1056
- "สุโขทัย",
1057
- "สุพรรณบุรี",
1058
- "สุราษฎร์ธานี",
1059
- "สุรินทร์",
1060
- "หนองคาย",
1061
- "หนองบัวลำภู",
1062
- "อ่างทอง",
1063
- "อำนาจเจริญ",
1064
- "อุดรธานี",
1065
- "อุตรดิตถ์",
1066
- "อุทัยธานี",
1067
- "อุบลราชธานี"
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 ServiceAreaModal = ({ area, onClose }) => {
1437
+ const MaterialCostModal = ({
1438
+ materialCost,
1439
+ onClose
1440
+ }) => {
1070
1441
  const [formData, setFormData] = useState({
1071
- kind: "PROVINCE",
1072
- value: "",
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 (area) {
1452
+ if (materialCost) {
1078
1453
  setFormData({
1079
- kind: area.kind,
1080
- value: area.value,
1081
- active: area.active
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
- }, [area]);
1463
+ }, [materialCost]);
1085
1464
  const handleSubmit = async (e) => {
1086
1465
  e.preventDefault();
1087
1466
  const errors = [];
1088
- const trimmedValue = formData.value.trim();
1089
- if (!trimmedValue) {
1090
- errors.push("กรุณากรอกค่า");
1467
+ if (!formData.name.trim()) {
1468
+ errors.push("กรุณากรอกชื่อวัสดุ");
1091
1469
  }
1092
- if (formData.kind === "PROVINCE") {
1093
- if (!TH_PROVINCES.includes(trimmedValue)) {
1094
- errors.push("จังหวัดไม่ถูกต้อง กรุณาตรวจสอบการสะกดชื่อจังหวัด");
1095
- }
1096
- } else if (formData.kind === "POSTCODE_PREFIX") {
1097
- if (!/^\d{1,5}$/.test(trimmedValue)) {
1098
- errors.push("รหัสไปรษณีย์ต้องเป็นตัวเลข 1-5 หลัก");
1099
- }
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
- kind: formData.kind,
1111
- value: trimmedValue,
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 = area ? `/admin/service-areas/${area.id}` : "/admin/service-areas";
1115
- const method = area ? "PUT" : "POST";
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: area ? "แก้ไขพื้นที่บริการแล้ว" : "สร้างพื้นที่บริการใหม่แล้ว"
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: area ? "แก้ไขพื้นที่บริการ" : "สร้างพื้นที่บริการใหม่" }) }),
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: "kind", children: "ประเภท *" }),
1144
- /* @__PURE__ */ jsxs(
1145
- Select,
1523
+ /* @__PURE__ */ jsx(Label, { htmlFor: "name", children: "ชื่อวัสดุ *" }),
1524
+ /* @__PURE__ */ jsx(
1525
+ Input,
1146
1526
  {
1147
- value: formData.kind,
1148
- onValueChange: (value) => setFormData({ ...formData, kind: value, value: "" }),
1149
- required: true,
1150
- children: [
1151
- /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกประเภท" }) }),
1152
- /* @__PURE__ */ jsxs(Select.Content, { children: [
1153
- /* @__PURE__ */ jsx(Select.Item, { value: "PROVINCE", children: "จังหวัด" }),
1154
- /* @__PURE__ */ jsx(Select.Item, { value: "POSTCODE_PREFIX", children: "รหัสไปรษณีย์" })
1155
- ] })
1156
- ]
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: "value", children: formData.kind === "PROVINCE" ? "ชื่อจังหวัด *" : "รหัสไปรษณีย์ (1-5 หลัก) *" }),
1162
- formData.kind === "PROVINCE" ? /* @__PURE__ */ jsxs(
1536
+ /* @__PURE__ */ jsx(Label, { htmlFor: "category", children: "หมวดหมู่ (ไม่บังคับ)" }),
1537
+ /* @__PURE__ */ jsxs(
1163
1538
  Select,
1164
1539
  {
1165
- value: formData.value,
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: TH_PROVINCES.map((province) => /* @__PURE__ */ jsx(Select.Item, { value: province, children: province }, province)) })
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
- ) : /* @__PURE__ */ jsx(
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: "value",
1177
- type: "text",
1178
- value: formData.value,
1179
- onChange: (e) => setFormData({ ...formData, value: e.target.value }),
1180
- placeholder: "เช่น 10, 102, 10200",
1181
- maxLength: 5,
1182
- pattern: "\\d{1,5}",
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
- formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
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 AREA_KINDS = [
1631
+ const CATEGORIES = [
1216
1632
  { value: "ALL", label: "ทั้งหมด" },
1217
- { value: "PROVINCE", label: "จังหวัด" },
1218
- { value: "POSTCODE_PREFIX", label: "รหัสไปรษณีย์" }
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 ServiceAreasTable = () => {
1221
- const [areas, setAreas] = useState([]);
1222
- const [filteredAreas, setFilteredAreas] = useState([]);
1640
+ const MaterialCostsTable = () => {
1641
+ const [materialCosts, setMaterialCosts] = useState([]);
1642
+ const [filteredMaterialCosts, setFilteredMaterialCosts] = useState([]);
1223
1643
  const [searchTerm, setSearchTerm] = useState("");
1224
- const [kindFilter, setKindFilter] = useState("ALL");
1644
+ const [categoryFilter, setCategoryFilter] = useState("ALL");
1225
1645
  const [isModalOpen, setIsModalOpen] = useState(false);
1226
- const [editingArea, setEditingArea] = useState(null);
1646
+ const [editingMaterialCost, setEditingMaterialCost] = useState(null);
1227
1647
  const [isLoading, setIsLoading] = useState(false);
1228
- const [deleteAreaId, setDeleteAreaId] = useState(null);
1229
- const [deleteAreaValue, setDeleteAreaValue] = useState("");
1648
+ const [deleteMaterialCostId, setDeleteMaterialCostId] = useState(null);
1649
+ const [deleteMaterialCostName, setDeleteMaterialCostName] = useState("");
1230
1650
  useEffect(() => {
1231
- fetchAreas();
1651
+ fetchMaterialCosts();
1232
1652
  }, []);
1233
1653
  useEffect(() => {
1234
- let filtered = areas;
1235
- if (kindFilter !== "ALL") {
1236
- filtered = filtered.filter((area) => area.kind === kindFilter);
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
- (area) => area.value.toLowerCase().includes(searchTerm.toLowerCase())
1660
+ (mc) => mc.name.toLowerCase().includes(searchTerm.toLowerCase())
1241
1661
  );
1242
1662
  }
1243
- setFilteredAreas(filtered);
1244
- }, [searchTerm, kindFilter, areas]);
1245
- const fetchAreas = async () => {
1663
+ setFilteredMaterialCosts(filtered);
1664
+ }, [searchTerm, categoryFilter, materialCosts]);
1665
+ const fetchMaterialCosts = async () => {
1246
1666
  setIsLoading(true);
1247
1667
  try {
1248
- const response = await fetch("/admin/service-areas", {
1668
+ const response = await fetch("/admin/material-costs", {
1249
1669
  credentials: "include"
1250
1670
  });
1251
1671
  const data = await response.json();
1252
- console.log("Fetched service areas:", data);
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 (area) => {
1681
+ const handleToggleActive = async (materialCost) => {
1263
1682
  try {
1264
- const response = await fetch(`/admin/service-areas/${area.id}`, {
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({ ...area, active: !area.active })
1687
+ body: JSON.stringify({ ...materialCost, active: !materialCost.active })
1269
1688
  });
1270
1689
  if (response.ok) {
1271
1690
  toast.success("สำเร็จ", {
1272
- description: `${area.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}พื้นที่ ${area.value} แล้ว`
1691
+ description: `${materialCost.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}วัสดุ ${materialCost.name} แล้ว`
1273
1692
  });
1274
- fetchAreas();
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 (!deleteAreaId) return;
1704
+ if (!deleteMaterialCostId) return;
1286
1705
  try {
1287
- const response = await fetch(`/admin/service-areas/${deleteAreaId}`, {
1288
- method: "DELETE",
1289
- credentials: "include"
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: `ลบพื้นที่ ${deleteAreaValue} แล้ว`
1715
+ description: `ลบวัสดุ ${deleteMaterialCostName} แล้ว`
1294
1716
  });
1295
- fetchAreas();
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
- setDeleteAreaId(null);
1305
- setDeleteAreaValue("");
1726
+ setDeleteMaterialCostId(null);
1727
+ setDeleteMaterialCostName("");
1306
1728
  }
1307
1729
  };
1308
- const handleEdit = (area) => {
1309
- setEditingArea(area);
1730
+ const handleEdit = (materialCost) => {
1731
+ setEditingMaterialCost(materialCost);
1310
1732
  setIsModalOpen(true);
1311
1733
  };
1312
1734
  const handleCreateNew = () => {
1313
- setEditingArea(null);
1735
+ setEditingMaterialCost(null);
1314
1736
  setIsModalOpen(true);
1315
1737
  };
1316
1738
  const handleModalClose = () => {
1317
1739
  setIsModalOpen(false);
1318
- setEditingArea(null);
1319
- fetchAreas();
1740
+ setEditingMaterialCost(null);
1741
+ fetchMaterialCosts();
1320
1742
  };
1321
- const openDeletePrompt = (id, value) => {
1322
- setDeleteAreaId(id);
1323
- setDeleteAreaValue(value);
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: kindFilter, onValueChange: setKindFilter, children: [
1340
- /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "ประเภท" }) }),
1341
- /* @__PURE__ */ jsx(Select.Content, { children: AREA_KINDS.map((kind) => /* @__PURE__ */ jsx(Select.Item, { value: kind.value, children: kind.label }, kind.value)) })
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: "กำลังโหลด..." }) }) : filteredAreas.length === 0 ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลพื้นที่บริการ" }) }) : filteredAreas.map((area) => /* @__PURE__ */ jsxs(Table.Row, { children: [
1357
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: area.kind === "PROVINCE" ? "blue" : "purple", children: area.kind === "PROVINCE" ? "จังหวัด" : "รหัสไปรษณีย์" }) }),
1358
- /* @__PURE__ */ jsx(Table.Cell, { children: area.value }),
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: area.active ? "green" : "grey",
1799
+ color: materialCost.active ? "green" : "grey",
1363
1800
  className: "cursor-pointer",
1364
- onClick: () => handleToggleActive(area),
1365
- children: area.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
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(area),
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(area.id, area.value),
1820
+ onClick: () => openDeletePrompt(materialCost.id, materialCost.name),
1384
1821
  children: /* @__PURE__ */ jsx(Trash, {})
1385
1822
  }
1386
1823
  )
1387
1824
  ] }) })
1388
- ] }, area.id)) })
1825
+ ] }, materialCost.id)) })
1389
1826
  ] })
1390
1827
  ] }),
1391
- isModalOpen && /* @__PURE__ */ jsx(ServiceAreaModal, { area: editingArea, onClose: handleModalClose }),
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: !!deleteAreaId,
1839
+ open: !!deleteMaterialCostId,
1397
1840
  onOpenChange: () => {
1398
- setDeleteAreaId(null);
1399
- setDeleteAreaValue("");
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
- deleteAreaValue,
1848
+ 'คุณต้องการลบวัสดุ "',
1849
+ deleteMaterialCostName,
1407
1850
  '" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
1408
1851
  ] })
1409
1852
  ] }),
@@ -1416,63 +1859,98 @@ const ServiceAreasTable = () => {
1416
1859
  )
1417
1860
  ] });
1418
1861
  };
1419
- const AreasPage = () => {
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(ServiceAreasTable, {})
1423
- ] }) });
1424
- };
1425
- const config$2 = defineRouteConfig({
1426
- icon: MapPin,
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 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: "อื่นๆ" }
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 MaterialCostModal = ({
1438
- materialCost,
1439
- onClose
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
- name: "",
1443
- unit: "",
1444
- cost_per_unit: "",
1885
+ carrier_type: "COMPANY_FLEET",
1886
+ service_code: "MESSENGER_3H",
1887
+ max_weight_kg: "",
1888
+ price: "",
1445
1889
  currency: "THB",
1446
- category: "",
1447
- description: "",
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
- if (materialCost) {
1902
+ var _a, _b, _c, _d;
1903
+ if (rate) {
1453
1904
  setFormData({
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
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
- }, [materialCost]);
1918
+ }, [rate]);
1464
1919
  const handleSubmit = async (e) => {
1465
1920
  e.preventDefault();
1466
1921
  const errors = [];
1467
- if (!formData.name.trim()) {
1468
- errors.push("กรุณากรอกชื่อวัสดุ");
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 (!formData.unit.trim()) {
1471
- errors.push("กรุณากรอกหน่วย");
1928
+ if (isNaN(price) || price < 0) {
1929
+ errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
1472
1930
  }
1473
- const costPerUnit = parseFloat(formData.cost_per_unit);
1474
- if (isNaN(costPerUnit) || costPerUnit < 0) {
1475
- errors.push("ต้นทุนต่อหน่วยต้องเป็นตัวเลขไม่ติดลบ");
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
- name: formData.name.trim(),
1487
- unit: formData.unit.trim(),
1488
- cost_per_unit: costPerUnit,
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
- category: formData.category || void 0,
1491
- description: formData.description.trim() || void 0,
1969
+ priority,
1492
1970
  active: formData.active
1493
1971
  };
1494
- const url = materialCost ? `/admin/material-costs/${materialCost.id}` : "/admin/material-costs";
1495
- const method = materialCost ? "PUT" : "POST";
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: materialCost ? "แก้ไขรายการต้นทุนวัสดุแล้ว" : "สร้างรายการต้นทุนวัสดุใหม่แล้ว"
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: materialCost ? "แก้ไขรายการต้นทุนวัสดุ" : "สร้างรายการต้นทุนวัสดุใหม่" }) }),
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: "name", children: "ชื่อวัสดุ *" }),
1524
- /* @__PURE__ */ jsx(
1525
- Input,
2008
+ /* @__PURE__ */ jsx(Label, { htmlFor: "carrier", children: "ประเภทขนส่ง *" }),
2009
+ /* @__PURE__ */ jsxs(
2010
+ Select,
1526
2011
  {
1527
- id: "name",
1528
- value: formData.name,
1529
- onChange: (e) => setFormData({ ...formData, name: e.target.value }),
1530
- placeholder: "เช่น พลาสติกกันกระแทก, กล่องกระดาษ",
1531
- required: true
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: "category", children: "หมวดหมู่ (ไม่บังคับ)" }),
2022
+ /* @__PURE__ */ jsx(Label, { htmlFor: "service", children: "บริการจัดส่ง *" }),
1537
2023
  /* @__PURE__ */ jsxs(
1538
2024
  Select,
1539
2025
  {
1540
- value: formData.category || void 0,
1541
- onValueChange: (value) => setFormData({ ...formData, category: value }),
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: CATEGORIES$1.map((category) => /* @__PURE__ */ jsx(Select.Item, { value: category.value, children: category.label }, category.value)) })
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: "unit", children: "หน่วย *" }),
2052
+ /* @__PURE__ */ jsx(Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
1552
2053
  /* @__PURE__ */ jsx(
1553
2054
  Input,
1554
2055
  {
1555
- id: "unit",
1556
- value: formData.unit,
1557
- onChange: (e) => setFormData({ ...formData, unit: e.target.value }),
1558
- placeholder: "เช่น ชิ้น, เมตร, กิโลกรัม",
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: "costPerUnit", children: "ต้นทุนต่อหน่วย *" }),
2068
+ /* @__PURE__ */ jsx(Label, { htmlFor: "price", children: "ราคา (THB) *" }),
1565
2069
  /* @__PURE__ */ jsx(
1566
2070
  Input,
1567
2071
  {
1568
- id: "costPerUnit",
2072
+ id: "price",
1569
2073
  type: "number",
1570
2074
  step: "0.01",
1571
- value: formData.cost_per_unit,
1572
- onChange: (e) => setFormData({ ...formData, cost_per_unit: e.target.value }),
1573
- placeholder: "0.00",
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: "currency", children: "สกุลเงิน" }),
2085
+ /* @__PURE__ */ jsx(Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
1581
2086
  /* @__PURE__ */ jsx(
1582
2087
  Input,
1583
2088
  {
1584
- id: "currency",
1585
- value: formData.currency,
1586
- onChange: (e) => setFormData({ ...formData, currency: e.target.value }),
1587
- placeholder: "THB"
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__ */ 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
- )
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 CATEGORIES = [
2165
+ const CARRIER_TYPES = [
1632
2166
  { value: "ALL", label: "ทั้งหมด" },
1633
- { value: "PACKAGING", label: "วัสดุหีบห่อ" },
1634
- { value: "PROTECTION", label: "วัสดุป้องกัน" },
1635
- { value: "FILLING", label: "วัสดุเติมช่องว่าง" },
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 MaterialCostsTable = () => {
1641
- const [materialCosts, setMaterialCosts] = useState([]);
1642
- const [filteredMaterialCosts, setFilteredMaterialCosts] = useState([]);
1643
- const [searchTerm, setSearchTerm] = useState("");
1644
- const [categoryFilter, setCategoryFilter] = useState("ALL");
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 [editingMaterialCost, setEditingMaterialCost] = useState(null);
2184
+ const [editingRate, setEditingRate] = useState(null);
1647
2185
  const [isLoading, setIsLoading] = useState(false);
1648
- const [deleteMaterialCostId, setDeleteMaterialCostId] = useState(null);
1649
- const [deleteMaterialCostName, setDeleteMaterialCostName] = useState("");
2186
+ const [deleteRateId, setDeleteRateId] = useState(null);
1650
2187
  useEffect(() => {
1651
- fetchMaterialCosts();
2188
+ fetchRates();
1652
2189
  }, []);
1653
2190
  useEffect(() => {
1654
- let filtered = materialCosts;
1655
- if (categoryFilter !== "ALL") {
1656
- filtered = filtered.filter((mc) => mc.category === categoryFilter);
2191
+ let filtered = rates;
2192
+ if (carrierFilter !== "ALL") {
2193
+ filtered = filtered.filter((rate) => rate.carrier_type === carrierFilter);
1657
2194
  }
1658
- if (searchTerm) {
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
- setFilteredMaterialCosts(filtered);
1664
- }, [searchTerm, categoryFilter, materialCosts]);
1665
- const fetchMaterialCosts = async () => {
2198
+ setFilteredRates(filtered);
2199
+ }, [carrierFilter, serviceFilter, rates]);
2200
+ const fetchRates = async () => {
1666
2201
  setIsLoading(true);
1667
2202
  try {
1668
- const response = await fetch("/admin/material-costs", {
2203
+ const response = await fetch("/admin/shipping-rates", {
1669
2204
  credentials: "include"
1670
2205
  });
1671
2206
  const data = await response.json();
1672
- setMaterialCosts(data.material_costs || []);
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 (!deleteMaterialCostId) return;
2217
+ if (!deleteRateId) return;
1705
2218
  try {
1706
- const response = await fetch(
1707
- `/admin/material-costs/${deleteMaterialCostId}`,
1708
- {
1709
- method: "DELETE",
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: `ลบวัสดุ ${deleteMaterialCostName} แล้ว`
2225
+ description: "ลบอัตราค่าขนส่งแล้ว"
1716
2226
  });
1717
- fetchMaterialCosts();
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
- setDeleteMaterialCostId(null);
1727
- setDeleteMaterialCostName("");
2236
+ setDeleteRateId(null);
1728
2237
  }
1729
2238
  };
1730
- const handleEdit = (materialCost) => {
1731
- setEditingMaterialCost(materialCost);
2239
+ const handleEdit = (rate) => {
2240
+ setEditingRate(rate);
1732
2241
  setIsModalOpen(true);
1733
2242
  };
1734
2243
  const handleCreateNew = () => {
1735
- setEditingMaterialCost(null);
2244
+ setEditingRate(null);
1736
2245
  setIsModalOpen(true);
1737
2246
  };
1738
2247
  const handleModalClose = () => {
1739
2248
  setIsModalOpen(false);
1740
- setEditingMaterialCost(null);
1741
- fetchMaterialCosts();
2249
+ setEditingRate(null);
2250
+ fetchRates();
1742
2251
  };
1743
- const openDeletePrompt = (id, name) => {
1744
- setDeleteMaterialCostId(id);
1745
- setDeleteMaterialCostName(name);
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 getCategoryLabel = (category) => {
2256
+ const getServiceLabel = (code) => {
1748
2257
  var _a;
1749
- if (!category) return "-";
1750
- return ((_a = CATEGORIES.find((c) => c.value === category)) == null ? void 0 : _a.label) || category;
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 flex-1", children: [
1756
- /* @__PURE__ */ jsx(
1757
- Input,
1758
- {
1759
- type: "search",
1760
- placeholder: "ค้นหาชื่อวัสดุ...",
1761
- value: searchTerm,
1762
- onChange: (e) => setSearchTerm(e.target.value),
1763
- className: "max-w-md"
1764
- }
1765
- ),
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)) })
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: "กำลังโหลด..." }) }) : 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 }),
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
- materialCost.cost_per_unit.toFixed(2),
1792
- " ",
1793
- materialCost.currency
2307
+ "≤ ",
2308
+ rate.max_weight_kg,
2309
+ " kg"
1794
2310
  ] }),
1795
- /* @__PURE__ */ jsx(Table.Cell, { className: "max-w-xs truncate", children: materialCost.description || "-" }),
1796
- /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
1797
- Badge,
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(materialCost),
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: () => openDeletePrompt(materialCost.id, materialCost.name),
2329
+ onClick: () => setDeleteRateId(rate.id),
1821
2330
  children: /* @__PURE__ */ jsx(Trash, {})
1822
2331
  }
1823
2332
  )
1824
2333
  ] }) })
1825
- ] }, materialCost.id)) })
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: !!deleteMaterialCostId,
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__ */ jsxs(Prompt.Description, { children: [
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 MaterialCostsPage = () => {
1863
- return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
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: "ต้นทุนวัสดุ"
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
- carrier_type: "COMPANY_FLEET",
1886
- service_code: "MESSENGER_3H",
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, _c, _d;
2371
+ var _a, _b;
1903
2372
  if (rate) {
1904
2373
  setFormData({
1905
- carrier_type: rate.carrier_type,
1906
- service_code: rate.service_code,
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 maxWeight = parseFloat(formData.max_weight_kg);
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(maxWeight) || maxWeight <= 0) {
1926
- errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
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: formData.carrier_type,
1965
- service_code: formData.service_code,
1966
- max_weight_kg: maxWeight,
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: formData.currency,
2417
+ currency: "THB",
1969
2418
  priority,
1970
2419
  active: formData.active
1971
2420
  };
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";
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
- toast.success("สำเร็จ", {
1989
- description: rate ? "แก้ไขอัตราค่าขนส่งแล้ว" : "สร้างอัตราค่าขนส่งใหม่แล้ว"
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: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
2450
+ /* @__PURE__ */ jsx(Label, { htmlFor: "min_distance_km", children: "ระยะทางเริ่มต้น (กม.) *" }),
2053
2451
  /* @__PURE__ */ jsx(
2054
2452
  Input,
2055
2453
  {
2056
- id: "maxWeight",
2454
+ id: "min_distance_km",
2057
2455
  type: "number",
2058
- step: "0.1",
2059
2456
  min: "0",
2060
- value: formData.max_weight_kg,
2061
- onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
2062
- placeholder: "เช่น 3",
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: "price", children: "ราคา (THB) *" }),
2465
+ /* @__PURE__ */ jsx(Label, { htmlFor: "max_distance_km", children: "ระยะทางสูงสุด (กม.)" }),
2069
2466
  /* @__PURE__ */ jsx(
2070
2467
  Input,
2071
2468
  {
2072
- id: "price",
2469
+ id: "max_distance_km",
2073
2470
  type: "number",
2074
- step: "0.01",
2075
2471
  min: "0",
2076
- value: formData.price,
2077
- onChange: (e) => setFormData({ ...formData, price: e.target.value }),
2078
- placeholder: "เช่น 80",
2079
- required: true
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: "ตัวเลขสูงกว่าจะถูกเสนอให้เลือกก่อน (ค่าเริ่มต้น 0)" })
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(Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
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 CARRIER_TYPES = [
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 [filteredRates, setFilteredRates] = useState([]);
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 [isLoading, setIsLoading] = useState(false);
2186
- const [deleteRateId, setDeleteRateId] = useState(null);
2542
+ const [deleteId, setDeleteId] = useState(null);
2187
2543
  useEffect(() => {
2188
2544
  fetchRates();
2189
2545
  }, []);
2190
- useEffect(() => {
2191
- let filtered = rates;
2192
- if (carrierFilter !== "ALL") {
2193
- filtered = filtered.filter((rate) => rate.carrier_type === carrierFilter);
2194
- }
2195
- if (serviceFilter !== "ALL") {
2196
- filtered = filtered.filter((rate) => rate.service_code === serviceFilter);
2197
- }
2198
- setFilteredRates(filtered);
2199
- }, [carrierFilter, serviceFilter, rates]);
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("/admin/shipping-rates", {
2204
- credentials: "include"
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.rates || []);
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 (!deleteRateId) return;
2579
+ if (!deleteId) return;
2218
2580
  try {
2219
- const response = await fetch(`/admin/shipping-rates/${deleteRateId}`, {
2220
- method: "DELETE",
2221
- credentials: "include"
2222
- });
2223
- if (response.ok) {
2224
- toast.success("สำเร็จ", {
2225
- description: "ลบอัตราค่าขนส่งแล้ว"
2226
- });
2227
- fetchRates();
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
- setDeleteRateId(null);
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 getCarrierLabel = (type) => {
2253
- var _a;
2254
- return ((_a = CARRIER_TYPES.find((c) => c.value === type)) == null ? void 0 : _a.label) || type;
2255
- };
2256
- const getServiceLabel = (code) => {
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 flex-col gap-y-4", children: [
2271
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
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
- ] })
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(Table, { children: [
2294
- /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { 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" }),
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
- isModalOpen && /* @__PURE__ */ jsx(ShippingRateModal, { rate: editingRate, onClose: handleModalClose }),
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: !!deleteRateId,
2343
- onOpenChange: () => setDeleteRateId(null),
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: "/base-pricing",
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
  };