@lodashventure/medusa-parcel-shipping 0.4.7 → 0.4.8

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.
@@ -218,116 +218,55 @@ const OrderShippingQuoteWidget = ({ data }) => {
218
218
  adminSdk.defineWidgetConfig({
219
219
  zone: "order.details.after"
220
220
  });
221
- const TH_PROVINCES = [
222
- "กรุงเทพมหานคร",
223
- "กระบี่",
224
- "กาญจนบุรี",
225
- "กาฬสินธุ์",
226
- "กำแพงเพชร",
227
- "ขอนแก่น",
228
- "จันทบุรี",
229
- "ฉะเชิงเทรา",
230
- "ชลบุรี",
231
- "ชัยนาท",
232
- "ชัยภูมิ",
233
- "ชุมพร",
234
- "เชียงราย",
235
- "เชียงใหม่",
236
- "ตรัง",
237
- "ตราด",
238
- "ตาก",
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
- const ServiceAreaModal = ({ area, onClose }) => {
221
+ const ParcelBoxModal = ({ box, onClose }) => {
301
222
  const [formData, setFormData] = react.useState({
302
- kind: "PROVINCE",
303
- value: "",
223
+ name: "",
224
+ width_cm: "",
225
+ length_cm: "",
226
+ height_cm: "",
227
+ max_weight_kg: "",
228
+ price_thb: "",
304
229
  active: true
305
230
  });
306
231
  const [isSaving, setIsSaving] = react.useState(false);
307
232
  react.useEffect(() => {
308
- if (area) {
233
+ if (box) {
309
234
  setFormData({
310
- kind: area.kind,
311
- value: area.value,
312
- active: area.active
235
+ name: box.name,
236
+ width_cm: box.width_cm.toString(),
237
+ length_cm: box.length_cm.toString(),
238
+ height_cm: box.height_cm.toString(),
239
+ max_weight_kg: box.max_weight_kg.toString(),
240
+ price_thb: box.price_thb.toString(),
241
+ active: box.active
313
242
  });
314
243
  }
315
- }, [area]);
244
+ }, [box]);
316
245
  const handleSubmit = async (e) => {
317
246
  e.preventDefault();
318
247
  const errors = [];
319
- const trimmedValue = formData.value.trim();
320
- if (!trimmedValue) {
321
- errors.push("กรุณากรอกค่า");
248
+ if (!formData.name.trim()) {
249
+ errors.push("กรุณากรอกชื่อกล่อง");
322
250
  }
323
- if (formData.kind === "PROVINCE") {
324
- if (!TH_PROVINCES.includes(trimmedValue)) {
325
- errors.push("จังหวัดไม่ถูกต้อง กรุณาตรวจสอบการสะกดชื่อจังหวัด");
326
- }
327
- } else if (formData.kind === "POSTCODE_PREFIX") {
328
- if (!/^\d{1,5}$/.test(trimmedValue)) {
329
- errors.push("รหัสไปรษณีย์ต้องเป็นตัวเลข 1-5 หลัก");
330
- }
251
+ const width = parseFloat(formData.width_cm);
252
+ const length = parseFloat(formData.length_cm);
253
+ const height = parseFloat(formData.height_cm);
254
+ const maxWeight = parseFloat(formData.max_weight_kg);
255
+ const price = parseFloat(formData.price_thb);
256
+ if (isNaN(width) || width <= 0) {
257
+ errors.push("ความกว้างต้องเป็นตัวเลขมากกว่า 0");
258
+ }
259
+ if (isNaN(length) || length <= 0) {
260
+ errors.push("ความยาวต้องเป็นตัวเลขมากกว่า 0");
261
+ }
262
+ if (isNaN(height) || height <= 0) {
263
+ errors.push("ความสูงต้องเป็นตัวเลขมากกว่า 0");
264
+ }
265
+ if (isNaN(maxWeight) || maxWeight <= 0) {
266
+ errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
267
+ }
268
+ if (isNaN(price) || price < 0) {
269
+ errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
331
270
  }
332
271
  if (errors.length > 0) {
333
272
  ui.toast.error("ข้อมูลไม่ถูกต้อง", {
@@ -338,12 +277,16 @@ const ServiceAreaModal = ({ area, onClose }) => {
338
277
  setIsSaving(true);
339
278
  try {
340
279
  const payload = {
341
- kind: formData.kind,
342
- value: trimmedValue,
280
+ name: formData.name.trim(),
281
+ width_cm: width,
282
+ length_cm: length,
283
+ height_cm: height,
284
+ max_weight_kg: maxWeight,
285
+ price_thb: price,
343
286
  active: formData.active
344
287
  };
345
- const url = area ? `/admin/service-areas/${area.id}` : "/admin/service-areas";
346
- const method = area ? "PUT" : "POST";
288
+ const url = box ? `/admin/boxes/${box.id}` : "/admin/boxes";
289
+ const method = box ? "PUT" : "POST";
347
290
  const response = await fetch(url, {
348
291
  method,
349
292
  headers: { "Content-Type": "application/json" },
@@ -352,7 +295,7 @@ const ServiceAreaModal = ({ area, onClose }) => {
352
295
  });
353
296
  if (response.ok) {
354
297
  ui.toast.success("สำเร็จ", {
355
- description: area ? "แก้ไขพื้นที่บริการแล้ว" : "สร้างพื้นที่บริการใหม่แล้ว"
298
+ description: box ? "แก้ไขกล่องพัสดุแล้ว" : "สร้างกล่องพัสดุใหม่แล้ว"
356
299
  });
357
300
  onClose();
358
301
  } else {
@@ -368,53 +311,97 @@ const ServiceAreaModal = ({ area, onClose }) => {
368
311
  }
369
312
  };
370
313
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
371
- /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: area ? "แก้ไขพื้นที่บริการ" : "สร้างพื้นที่บริการใหม่" }) }),
314
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: box ? "แก้ไขกล่องพัสดุ" : "สร้างกล่องพัสดุใหม่" }) }),
372
315
  /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
373
316
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
374
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "kind", children: "ประเภท *" }),
375
- /* @__PURE__ */ jsxRuntime.jsxs(
376
- ui.Select,
317
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "name", children: "ชื่อกล่อง *" }),
318
+ /* @__PURE__ */ jsxRuntime.jsx(
319
+ ui.Input,
377
320
  {
378
- value: formData.kind,
379
- onValueChange: (value) => setFormData({ ...formData, kind: value, value: "" }),
380
- required: true,
381
- children: [
382
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกประเภท" }) }),
383
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
384
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "PROVINCE", children: "จังหวัด" }),
385
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "POSTCODE_PREFIX", children: "รหัสไปรษณีย์" })
386
- ] })
387
- ]
321
+ id: "name",
322
+ value: formData.name,
323
+ onChange: (e) => setFormData({ ...formData, name: e.target.value }),
324
+ placeholder: "เช่น กล่อง S, กล่อง M",
325
+ required: true
388
326
  }
389
327
  )
390
328
  ] }),
329
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-x-4", children: [
330
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
331
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "width", children: "ความกว้าง (cm) *" }),
332
+ /* @__PURE__ */ jsxRuntime.jsx(
333
+ ui.Input,
334
+ {
335
+ id: "width",
336
+ type: "number",
337
+ step: "0.1",
338
+ value: formData.width_cm,
339
+ onChange: (e) => setFormData({ ...formData, width_cm: e.target.value }),
340
+ placeholder: "20",
341
+ required: true
342
+ }
343
+ )
344
+ ] }),
345
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
346
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "length", children: "ความยาว (cm) *" }),
347
+ /* @__PURE__ */ jsxRuntime.jsx(
348
+ ui.Input,
349
+ {
350
+ id: "length",
351
+ type: "number",
352
+ step: "0.1",
353
+ value: formData.length_cm,
354
+ onChange: (e) => setFormData({ ...formData, length_cm: e.target.value }),
355
+ placeholder: "30",
356
+ required: true
357
+ }
358
+ )
359
+ ] }),
360
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
361
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "height", children: "ความสูง (cm) *" }),
362
+ /* @__PURE__ */ jsxRuntime.jsx(
363
+ ui.Input,
364
+ {
365
+ id: "height",
366
+ type: "number",
367
+ step: "0.1",
368
+ value: formData.height_cm,
369
+ onChange: (e) => setFormData({ ...formData, height_cm: e.target.value }),
370
+ placeholder: "15",
371
+ required: true
372
+ }
373
+ )
374
+ ] })
375
+ ] }),
391
376
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
392
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "value", children: formData.kind === "PROVINCE" ? "ชื่อจังหวัด *" : "รหัสไปรษณีย์ (1-5 หลัก) *" }),
393
- formData.kind === "PROVINCE" ? /* @__PURE__ */ jsxRuntime.jsxs(
394
- ui.Select,
377
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
378
+ /* @__PURE__ */ jsxRuntime.jsx(
379
+ ui.Input,
395
380
  {
396
- value: formData.value,
397
- onValueChange: (value) => setFormData({ ...formData, value }),
398
- required: true,
399
- children: [
400
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกจังหวัด" }) }),
401
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: TH_PROVINCES.map((province) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: province, children: province }, province)) })
402
- ]
381
+ id: "maxWeight",
382
+ type: "number",
383
+ step: "0.1",
384
+ value: formData.max_weight_kg,
385
+ onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
386
+ placeholder: "0.5",
387
+ required: true
403
388
  }
404
- ) : /* @__PURE__ */ jsxRuntime.jsx(
389
+ )
390
+ ] }),
391
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
392
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
393
+ /* @__PURE__ */ jsxRuntime.jsx(
405
394
  ui.Input,
406
395
  {
407
- id: "value",
408
- type: "text",
409
- value: formData.value,
410
- onChange: (e) => setFormData({ ...formData, value: e.target.value }),
411
- placeholder: "เช่น 10, 102, 10200",
412
- maxLength: 5,
413
- pattern: "\\d{1,5}",
396
+ id: "price",
397
+ type: "number",
398
+ step: "0.01",
399
+ value: formData.price_thb,
400
+ onChange: (e) => setFormData({ ...formData, price_thb: e.target.value }),
401
+ placeholder: "10.00",
414
402
  required: true
415
403
  }
416
- ),
417
- formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
404
+ )
418
405
  ] }),
419
406
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
420
407
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -428,81 +415,59 @@ const ServiceAreaModal = ({ area, onClose }) => {
428
415
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
429
416
  ] }),
430
417
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
431
- /* @__PURE__ */ jsxRuntime.jsx(
432
- ui.Button,
433
- {
434
- type: "button",
435
- variant: "secondary",
436
- onClick: onClose,
437
- disabled: isSaving,
438
- children: "ยกเลิก"
439
- }
440
- ),
418
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
441
419
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
442
420
  ] })
443
421
  ] }) })
444
422
  ] }) });
445
423
  };
446
- const AREA_KINDS = [
447
- { value: "ALL", label: "ทั้งหมด" },
448
- { value: "PROVINCE", label: "จังหวัด" },
449
- { value: "POSTCODE_PREFIX", label: "รหัสไปรษณีย์" }
450
- ];
451
- const ServiceAreasTable = () => {
452
- const [areas, setAreas] = react.useState([]);
453
- const [filteredAreas, setFilteredAreas] = react.useState([]);
424
+ const ParcelBoxesTable = () => {
425
+ const [boxes, setBoxes] = react.useState([]);
426
+ const [filteredBoxes, setFilteredBoxes] = react.useState([]);
454
427
  const [searchTerm, setSearchTerm] = react.useState("");
455
- const [kindFilter, setKindFilter] = react.useState("ALL");
456
428
  const [isModalOpen, setIsModalOpen] = react.useState(false);
457
- const [editingArea, setEditingArea] = react.useState(null);
429
+ const [editingBox, setEditingBox] = react.useState(null);
458
430
  const [isLoading, setIsLoading] = react.useState(false);
459
- const [deleteAreaId, setDeleteAreaId] = react.useState(null);
460
- const [deleteAreaValue, setDeleteAreaValue] = react.useState("");
431
+ const [deleteBoxId, setDeleteBoxId] = react.useState(null);
432
+ const [deleteBoxName, setDeleteBoxName] = react.useState("");
461
433
  react.useEffect(() => {
462
- fetchAreas();
434
+ fetchBoxes();
463
435
  }, []);
464
436
  react.useEffect(() => {
465
- let filtered = areas;
466
- if (kindFilter !== "ALL") {
467
- filtered = filtered.filter((area) => area.kind === kindFilter);
468
- }
469
- if (searchTerm) {
470
- filtered = filtered.filter(
471
- (area) => area.value.toLowerCase().includes(searchTerm.toLowerCase())
472
- );
473
- }
474
- setFilteredAreas(filtered);
475
- }, [searchTerm, kindFilter, areas]);
476
- const fetchAreas = async () => {
437
+ const filtered = boxes.filter(
438
+ (box) => box.name.toLowerCase().includes(searchTerm.toLowerCase())
439
+ );
440
+ setFilteredBoxes(filtered);
441
+ }, [searchTerm, boxes]);
442
+ const fetchBoxes = async () => {
477
443
  setIsLoading(true);
478
444
  try {
479
- const response = await fetch("/admin/service-areas", {
445
+ const response = await fetch("/admin/boxes", {
480
446
  credentials: "include"
481
447
  });
482
448
  const data = await response.json();
483
- console.log("Fetched service areas:", data);
484
- setAreas(data.service_areas || []);
449
+ setBoxes(data.boxes || []);
485
450
  } catch (error) {
486
451
  ui.toast.error("ข้อผิดพลาด", {
487
- description: "ไม่สามารถโหลดข้อมูลพื้นที่บริการได้"
452
+ description: "ไม่สามารถโหลดข้อมูลกล่องพัสดุได้"
488
453
  });
489
454
  } finally {
490
455
  setIsLoading(false);
491
456
  }
492
457
  };
493
- const handleToggleActive = async (area) => {
458
+ const handleToggleActive = async (box) => {
494
459
  try {
495
- const response = await fetch(`/admin/service-areas/${area.id}`, {
460
+ const response = await fetch(`/admin/boxes/${box.id}`, {
496
461
  method: "PUT",
497
462
  headers: { "Content-Type": "application/json" },
498
463
  credentials: "include",
499
- body: JSON.stringify({ ...area, active: !area.active })
464
+ body: JSON.stringify({ ...box, active: !box.active })
500
465
  });
501
466
  if (response.ok) {
502
467
  ui.toast.success("สำเร็จ", {
503
- description: `${area.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}พื้นที่ ${area.value} แล้ว`
468
+ description: `${box.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}กล่อง ${box.name} แล้ว`
504
469
  });
505
- fetchAreas();
470
+ fetchBoxes();
506
471
  } else {
507
472
  throw new Error("Failed to update");
508
473
  }
@@ -513,87 +478,91 @@ const ServiceAreasTable = () => {
513
478
  }
514
479
  };
515
480
  const handleDelete = async () => {
516
- if (!deleteAreaId) return;
481
+ if (!deleteBoxId) return;
517
482
  try {
518
- const response = await fetch(`/admin/service-areas/${deleteAreaId}`, {
483
+ const response = await fetch(`/admin/boxes/${deleteBoxId}`, {
519
484
  method: "DELETE",
520
485
  credentials: "include"
521
486
  });
522
487
  if (response.ok) {
523
488
  ui.toast.success("สำเร็จ", {
524
- description: `ลบพื้นที่ ${deleteAreaValue} แล้ว`
489
+ description: `ลบกล่อง ${deleteBoxName} แล้ว`
525
490
  });
526
- fetchAreas();
491
+ fetchBoxes();
527
492
  } else {
528
493
  throw new Error("Failed to delete");
529
494
  }
530
495
  } catch (error) {
531
496
  ui.toast.error("ข้อผิดพลาด", {
532
- description: "ไม่สามารถลบพื้นที่ได้"
497
+ description: "ไม่สามารถลบกล่องได้"
533
498
  });
534
499
  } finally {
535
- setDeleteAreaId(null);
536
- setDeleteAreaValue("");
500
+ setDeleteBoxId(null);
501
+ setDeleteBoxName("");
537
502
  }
538
503
  };
539
- const handleEdit = (area) => {
540
- setEditingArea(area);
504
+ const handleEdit = (box) => {
505
+ setEditingBox(box);
541
506
  setIsModalOpen(true);
542
507
  };
543
508
  const handleCreateNew = () => {
544
- setEditingArea(null);
509
+ setEditingBox(null);
545
510
  setIsModalOpen(true);
546
511
  };
547
512
  const handleModalClose = () => {
548
513
  setIsModalOpen(false);
549
- setEditingArea(null);
550
- fetchAreas();
551
- };
552
- const openDeletePrompt = (id, value) => {
553
- setDeleteAreaId(id);
554
- setDeleteAreaValue(value);
514
+ setEditingBox(null);
515
+ fetchBoxes();
555
516
  };
556
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
557
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
558
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
559
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4 flex-1", children: [
560
- /* @__PURE__ */ jsxRuntime.jsx(
561
- ui.Input,
562
- {
563
- type: "search",
564
- placeholder: "ค้นหาจังหวัดหรือรหัสไปรษณีย์...",
565
- value: searchTerm,
566
- onChange: (e) => setSearchTerm(e.target.value),
567
- className: "max-w-md"
568
- }
569
- ),
570
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: kindFilter, onValueChange: setKindFilter, children: [
571
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ประเภท" }) }),
572
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: AREA_KINDS.map((kind) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: kind.value, children: kind.label }, kind.value)) })
573
- ] })
574
- ] }),
517
+ const openDeletePrompt = (id, name) => {
518
+ setDeleteBoxId(id);
519
+ setDeleteBoxName(name);
520
+ };
521
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
522
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
523
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
524
+ /* @__PURE__ */ jsxRuntime.jsx(
525
+ ui.Input,
526
+ {
527
+ type: "search",
528
+ placeholder: "ค้นหาชื่อกล่อง...",
529
+ value: searchTerm,
530
+ onChange: (e) => setSearchTerm(e.target.value),
531
+ className: "max-w-md"
532
+ }
533
+ ),
575
534
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
576
535
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
577
- "สร้างพื้นที่ใหม่"
536
+ "สร้างกล่องใหม่"
578
537
  ] })
579
538
  ] }),
580
539
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
581
540
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
582
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ประเภท" }),
583
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ค่า" }),
541
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ชื่อ" }),
542
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ขนาด (กว้าง×ยาว×สูง cm)" }),
543
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "น้ำหนักสูงสุด (kg)" }),
544
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
584
545
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
585
546
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
586
547
  ] }) }),
587
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredAreas.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลพื้นที่บริการ" }) }) : filteredAreas.map((area) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
588
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: area.kind === "PROVINCE" ? "blue" : "purple", children: area.kind === "PROVINCE" ? "จังหวัด" : "รหัสไปรษณีย์" }) }),
589
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: area.value }),
548
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredBoxes.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลกล่องพัสดุ" }) }) : filteredBoxes.map((box) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
549
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.name }),
550
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
551
+ box.width_cm,
552
+ " × ",
553
+ box.length_cm,
554
+ " × ",
555
+ box.height_cm
556
+ ] }),
557
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.max_weight_kg }),
558
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.price_thb.toFixed(2) }),
590
559
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
591
560
  ui.Badge,
592
561
  {
593
- color: area.active ? "green" : "grey",
562
+ color: box.active ? "green" : "grey",
594
563
  className: "cursor-pointer",
595
- onClick: () => handleToggleActive(area),
596
- children: area.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
564
+ onClick: () => handleToggleActive(box),
565
+ children: box.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
597
566
  }
598
567
  ) }),
599
568
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
@@ -602,7 +571,7 @@ const ServiceAreasTable = () => {
602
571
  {
603
572
  variant: "transparent",
604
573
  size: "small",
605
- onClick: () => handleEdit(area),
574
+ onClick: () => handleEdit(box),
606
575
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
607
576
  }
608
577
  ),
@@ -611,30 +580,30 @@ const ServiceAreasTable = () => {
611
580
  {
612
581
  variant: "transparent",
613
582
  size: "small",
614
- onClick: () => openDeletePrompt(area.id, area.value),
583
+ onClick: () => openDeletePrompt(box.id, box.name),
615
584
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
616
585
  }
617
586
  )
618
587
  ] }) })
619
- ] }, area.id)) })
588
+ ] }, box.id)) })
620
589
  ] })
621
590
  ] }),
622
- isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ServiceAreaModal, { area: editingArea, onClose: handleModalClose }),
591
+ isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ParcelBoxModal, { box: editingBox, onClose: handleModalClose }),
623
592
  /* @__PURE__ */ jsxRuntime.jsx(
624
593
  ui.Prompt,
625
594
  {
626
595
  variant: "confirmation",
627
- open: !!deleteAreaId,
596
+ open: !!deleteBoxId,
628
597
  onOpenChange: () => {
629
- setDeleteAreaId(null);
630
- setDeleteAreaValue("");
598
+ setDeleteBoxId(null);
599
+ setDeleteBoxName("");
631
600
  },
632
601
  children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
633
602
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
634
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบพื้นที่บริการ" }),
603
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบกล่องพัสดุ" }),
635
604
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
636
- 'คุณต้องการลบพื้นที่ "',
637
- deleteAreaValue,
605
+ 'คุณต้องการลบกล่อง "',
606
+ deleteBoxName,
638
607
  '" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
639
608
  ] })
640
609
  ] }),
@@ -647,65 +616,63 @@ const ServiceAreasTable = () => {
647
616
  )
648
617
  ] });
649
618
  };
650
- const AreasPage = () => {
619
+ const BoxesPage = () => {
651
620
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
652
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "พื้นที่บริการ" }) }),
653
- /* @__PURE__ */ jsxRuntime.jsx(ServiceAreasTable, {})
621
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "กล่องพัสดุ" }) }),
622
+ /* @__PURE__ */ jsxRuntime.jsx(ParcelBoxesTable, {})
654
623
  ] }) });
655
624
  };
656
625
  const config$3 = adminSdk.defineRouteConfig({
657
- icon: icons.MapPin,
658
- label: "พื้นที่บริการ"
626
+ icon: icons.ArchiveBox,
627
+ label: "กล่องพัสดุ"
659
628
  });
660
- const ParcelBoxModal = ({ box, onClose }) => {
629
+ const CATEGORIES$1 = [
630
+ { value: "PACKAGING", label: "วัสดุหีบห่อ" },
631
+ { value: "PROTECTION", label: "วัสดุป้องกัน" },
632
+ { value: "FILLING", label: "วัสดุเติมช่องว่าง" },
633
+ { value: "TAPE", label: "เทป/กาว" },
634
+ { value: "LABEL", label: "ฉลาก/สติกเกอร์" },
635
+ { value: "OTHER", label: "อื่นๆ" }
636
+ ];
637
+ const MaterialCostModal = ({
638
+ materialCost,
639
+ onClose
640
+ }) => {
661
641
  const [formData, setFormData] = react.useState({
662
642
  name: "",
663
- width_cm: "",
664
- length_cm: "",
665
- height_cm: "",
666
- max_weight_kg: "",
667
- price_thb: "",
643
+ unit: "",
644
+ cost_per_unit: "",
645
+ currency: "THB",
646
+ category: "",
647
+ description: "",
668
648
  active: true
669
649
  });
670
650
  const [isSaving, setIsSaving] = react.useState(false);
671
651
  react.useEffect(() => {
672
- if (box) {
652
+ if (materialCost) {
673
653
  setFormData({
674
- name: box.name,
675
- width_cm: box.width_cm.toString(),
676
- length_cm: box.length_cm.toString(),
677
- height_cm: box.height_cm.toString(),
678
- max_weight_kg: box.max_weight_kg.toString(),
679
- price_thb: box.price_thb.toString(),
680
- active: box.active
654
+ name: materialCost.name,
655
+ unit: materialCost.unit,
656
+ cost_per_unit: materialCost.cost_per_unit.toString(),
657
+ currency: materialCost.currency,
658
+ category: materialCost.category || "",
659
+ description: materialCost.description || "",
660
+ active: materialCost.active
681
661
  });
682
662
  }
683
- }, [box]);
663
+ }, [materialCost]);
684
664
  const handleSubmit = async (e) => {
685
665
  e.preventDefault();
686
666
  const errors = [];
687
667
  if (!formData.name.trim()) {
688
- errors.push("กรุณากรอกชื่อกล่อง");
689
- }
690
- const width = parseFloat(formData.width_cm);
691
- const length = parseFloat(formData.length_cm);
692
- const height = parseFloat(formData.height_cm);
693
- const maxWeight = parseFloat(formData.max_weight_kg);
694
- const price = parseFloat(formData.price_thb);
695
- if (isNaN(width) || width <= 0) {
696
- errors.push("ความกว้างต้องเป็นตัวเลขมากกว่า 0");
697
- }
698
- if (isNaN(length) || length <= 0) {
699
- errors.push("ความยาวต้องเป็นตัวเลขมากกว่า 0");
700
- }
701
- if (isNaN(height) || height <= 0) {
702
- errors.push("ความสูงต้องเป็นตัวเลขมากกว่า 0");
668
+ errors.push("กรุณากรอกชื่อวัสดุ");
703
669
  }
704
- if (isNaN(maxWeight) || maxWeight <= 0) {
705
- errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
670
+ if (!formData.unit.trim()) {
671
+ errors.push("กรุณากรอกหน่วย");
706
672
  }
707
- if (isNaN(price) || price < 0) {
708
- errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
673
+ const costPerUnit = parseFloat(formData.cost_per_unit);
674
+ if (isNaN(costPerUnit) || costPerUnit < 0) {
675
+ errors.push("ต้นทุนต่อหน่วยต้องเป็นตัวเลขไม่ติดลบ");
709
676
  }
710
677
  if (errors.length > 0) {
711
678
  ui.toast.error("ข้อมูลไม่ถูกต้อง", {
@@ -717,15 +684,15 @@ const ParcelBoxModal = ({ box, onClose }) => {
717
684
  try {
718
685
  const payload = {
719
686
  name: formData.name.trim(),
720
- width_cm: width,
721
- length_cm: length,
722
- height_cm: height,
723
- max_weight_kg: maxWeight,
724
- price_thb: price,
687
+ unit: formData.unit.trim(),
688
+ cost_per_unit: costPerUnit,
689
+ currency: formData.currency,
690
+ category: formData.category || void 0,
691
+ description: formData.description.trim() || void 0,
725
692
  active: formData.active
726
693
  };
727
- const url = box ? `/admin/boxes/${box.id}` : "/admin/boxes";
728
- const method = box ? "PUT" : "POST";
694
+ const url = materialCost ? `/admin/material-costs/${materialCost.id}` : "/admin/material-costs";
695
+ const method = materialCost ? "PUT" : "POST";
729
696
  const response = await fetch(url, {
730
697
  method,
731
698
  headers: { "Content-Type": "application/json" },
@@ -734,7 +701,7 @@ const ParcelBoxModal = ({ box, onClose }) => {
734
701
  });
735
702
  if (response.ok) {
736
703
  ui.toast.success("สำเร็จ", {
737
- description: box ? "แก้ไขกล่องพัสดุแล้ว" : "สร้างกล่องพัสดุใหม่แล้ว"
704
+ description: materialCost ? "แก้ไขรายการต้นทุนวัสดุแล้ว" : "สร้างรายการต้นทุนวัสดุใหม่แล้ว"
738
705
  });
739
706
  onClose();
740
707
  } else {
@@ -750,95 +717,87 @@ const ParcelBoxModal = ({ box, onClose }) => {
750
717
  }
751
718
  };
752
719
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
753
- /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: box ? "แก้ไขกล่องพัสดุ" : "สร้างกล่องพัสดุใหม่" }) }),
720
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: materialCost ? "แก้ไขรายการต้นทุนวัสดุ" : "สร้างรายการต้นทุนวัสดุใหม่" }) }),
754
721
  /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
755
722
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
756
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "name", children: "ชื่อกล่อง *" }),
723
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "name", children: "ชื่อวัสดุ *" }),
757
724
  /* @__PURE__ */ jsxRuntime.jsx(
758
725
  ui.Input,
759
726
  {
760
727
  id: "name",
761
728
  value: formData.name,
762
729
  onChange: (e) => setFormData({ ...formData, name: e.target.value }),
763
- placeholder: "เช่น กล่อง S, กล่อง M",
730
+ placeholder: "เช่น พลาสติกกันกระแทก, กล่องกระดาษ",
764
731
  required: true
765
732
  }
766
733
  )
767
734
  ] }),
768
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-x-4", children: [
769
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
770
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "width", children: "ความกว้าง (cm) *" }),
771
- /* @__PURE__ */ jsxRuntime.jsx(
772
- ui.Input,
773
- {
774
- id: "width",
775
- type: "number",
776
- step: "0.1",
777
- value: formData.width_cm,
778
- onChange: (e) => setFormData({ ...formData, width_cm: e.target.value }),
779
- placeholder: "20",
780
- required: true
781
- }
782
- )
783
- ] }),
735
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
736
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "category", children: "หมวดหมู่ (ไม่บังคับ)" }),
737
+ /* @__PURE__ */ jsxRuntime.jsxs(
738
+ ui.Select,
739
+ {
740
+ value: formData.category || void 0,
741
+ onValueChange: (value) => setFormData({ ...formData, category: value }),
742
+ children: [
743
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกหมวดหมู่ (ไม่บังคับ)" }) }),
744
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CATEGORIES$1.map((category) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: category.value, children: category.label }, category.value)) })
745
+ ]
746
+ }
747
+ )
748
+ ] }),
749
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
784
750
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
785
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "length", children: "ความยาว (cm) *" }),
751
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "unit", children: "หน่วย *" }),
786
752
  /* @__PURE__ */ jsxRuntime.jsx(
787
753
  ui.Input,
788
754
  {
789
- id: "length",
790
- type: "number",
791
- step: "0.1",
792
- value: formData.length_cm,
793
- onChange: (e) => setFormData({ ...formData, length_cm: e.target.value }),
794
- placeholder: "30",
755
+ id: "unit",
756
+ value: formData.unit,
757
+ onChange: (e) => setFormData({ ...formData, unit: e.target.value }),
758
+ placeholder: "เช่น ชิ้น, เมตร, กิโลกรัม",
795
759
  required: true
796
760
  }
797
761
  )
798
762
  ] }),
799
763
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
800
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "height", children: "ความสูง (cm) *" }),
764
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "costPerUnit", children: "ต้นทุนต่อหน่วย *" }),
801
765
  /* @__PURE__ */ jsxRuntime.jsx(
802
766
  ui.Input,
803
767
  {
804
- id: "height",
768
+ id: "costPerUnit",
805
769
  type: "number",
806
- step: "0.1",
807
- value: formData.height_cm,
808
- onChange: (e) => setFormData({ ...formData, height_cm: e.target.value }),
809
- placeholder: "15",
770
+ step: "0.01",
771
+ value: formData.cost_per_unit,
772
+ onChange: (e) => setFormData({ ...formData, cost_per_unit: e.target.value }),
773
+ placeholder: "0.00",
810
774
  required: true
811
775
  }
812
776
  )
813
777
  ] })
814
778
  ] }),
815
779
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
816
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
780
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "currency", children: "สกุลเงิน" }),
817
781
  /* @__PURE__ */ jsxRuntime.jsx(
818
782
  ui.Input,
819
783
  {
820
- id: "maxWeight",
821
- type: "number",
822
- step: "0.1",
823
- value: formData.max_weight_kg,
824
- onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
825
- placeholder: "0.5",
826
- required: true
784
+ id: "currency",
785
+ value: formData.currency,
786
+ onChange: (e) => setFormData({ ...formData, currency: e.target.value }),
787
+ placeholder: "THB"
827
788
  }
828
789
  )
829
790
  ] }),
830
791
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
831
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
792
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "description", children: "คำอธิบาย" }),
832
793
  /* @__PURE__ */ jsxRuntime.jsx(
833
- ui.Input,
794
+ ui.Textarea,
834
795
  {
835
- id: "price",
836
- type: "number",
837
- step: "0.01",
838
- value: formData.price_thb,
839
- onChange: (e) => setFormData({ ...formData, price_thb: e.target.value }),
840
- placeholder: "10.00",
841
- required: true
796
+ id: "description",
797
+ value: formData.description,
798
+ onChange: (e) => setFormData({ ...formData, description: e.target.value }),
799
+ placeholder: "คำอธิบายเพิ่มเติมเกี่ยวกับวัสดุนี้...",
800
+ rows: 3
842
801
  }
843
802
  )
844
803
  ] }),
@@ -851,62 +810,87 @@ const ParcelBoxModal = ({ box, onClose }) => {
851
810
  onCheckedChange: (checked) => setFormData({ ...formData, active: checked })
852
811
  }
853
812
  ),
854
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
855
- ] }),
856
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
857
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
813
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
814
+ ] }),
815
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
816
+ /* @__PURE__ */ jsxRuntime.jsx(
817
+ ui.Button,
818
+ {
819
+ type: "button",
820
+ variant: "secondary",
821
+ onClick: onClose,
822
+ disabled: isSaving,
823
+ children: "ยกเลิก"
824
+ }
825
+ ),
858
826
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
859
827
  ] })
860
828
  ] }) })
861
829
  ] }) });
862
830
  };
863
- const ParcelBoxesTable = () => {
864
- const [boxes, setBoxes] = react.useState([]);
865
- const [filteredBoxes, setFilteredBoxes] = react.useState([]);
831
+ const CATEGORIES = [
832
+ { value: "ALL", label: "ทั้งหมด" },
833
+ { value: "PACKAGING", label: "วัสดุหีบห่อ" },
834
+ { value: "PROTECTION", label: "วัสดุป้องกัน" },
835
+ { value: "FILLING", label: "วัสดุเติมช่องว่าง" },
836
+ { value: "TAPE", label: "เทป/กาว" },
837
+ { value: "LABEL", label: "ฉลาก/สติกเกอร์" },
838
+ { value: "OTHER", label: "อื่นๆ" }
839
+ ];
840
+ const MaterialCostsTable = () => {
841
+ const [materialCosts, setMaterialCosts] = react.useState([]);
842
+ const [filteredMaterialCosts, setFilteredMaterialCosts] = react.useState([]);
866
843
  const [searchTerm, setSearchTerm] = react.useState("");
844
+ const [categoryFilter, setCategoryFilter] = react.useState("ALL");
867
845
  const [isModalOpen, setIsModalOpen] = react.useState(false);
868
- const [editingBox, setEditingBox] = react.useState(null);
846
+ const [editingMaterialCost, setEditingMaterialCost] = react.useState(null);
869
847
  const [isLoading, setIsLoading] = react.useState(false);
870
- const [deleteBoxId, setDeleteBoxId] = react.useState(null);
871
- const [deleteBoxName, setDeleteBoxName] = react.useState("");
848
+ const [deleteMaterialCostId, setDeleteMaterialCostId] = react.useState(null);
849
+ const [deleteMaterialCostName, setDeleteMaterialCostName] = react.useState("");
872
850
  react.useEffect(() => {
873
- fetchBoxes();
851
+ fetchMaterialCosts();
874
852
  }, []);
875
853
  react.useEffect(() => {
876
- const filtered = boxes.filter(
877
- (box) => box.name.toLowerCase().includes(searchTerm.toLowerCase())
878
- );
879
- setFilteredBoxes(filtered);
880
- }, [searchTerm, boxes]);
881
- const fetchBoxes = async () => {
854
+ let filtered = materialCosts;
855
+ if (categoryFilter !== "ALL") {
856
+ filtered = filtered.filter((mc) => mc.category === categoryFilter);
857
+ }
858
+ if (searchTerm) {
859
+ filtered = filtered.filter(
860
+ (mc) => mc.name.toLowerCase().includes(searchTerm.toLowerCase())
861
+ );
862
+ }
863
+ setFilteredMaterialCosts(filtered);
864
+ }, [searchTerm, categoryFilter, materialCosts]);
865
+ const fetchMaterialCosts = async () => {
882
866
  setIsLoading(true);
883
867
  try {
884
- const response = await fetch("/admin/boxes", {
868
+ const response = await fetch("/admin/material-costs", {
885
869
  credentials: "include"
886
870
  });
887
871
  const data = await response.json();
888
- setBoxes(data.boxes || []);
872
+ setMaterialCosts(data.material_costs || []);
889
873
  } catch (error) {
890
874
  ui.toast.error("ข้อผิดพลาด", {
891
- description: "ไม่สามารถโหลดข้อมูลกล่องพัสดุได้"
875
+ description: "ไม่สามารถโหลดข้อมูลต้นทุนวัสดุได้"
892
876
  });
893
877
  } finally {
894
878
  setIsLoading(false);
895
879
  }
896
880
  };
897
- const handleToggleActive = async (box) => {
881
+ const handleToggleActive = async (materialCost) => {
898
882
  try {
899
- const response = await fetch(`/admin/boxes/${box.id}`, {
883
+ const response = await fetch(`/admin/material-costs/${materialCost.id}`, {
900
884
  method: "PUT",
901
885
  headers: { "Content-Type": "application/json" },
902
886
  credentials: "include",
903
- body: JSON.stringify({ ...box, active: !box.active })
887
+ body: JSON.stringify({ ...materialCost, active: !materialCost.active })
904
888
  });
905
889
  if (response.ok) {
906
890
  ui.toast.success("สำเร็จ", {
907
- description: `${box.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}กล่อง ${box.name} แล้ว`
891
+ description: `${materialCost.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}วัสดุ ${materialCost.name} แล้ว`
908
892
  });
909
- fetchBoxes();
893
+ fetchMaterialCosts();
910
894
  } else {
911
895
  throw new Error("Failed to update");
912
896
  }
@@ -917,91 +901,105 @@ const ParcelBoxesTable = () => {
917
901
  }
918
902
  };
919
903
  const handleDelete = async () => {
920
- if (!deleteBoxId) return;
904
+ if (!deleteMaterialCostId) return;
921
905
  try {
922
- const response = await fetch(`/admin/boxes/${deleteBoxId}`, {
923
- method: "DELETE",
924
- credentials: "include"
925
- });
906
+ const response = await fetch(
907
+ `/admin/material-costs/${deleteMaterialCostId}`,
908
+ {
909
+ method: "DELETE",
910
+ credentials: "include"
911
+ }
912
+ );
926
913
  if (response.ok) {
927
914
  ui.toast.success("สำเร็จ", {
928
- description: `ลบกล่อง ${deleteBoxName} แล้ว`
915
+ description: `ลบวัสดุ ${deleteMaterialCostName} แล้ว`
929
916
  });
930
- fetchBoxes();
917
+ fetchMaterialCosts();
931
918
  } else {
932
919
  throw new Error("Failed to delete");
933
920
  }
934
921
  } catch (error) {
935
922
  ui.toast.error("ข้อผิดพลาด", {
936
- description: "ไม่สามารถลบกล่องได้"
923
+ description: "ไม่สามารถลบวัสดุได้"
937
924
  });
938
925
  } finally {
939
- setDeleteBoxId(null);
940
- setDeleteBoxName("");
926
+ setDeleteMaterialCostId(null);
927
+ setDeleteMaterialCostName("");
941
928
  }
942
929
  };
943
- const handleEdit = (box) => {
944
- setEditingBox(box);
930
+ const handleEdit = (materialCost) => {
931
+ setEditingMaterialCost(materialCost);
945
932
  setIsModalOpen(true);
946
933
  };
947
934
  const handleCreateNew = () => {
948
- setEditingBox(null);
935
+ setEditingMaterialCost(null);
949
936
  setIsModalOpen(true);
950
937
  };
951
938
  const handleModalClose = () => {
952
939
  setIsModalOpen(false);
953
- setEditingBox(null);
954
- fetchBoxes();
940
+ setEditingMaterialCost(null);
941
+ fetchMaterialCosts();
955
942
  };
956
943
  const openDeletePrompt = (id, name) => {
957
- setDeleteBoxId(id);
958
- setDeleteBoxName(name);
944
+ setDeleteMaterialCostId(id);
945
+ setDeleteMaterialCostName(name);
946
+ };
947
+ const getCategoryLabel = (category) => {
948
+ var _a;
949
+ if (!category) return "-";
950
+ return ((_a = CATEGORIES.find((c) => c.value === category)) == null ? void 0 : _a.label) || category;
959
951
  };
960
952
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
961
953
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
962
954
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
963
- /* @__PURE__ */ jsxRuntime.jsx(
964
- ui.Input,
965
- {
966
- type: "search",
967
- placeholder: "ค้นหาชื่อกล่อง...",
968
- value: searchTerm,
969
- onChange: (e) => setSearchTerm(e.target.value),
970
- className: "max-w-md"
971
- }
972
- ),
955
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4 flex-1", children: [
956
+ /* @__PURE__ */ jsxRuntime.jsx(
957
+ ui.Input,
958
+ {
959
+ type: "search",
960
+ placeholder: "ค้นหาชื่อวัสดุ...",
961
+ value: searchTerm,
962
+ onChange: (e) => setSearchTerm(e.target.value),
963
+ className: "max-w-md"
964
+ }
965
+ ),
966
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: categoryFilter, onValueChange: setCategoryFilter, children: [
967
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "หมวดหมู่" }) }),
968
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CATEGORIES.map((category) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: category.value, children: category.label }, category.value)) })
969
+ ] })
970
+ ] }),
973
971
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
974
972
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
975
- "สร้างกล่องใหม่"
973
+ "สร้างรายการใหม่"
976
974
  ] })
977
975
  ] }),
978
976
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
979
977
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
980
978
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ชื่อ" }),
981
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ขนาด (กว้าง×ยาว×สูง cm)" }),
982
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "น้ำหนักสูงสุด (kg)" }),
983
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
979
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "หมวดหมู่" }),
980
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "หน่วย" }),
981
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ต้นทุนต่อหน่วย" }),
982
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "คำอธิบาย" }),
984
983
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
985
984
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
986
985
  ] }) }),
987
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredBoxes.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลกล่องพัสดุ" }) }) : filteredBoxes.map((box) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
988
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.name }),
986
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredMaterialCosts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลต้นทุนวัสดุ" }) }) : filteredMaterialCosts.map((materialCost) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
987
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: materialCost.name }),
988
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "blue", children: getCategoryLabel(materialCost.category) }) }),
989
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: materialCost.unit }),
989
990
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
990
- box.width_cm,
991
- " × ",
992
- box.length_cm,
993
- " × ",
994
- box.height_cm
991
+ materialCost.cost_per_unit.toFixed(2),
992
+ " ",
993
+ materialCost.currency
995
994
  ] }),
996
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.max_weight_kg }),
997
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.price_thb.toFixed(2) }),
995
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "max-w-xs truncate", children: materialCost.description || "-" }),
998
996
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
999
997
  ui.Badge,
1000
998
  {
1001
- color: box.active ? "green" : "grey",
999
+ color: materialCost.active ? "green" : "grey",
1002
1000
  className: "cursor-pointer",
1003
- onClick: () => handleToggleActive(box),
1004
- children: box.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
1001
+ onClick: () => handleToggleActive(materialCost),
1002
+ children: materialCost.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
1005
1003
  }
1006
1004
  ) }),
1007
1005
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
@@ -1010,7 +1008,7 @@ const ParcelBoxesTable = () => {
1010
1008
  {
1011
1009
  variant: "transparent",
1012
1010
  size: "small",
1013
- onClick: () => handleEdit(box),
1011
+ onClick: () => handleEdit(materialCost),
1014
1012
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
1015
1013
  }
1016
1014
  ),
@@ -1019,30 +1017,36 @@ const ParcelBoxesTable = () => {
1019
1017
  {
1020
1018
  variant: "transparent",
1021
1019
  size: "small",
1022
- onClick: () => openDeletePrompt(box.id, box.name),
1020
+ onClick: () => openDeletePrompt(materialCost.id, materialCost.name),
1023
1021
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
1024
1022
  }
1025
1023
  )
1026
1024
  ] }) })
1027
- ] }, box.id)) })
1025
+ ] }, materialCost.id)) })
1028
1026
  ] })
1029
1027
  ] }),
1030
- isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ParcelBoxModal, { box: editingBox, onClose: handleModalClose }),
1028
+ isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(
1029
+ MaterialCostModal,
1030
+ {
1031
+ materialCost: editingMaterialCost,
1032
+ onClose: handleModalClose
1033
+ }
1034
+ ),
1031
1035
  /* @__PURE__ */ jsxRuntime.jsx(
1032
1036
  ui.Prompt,
1033
1037
  {
1034
1038
  variant: "confirmation",
1035
- open: !!deleteBoxId,
1039
+ open: !!deleteMaterialCostId,
1036
1040
  onOpenChange: () => {
1037
- setDeleteBoxId(null);
1038
- setDeleteBoxName("");
1041
+ setDeleteMaterialCostId(null);
1042
+ setDeleteMaterialCostName("");
1039
1043
  },
1040
1044
  children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
1041
1045
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
1042
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบกล่องพัสดุ" }),
1046
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบรายการต้นทุนวัสดุ" }),
1043
1047
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
1044
- 'คุณต้องการลบกล่อง "',
1045
- deleteBoxName,
1048
+ 'คุณต้องการลบวัสดุ "',
1049
+ deleteMaterialCostName,
1046
1050
  '" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
1047
1051
  ] })
1048
1052
  ] }),
@@ -1055,63 +1059,97 @@ const ParcelBoxesTable = () => {
1055
1059
  )
1056
1060
  ] });
1057
1061
  };
1058
- const BoxesPage = () => {
1062
+ const MaterialCostsPage = () => {
1059
1063
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
1060
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "กล่องพัสดุ" }) }),
1061
- /* @__PURE__ */ jsxRuntime.jsx(ParcelBoxesTable, {})
1064
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "ต้นทุนวัสดุ" }) }),
1065
+ /* @__PURE__ */ jsxRuntime.jsx(MaterialCostsTable, {})
1062
1066
  ] }) });
1063
1067
  };
1064
1068
  const config$2 = adminSdk.defineRouteConfig({
1065
- icon: icons.ArchiveBox,
1066
- label: "กล่องพัสดุ"
1069
+ icon: icons.CurrencyDollarSolid,
1070
+ label: "ต้นทุนวัสดุ"
1067
1071
  });
1068
- const CATEGORIES$1 = [
1069
- { value: "PACKAGING", label: "วัสดุหีบห่อ" },
1070
- { value: "PROTECTION", label: "วัสดุป้องกัน" },
1071
- { value: "FILLING", label: "วัสดุเติมช่องว่าง" },
1072
- { value: "TAPE", label: "เทป/กาว" },
1073
- { value: "LABEL", label: "ฉลาก/สติกเกอร์" },
1074
- { value: "OTHER", label: "อื่นๆ" }
1072
+ const CARRIER_TYPES$1 = [
1073
+ { value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
1074
+ { value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
1075
+ { value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
1075
1076
  ];
1076
- const MaterialCostModal = ({
1077
- materialCost,
1078
- onClose
1079
- }) => {
1077
+ const SERVICE_CODES$1 = [
1078
+ { value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม.", usesHours: true },
1079
+ { value: "SAME_DAY", label: "ส่งด่วนภายในวัน", usesHours: true },
1080
+ { value: "STANDARD_3_5D", label: "EMS 3-5 วัน", usesHours: false },
1081
+ { value: "EXPRESS_1_2D", label: "Express 1-2 วัน", usesHours: false }
1082
+ ];
1083
+ const ShippingRateModal = ({ rate, onClose }) => {
1080
1084
  const [formData, setFormData] = react.useState({
1081
- name: "",
1082
- unit: "",
1083
- cost_per_unit: "",
1085
+ carrier_type: "COMPANY_FLEET",
1086
+ service_code: "MESSENGER_3H",
1087
+ max_weight_kg: "",
1088
+ price: "",
1084
1089
  currency: "THB",
1085
- category: "",
1086
- description: "",
1090
+ priority: "0",
1091
+ eta_hours_min: "",
1092
+ eta_hours_max: "",
1093
+ eta_days_min: "",
1094
+ eta_days_max: "",
1087
1095
  active: true
1088
1096
  });
1089
1097
  const [isSaving, setIsSaving] = react.useState(false);
1098
+ const selectedService = SERVICE_CODES$1.find((s) => s.value === formData.service_code);
1099
+ const usesHours = (selectedService == null ? void 0 : selectedService.usesHours) ?? false;
1090
1100
  react.useEffect(() => {
1091
- if (materialCost) {
1101
+ var _a, _b, _c, _d;
1102
+ if (rate) {
1092
1103
  setFormData({
1093
- name: materialCost.name,
1094
- unit: materialCost.unit,
1095
- cost_per_unit: materialCost.cost_per_unit.toString(),
1096
- currency: materialCost.currency,
1097
- category: materialCost.category || "",
1098
- description: materialCost.description || "",
1099
- active: materialCost.active
1104
+ carrier_type: rate.carrier_type,
1105
+ service_code: rate.service_code,
1106
+ max_weight_kg: rate.max_weight_kg.toString(),
1107
+ price: rate.price.toString(),
1108
+ currency: rate.currency,
1109
+ priority: (rate.priority ?? 0).toString(),
1110
+ eta_hours_min: ((_a = rate.eta_hours_min) == null ? void 0 : _a.toString()) || "",
1111
+ eta_hours_max: ((_b = rate.eta_hours_max) == null ? void 0 : _b.toString()) || "",
1112
+ eta_days_min: ((_c = rate.eta_days_min) == null ? void 0 : _c.toString()) || "",
1113
+ eta_days_max: ((_d = rate.eta_days_max) == null ? void 0 : _d.toString()) || "",
1114
+ active: rate.active
1100
1115
  });
1101
1116
  }
1102
- }, [materialCost]);
1117
+ }, [rate]);
1103
1118
  const handleSubmit = async (e) => {
1104
1119
  e.preventDefault();
1105
1120
  const errors = [];
1106
- if (!formData.name.trim()) {
1107
- errors.push("กรุณากรอกชื่อวัสดุ");
1121
+ const maxWeight = parseFloat(formData.max_weight_kg);
1122
+ const price = parseFloat(formData.price);
1123
+ const priority = parseInt(formData.priority);
1124
+ if (isNaN(maxWeight) || maxWeight <= 0) {
1125
+ errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
1108
1126
  }
1109
- if (!formData.unit.trim()) {
1110
- errors.push("กรุณากรอกหน่วย");
1127
+ if (isNaN(price) || price < 0) {
1128
+ errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
1111
1129
  }
1112
- const costPerUnit = parseFloat(formData.cost_per_unit);
1113
- if (isNaN(costPerUnit) || costPerUnit < 0) {
1114
- errors.push("ต้นทุนต่อหน่วยต้องเป็นตัวเลขไม่ติดลบ");
1130
+ if (isNaN(priority)) {
1131
+ errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
1132
+ }
1133
+ let etaHoursMin;
1134
+ let etaHoursMax;
1135
+ let etaDaysMin;
1136
+ let etaDaysMax;
1137
+ if (usesHours) {
1138
+ etaHoursMin = formData.eta_hours_min ? parseFloat(formData.eta_hours_min) : void 0;
1139
+ etaHoursMax = formData.eta_hours_max ? parseFloat(formData.eta_hours_max) : void 0;
1140
+ if (etaHoursMin === void 0 || etaHoursMax === void 0) {
1141
+ errors.push("กรุณากรอก ETA เป็นชั่วโมงให้ครบ");
1142
+ } else if (etaHoursMin > etaHoursMax) {
1143
+ errors.push("ETA ชั่วโมงต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
1144
+ }
1145
+ } else {
1146
+ etaDaysMin = formData.eta_days_min ? parseFloat(formData.eta_days_min) : void 0;
1147
+ etaDaysMax = formData.eta_days_max ? parseFloat(formData.eta_days_max) : void 0;
1148
+ if (etaDaysMin === void 0 || etaDaysMax === void 0) {
1149
+ errors.push("กรุณากรอก ETA เป็นวันให้ครบ");
1150
+ } else if (etaDaysMin > etaDaysMax) {
1151
+ errors.push("ETA วันต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
1152
+ }
1115
1153
  }
1116
1154
  if (errors.length > 0) {
1117
1155
  ui.toast.error("ข้อมูลไม่ถูกต้อง", {
@@ -1122,16 +1160,23 @@ const MaterialCostModal = ({
1122
1160
  setIsSaving(true);
1123
1161
  try {
1124
1162
  const payload = {
1125
- name: formData.name.trim(),
1126
- unit: formData.unit.trim(),
1127
- cost_per_unit: costPerUnit,
1163
+ carrier_type: formData.carrier_type,
1164
+ service_code: formData.service_code,
1165
+ max_weight_kg: maxWeight,
1166
+ price,
1128
1167
  currency: formData.currency,
1129
- category: formData.category || void 0,
1130
- description: formData.description.trim() || void 0,
1168
+ priority,
1131
1169
  active: formData.active
1132
1170
  };
1133
- const url = materialCost ? `/admin/material-costs/${materialCost.id}` : "/admin/material-costs";
1134
- const method = materialCost ? "PUT" : "POST";
1171
+ if (usesHours) {
1172
+ payload.eta_hours_min = etaHoursMin;
1173
+ payload.eta_hours_max = etaHoursMax;
1174
+ } else {
1175
+ payload.eta_days_min = etaDaysMin;
1176
+ payload.eta_days_max = etaDaysMax;
1177
+ }
1178
+ const url = rate ? `/admin/shipping-rates/${rate.id}` : "/admin/shipping-rates";
1179
+ const method = rate ? "PUT" : "POST";
1135
1180
  const response = await fetch(url, {
1136
1181
  method,
1137
1182
  headers: { "Content-Type": "application/json" },
@@ -1140,7 +1185,7 @@ const MaterialCostModal = ({
1140
1185
  });
1141
1186
  if (response.ok) {
1142
1187
  ui.toast.success("สำเร็จ", {
1143
- description: materialCost ? "แก้ไขรายการต้นทุนวัสดุแล้ว" : "สร้างรายการต้นทุนวัสดุใหม่แล้ว"
1188
+ description: rate ? "แก้ไขอัตราค่าขนส่งแล้ว" : "สร้างอัตราค่าขนส่งใหม่แล้ว"
1144
1189
  });
1145
1190
  onClose();
1146
1191
  } else {
@@ -1156,89 +1201,139 @@ const MaterialCostModal = ({
1156
1201
  }
1157
1202
  };
1158
1203
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
1159
- /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: materialCost ? "แก้ไขรายการต้นทุนวัสดุ" : "สร้างรายการต้นทุนวัสดุใหม่" }) }),
1204
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: rate ? "แก้ไขอัตราค่าขนส่ง" : "สร้างอัตราค่าขนส่งใหม่" }) }),
1160
1205
  /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
1161
1206
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1162
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "name", children: "ชื่อวัสดุ *" }),
1163
- /* @__PURE__ */ jsxRuntime.jsx(
1164
- ui.Input,
1207
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "carrier", children: "ประเภทขนส่ง *" }),
1208
+ /* @__PURE__ */ jsxRuntime.jsxs(
1209
+ ui.Select,
1165
1210
  {
1166
- id: "name",
1167
- value: formData.name,
1168
- onChange: (e) => setFormData({ ...formData, name: e.target.value }),
1169
- placeholder: "เช่น พลาสติกกันกระแทก, กล่องกระดาษ",
1170
- required: true
1211
+ value: formData.carrier_type,
1212
+ onValueChange: (value) => setFormData({ ...formData, carrier_type: value }),
1213
+ children: [
1214
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกประเภทขนส่ง" }) }),
1215
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CARRIER_TYPES$1.map((carrier) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: carrier.value, children: carrier.label }, carrier.value)) })
1216
+ ]
1171
1217
  }
1172
1218
  )
1173
1219
  ] }),
1174
1220
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1175
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "category", children: "หมวดหมู่ (ไม่บังคับ)" }),
1221
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "service", children: "บริการจัดส่ง *" }),
1176
1222
  /* @__PURE__ */ jsxRuntime.jsxs(
1177
1223
  ui.Select,
1178
1224
  {
1179
- value: formData.category || void 0,
1180
- onValueChange: (value) => setFormData({ ...formData, category: value }),
1225
+ value: formData.service_code,
1226
+ onValueChange: (value) => setFormData({
1227
+ ...formData,
1228
+ service_code: value,
1229
+ eta_hours_min: "",
1230
+ eta_hours_max: "",
1231
+ eta_days_min: "",
1232
+ eta_days_max: ""
1233
+ }),
1181
1234
  children: [
1182
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกหมวดหมู่ (ไม่บังคับ)" }) }),
1183
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CATEGORIES$1.map((category) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: category.value, children: category.label }, category.value)) })
1235
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกบริการ" }) }),
1236
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: SERVICE_CODES$1.map((service) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: service.value, children: service.label }, service.value)) })
1184
1237
  ]
1185
1238
  }
1186
1239
  )
1187
1240
  ] }),
1188
1241
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
1189
1242
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1190
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "unit", children: "หน่วย *" }),
1243
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
1191
1244
  /* @__PURE__ */ jsxRuntime.jsx(
1192
1245
  ui.Input,
1193
1246
  {
1194
- id: "unit",
1195
- value: formData.unit,
1196
- onChange: (e) => setFormData({ ...formData, unit: e.target.value }),
1197
- placeholder: "เช่น ชิ้น, เมตร, กิโลกรัม",
1247
+ id: "maxWeight",
1248
+ type: "number",
1249
+ step: "0.1",
1250
+ min: "0",
1251
+ value: formData.max_weight_kg,
1252
+ onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
1253
+ placeholder: "เช่น 3",
1198
1254
  required: true
1199
1255
  }
1200
1256
  )
1201
1257
  ] }),
1202
1258
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1203
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "costPerUnit", children: "ต้นทุนต่อหน่วย *" }),
1259
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
1204
1260
  /* @__PURE__ */ jsxRuntime.jsx(
1205
1261
  ui.Input,
1206
1262
  {
1207
- id: "costPerUnit",
1263
+ id: "price",
1208
1264
  type: "number",
1209
1265
  step: "0.01",
1210
- value: formData.cost_per_unit,
1211
- onChange: (e) => setFormData({ ...formData, cost_per_unit: e.target.value }),
1212
- placeholder: "0.00",
1266
+ min: "0",
1267
+ value: formData.price,
1268
+ onChange: (e) => setFormData({ ...formData, price: e.target.value }),
1269
+ placeholder: "เช่น 80",
1213
1270
  required: true
1214
1271
  }
1215
1272
  )
1216
1273
  ] })
1217
1274
  ] }),
1218
1275
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1219
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "currency", children: "สกุลเงิน" }),
1276
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
1220
1277
  /* @__PURE__ */ jsxRuntime.jsx(
1221
1278
  ui.Input,
1222
1279
  {
1223
- id: "currency",
1224
- value: formData.currency,
1225
- onChange: (e) => setFormData({ ...formData, currency: e.target.value }),
1226
- placeholder: "THB"
1280
+ id: "priority",
1281
+ type: "number",
1282
+ value: formData.priority,
1283
+ onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
1284
+ placeholder: "0"
1227
1285
  }
1228
- )
1286
+ ),
1287
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ตัวเลขสูงกว่าจะถูกเสนอให้เลือกก่อน (ค่าเริ่มต้น 0)" })
1229
1288
  ] }),
1230
1289
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1231
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "description", children: "คำอธิบาย" }),
1232
- /* @__PURE__ */ jsxRuntime.jsx(
1233
- ui.Textarea,
1234
- {
1235
- id: "description",
1236
- value: formData.description,
1237
- onChange: (e) => setFormData({ ...formData, description: e.target.value }),
1238
- placeholder: "คำอธิบายเพิ่มเติมเกี่ยวกับวัสดุนี้...",
1239
- rows: 3
1240
- }
1241
- )
1290
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { children: [
1291
+ usesHours ? "ETA (ชั่วโมง)" : "ETA (วัน)",
1292
+ " *"
1293
+ ] }),
1294
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-x-4 mt-2", children: usesHours ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1295
+ /* @__PURE__ */ jsxRuntime.jsx(
1296
+ ui.Input,
1297
+ {
1298
+ type: "number",
1299
+ placeholder: "ต่ำสุด",
1300
+ value: formData.eta_hours_min,
1301
+ onChange: (e) => setFormData({ ...formData, eta_hours_min: e.target.value }),
1302
+ required: true
1303
+ }
1304
+ ),
1305
+ /* @__PURE__ */ jsxRuntime.jsx(
1306
+ ui.Input,
1307
+ {
1308
+ type: "number",
1309
+ placeholder: "สูงสุด",
1310
+ value: formData.eta_hours_max,
1311
+ onChange: (e) => setFormData({ ...formData, eta_hours_max: e.target.value }),
1312
+ required: true
1313
+ }
1314
+ )
1315
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1316
+ /* @__PURE__ */ jsxRuntime.jsx(
1317
+ ui.Input,
1318
+ {
1319
+ type: "number",
1320
+ placeholder: "ต่ำสุด",
1321
+ value: formData.eta_days_min,
1322
+ onChange: (e) => setFormData({ ...formData, eta_days_min: e.target.value }),
1323
+ required: true
1324
+ }
1325
+ ),
1326
+ /* @__PURE__ */ jsxRuntime.jsx(
1327
+ ui.Input,
1328
+ {
1329
+ type: "number",
1330
+ placeholder: "สูงสุด",
1331
+ value: formData.eta_days_max,
1332
+ onChange: (e) => setFormData({ ...formData, eta_days_max: e.target.value }),
1333
+ required: true
1334
+ }
1335
+ )
1336
+ ] }) })
1242
1337
  ] }),
1243
1338
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
1244
1339
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1252,202 +1347,168 @@ const MaterialCostModal = ({
1252
1347
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
1253
1348
  ] }),
1254
1349
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
1255
- /* @__PURE__ */ jsxRuntime.jsx(
1256
- ui.Button,
1257
- {
1258
- type: "button",
1259
- variant: "secondary",
1260
- onClick: onClose,
1261
- disabled: isSaving,
1262
- children: "ยกเลิก"
1263
- }
1264
- ),
1350
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
1265
1351
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
1266
1352
  ] })
1267
1353
  ] }) })
1268
1354
  ] }) });
1269
1355
  };
1270
- const CATEGORIES = [
1356
+ const CARRIER_TYPES = [
1271
1357
  { value: "ALL", label: "ทั้งหมด" },
1272
- { value: "PACKAGING", label: "วัสดุหีบห่อ" },
1273
- { value: "PROTECTION", label: "วัสดุป้องกัน" },
1274
- { value: "FILLING", label: "วัสดุเติมช่องว่าง" },
1275
- { value: "TAPE", label: "เทป/กาว" },
1276
- { value: "LABEL", label: "ฉลาก/สติกเกอร์" },
1277
- { value: "OTHER", label: "อื่นๆ" }
1358
+ { value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
1359
+ { value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
1360
+ { value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
1278
1361
  ];
1279
- const MaterialCostsTable = () => {
1280
- const [materialCosts, setMaterialCosts] = react.useState([]);
1281
- const [filteredMaterialCosts, setFilteredMaterialCosts] = react.useState([]);
1282
- const [searchTerm, setSearchTerm] = react.useState("");
1283
- const [categoryFilter, setCategoryFilter] = react.useState("ALL");
1362
+ const SERVICE_CODES = [
1363
+ { value: "ALL", label: "ทั้งหมด" },
1364
+ { value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม." },
1365
+ { value: "SAME_DAY", label: "ส่งด่วนภายในวัน" },
1366
+ { value: "STANDARD_3_5D", label: "EMS 3-5 วัน" },
1367
+ { value: "EXPRESS_1_2D", label: "Express 1-2 วัน" }
1368
+ ];
1369
+ const ShippingRatesTable = () => {
1370
+ const [rates, setRates] = react.useState([]);
1371
+ const [filteredRates, setFilteredRates] = react.useState([]);
1372
+ const [carrierFilter, setCarrierFilter] = react.useState("ALL");
1373
+ const [serviceFilter, setServiceFilter] = react.useState("ALL");
1284
1374
  const [isModalOpen, setIsModalOpen] = react.useState(false);
1285
- const [editingMaterialCost, setEditingMaterialCost] = react.useState(null);
1375
+ const [editingRate, setEditingRate] = react.useState(null);
1286
1376
  const [isLoading, setIsLoading] = react.useState(false);
1287
- const [deleteMaterialCostId, setDeleteMaterialCostId] = react.useState(null);
1288
- const [deleteMaterialCostName, setDeleteMaterialCostName] = react.useState("");
1377
+ const [deleteRateId, setDeleteRateId] = react.useState(null);
1289
1378
  react.useEffect(() => {
1290
- fetchMaterialCosts();
1379
+ fetchRates();
1291
1380
  }, []);
1292
1381
  react.useEffect(() => {
1293
- let filtered = materialCosts;
1294
- if (categoryFilter !== "ALL") {
1295
- filtered = filtered.filter((mc) => mc.category === categoryFilter);
1296
- }
1297
- if (searchTerm) {
1298
- filtered = filtered.filter(
1299
- (mc) => mc.name.toLowerCase().includes(searchTerm.toLowerCase())
1300
- );
1382
+ let filtered = rates;
1383
+ if (carrierFilter !== "ALL") {
1384
+ filtered = filtered.filter((rate) => rate.carrier_type === carrierFilter);
1301
1385
  }
1302
- setFilteredMaterialCosts(filtered);
1303
- }, [searchTerm, categoryFilter, materialCosts]);
1304
- const fetchMaterialCosts = async () => {
1386
+ if (serviceFilter !== "ALL") {
1387
+ filtered = filtered.filter((rate) => rate.service_code === serviceFilter);
1388
+ }
1389
+ setFilteredRates(filtered);
1390
+ }, [carrierFilter, serviceFilter, rates]);
1391
+ const fetchRates = async () => {
1305
1392
  setIsLoading(true);
1306
1393
  try {
1307
- const response = await fetch("/admin/material-costs", {
1394
+ const response = await fetch("/admin/shipping-rates", {
1308
1395
  credentials: "include"
1309
1396
  });
1310
1397
  const data = await response.json();
1311
- setMaterialCosts(data.material_costs || []);
1398
+ setRates(data.rates || []);
1312
1399
  } catch (error) {
1313
1400
  ui.toast.error("ข้อผิดพลาด", {
1314
- description: "ไม่สามารถโหลดข้อมูลต้นทุนวัสดุได้"
1401
+ description: "ไม่สามารถโหลดข้อมูลอัตราค่าขนส่งได้"
1315
1402
  });
1316
1403
  } finally {
1317
1404
  setIsLoading(false);
1318
1405
  }
1319
1406
  };
1320
- const handleToggleActive = async (materialCost) => {
1321
- try {
1322
- const response = await fetch(`/admin/material-costs/${materialCost.id}`, {
1323
- method: "PUT",
1324
- headers: { "Content-Type": "application/json" },
1325
- credentials: "include",
1326
- body: JSON.stringify({ ...materialCost, active: !materialCost.active })
1327
- });
1328
- if (response.ok) {
1329
- ui.toast.success("สำเร็จ", {
1330
- description: `${materialCost.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}วัสดุ ${materialCost.name} แล้ว`
1331
- });
1332
- fetchMaterialCosts();
1333
- } else {
1334
- throw new Error("Failed to update");
1335
- }
1336
- } catch (error) {
1337
- ui.toast.error("ข้อผิดพลาด", {
1338
- description: "ไม่สามารถอัพเดทสถานะได้"
1339
- });
1340
- }
1341
- };
1342
1407
  const handleDelete = async () => {
1343
- if (!deleteMaterialCostId) return;
1408
+ if (!deleteRateId) return;
1344
1409
  try {
1345
- const response = await fetch(
1346
- `/admin/material-costs/${deleteMaterialCostId}`,
1347
- {
1348
- method: "DELETE",
1349
- credentials: "include"
1350
- }
1351
- );
1410
+ const response = await fetch(`/admin/shipping-rates/${deleteRateId}`, {
1411
+ method: "DELETE",
1412
+ credentials: "include"
1413
+ });
1352
1414
  if (response.ok) {
1353
1415
  ui.toast.success("สำเร็จ", {
1354
- description: `ลบวัสดุ ${deleteMaterialCostName} แล้ว`
1416
+ description: "ลบอัตราค่าขนส่งแล้ว"
1355
1417
  });
1356
- fetchMaterialCosts();
1418
+ fetchRates();
1357
1419
  } else {
1358
1420
  throw new Error("Failed to delete");
1359
1421
  }
1360
1422
  } catch (error) {
1361
1423
  ui.toast.error("ข้อผิดพลาด", {
1362
- description: "ไม่สามารถลบวัสดุได้"
1424
+ description: "ไม่สามารถลบอัตราค่าขนส่งได้"
1363
1425
  });
1364
1426
  } finally {
1365
- setDeleteMaterialCostId(null);
1366
- setDeleteMaterialCostName("");
1427
+ setDeleteRateId(null);
1367
1428
  }
1368
1429
  };
1369
- const handleEdit = (materialCost) => {
1370
- setEditingMaterialCost(materialCost);
1430
+ const handleEdit = (rate) => {
1431
+ setEditingRate(rate);
1371
1432
  setIsModalOpen(true);
1372
1433
  };
1373
1434
  const handleCreateNew = () => {
1374
- setEditingMaterialCost(null);
1435
+ setEditingRate(null);
1375
1436
  setIsModalOpen(true);
1376
1437
  };
1377
1438
  const handleModalClose = () => {
1378
1439
  setIsModalOpen(false);
1379
- setEditingMaterialCost(null);
1380
- fetchMaterialCosts();
1440
+ setEditingRate(null);
1441
+ fetchRates();
1381
1442
  };
1382
- const openDeletePrompt = (id, name) => {
1383
- setDeleteMaterialCostId(id);
1384
- setDeleteMaterialCostName(name);
1443
+ const getCarrierLabel = (type) => {
1444
+ var _a;
1445
+ return ((_a = CARRIER_TYPES.find((c) => c.value === type)) == null ? void 0 : _a.label) || type;
1385
1446
  };
1386
- const getCategoryLabel = (category) => {
1447
+ const getServiceLabel = (code) => {
1387
1448
  var _a;
1388
- if (!category) return "-";
1389
- return ((_a = CATEGORIES.find((c) => c.value === category)) == null ? void 0 : _a.label) || category;
1449
+ return ((_a = SERVICE_CODES.find((s) => s.value === code)) == null ? void 0 : _a.label) || code;
1450
+ };
1451
+ const formatETA = (rate) => {
1452
+ if (rate.eta_hours_min !== null && rate.eta_hours_min !== void 0 && rate.eta_hours_max !== null && rate.eta_hours_max !== void 0) {
1453
+ return `${rate.eta_hours_min}-${rate.eta_hours_max} ชม.`;
1454
+ }
1455
+ if (rate.eta_days_min !== null && rate.eta_days_min !== void 0 && rate.eta_days_max !== null && rate.eta_days_max !== void 0) {
1456
+ return `${rate.eta_days_min}-${rate.eta_days_max} วัน`;
1457
+ }
1458
+ return "-";
1390
1459
  };
1391
1460
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1392
1461
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
1393
1462
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
1394
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4 flex-1", children: [
1395
- /* @__PURE__ */ jsxRuntime.jsx(
1396
- ui.Input,
1397
- {
1398
- type: "search",
1399
- placeholder: "ค้นหาชื่อวัสดุ...",
1400
- value: searchTerm,
1401
- onChange: (e) => setSearchTerm(e.target.value),
1402
- className: "max-w-md"
1403
- }
1404
- ),
1405
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: categoryFilter, onValueChange: setCategoryFilter, children: [
1406
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "หมวดหมู่" }) }),
1407
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CATEGORIES.map((category) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: category.value, children: category.label }, category.value)) })
1463
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4 items-end", children: [
1464
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
1465
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "ประเภทขนส่ง" }),
1466
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: carrierFilter, onValueChange: setCarrierFilter, children: [
1467
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ทั้งหมด" }) }),
1468
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CARRIER_TYPES.map((type) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: type.value, children: type.label }, type.value)) })
1469
+ ] })
1470
+ ] }),
1471
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
1472
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "บริการจัดส่ง" }),
1473
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: serviceFilter, onValueChange: setServiceFilter, children: [
1474
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ทั้งหมด" }) }),
1475
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: SERVICE_CODES.map((service) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: service.value, children: service.label }, service.value)) })
1476
+ ] })
1408
1477
  ] })
1409
1478
  ] }),
1410
1479
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
1411
1480
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
1412
- "สร้างรายการใหม่"
1481
+ "สร้างอัตราใหม่"
1413
1482
  ] })
1414
1483
  ] }),
1415
1484
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
1416
1485
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1417
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ชื่อ" }),
1418
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "หมวดหมู่" }),
1419
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "หน่วย" }),
1420
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ต้นทุนต่อหน่วย" }),
1421
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "คำอธิบาย" }),
1486
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ประเภทขนส่ง" }),
1487
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "บริการ" }),
1488
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "น้ำหนัก ≤ (kg)" }),
1489
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
1490
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ETA" }),
1422
1491
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
1423
1492
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
1424
1493
  ] }) }),
1425
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredMaterialCosts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลต้นทุนวัสดุ" }) }) : filteredMaterialCosts.map((materialCost) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1426
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: materialCost.name }),
1427
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "blue", children: getCategoryLabel(materialCost.category) }) }),
1428
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: materialCost.unit }),
1494
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredRates.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลอัตราค่าขนส่ง" }) }) : filteredRates.map((rate) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1495
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: getCarrierLabel(rate.carrier_type) }),
1496
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: getServiceLabel(rate.service_code) }),
1429
1497
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
1430
- materialCost.cost_per_unit.toFixed(2),
1431
- " ",
1432
- materialCost.currency
1498
+ "≤ ",
1499
+ rate.max_weight_kg,
1500
+ " kg"
1433
1501
  ] }),
1434
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "max-w-xs truncate", children: materialCost.description || "-" }),
1435
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
1436
- ui.Badge,
1437
- {
1438
- color: materialCost.active ? "green" : "grey",
1439
- className: "cursor-pointer",
1440
- onClick: () => handleToggleActive(materialCost),
1441
- children: materialCost.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
1442
- }
1443
- ) }),
1502
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: rate.price.toFixed(2) }),
1503
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "blue", children: formatETA(rate) }) }),
1504
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
1444
1505
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
1445
1506
  /* @__PURE__ */ jsxRuntime.jsx(
1446
1507
  ui.Button,
1447
1508
  {
1448
1509
  variant: "transparent",
1449
1510
  size: "small",
1450
- onClick: () => handleEdit(materialCost),
1511
+ onClick: () => handleEdit(rate),
1451
1512
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
1452
1513
  }
1453
1514
  ),
@@ -1456,38 +1517,25 @@ const MaterialCostsTable = () => {
1456
1517
  {
1457
1518
  variant: "transparent",
1458
1519
  size: "small",
1459
- onClick: () => openDeletePrompt(materialCost.id, materialCost.name),
1520
+ onClick: () => setDeleteRateId(rate.id),
1460
1521
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
1461
1522
  }
1462
1523
  )
1463
1524
  ] }) })
1464
- ] }, materialCost.id)) })
1525
+ ] }, rate.id)) })
1465
1526
  ] })
1466
1527
  ] }),
1467
- isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(
1468
- MaterialCostModal,
1469
- {
1470
- materialCost: editingMaterialCost,
1471
- onClose: handleModalClose
1472
- }
1473
- ),
1528
+ isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ShippingRateModal, { rate: editingRate, onClose: handleModalClose }),
1474
1529
  /* @__PURE__ */ jsxRuntime.jsx(
1475
1530
  ui.Prompt,
1476
1531
  {
1477
1532
  variant: "confirmation",
1478
- open: !!deleteMaterialCostId,
1479
- onOpenChange: () => {
1480
- setDeleteMaterialCostId(null);
1481
- setDeleteMaterialCostName("");
1482
- },
1533
+ open: !!deleteRateId,
1534
+ onOpenChange: () => setDeleteRateId(null),
1483
1535
  children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
1484
1536
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
1485
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบรายการต้นทุนวัสดุ" }),
1486
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
1487
- 'คุณต้องการลบวัสดุ "',
1488
- deleteMaterialCostName,
1489
- '" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
1490
- ] })
1537
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบอัตราค่าขนส่ง" }),
1538
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: "คุณต้องการลบอัตราค่าขนส่งนี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
1491
1539
  ] }),
1492
1540
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
1493
1541
  /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "ยกเลิก" }),
@@ -1498,96 +1546,125 @@ const MaterialCostsTable = () => {
1498
1546
  )
1499
1547
  ] });
1500
1548
  };
1501
- const MaterialCostsPage = () => {
1549
+ const RatesPage = () => {
1502
1550
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
1503
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "ต้นทุนวัสดุ" }) }),
1504
- /* @__PURE__ */ jsxRuntime.jsx(MaterialCostsTable, {})
1551
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "อัตราค่าขนส่ง" }) }),
1552
+ /* @__PURE__ */ jsxRuntime.jsx(ShippingRatesTable, {})
1505
1553
  ] }) });
1506
1554
  };
1507
1555
  const config$1 = adminSdk.defineRouteConfig({
1508
- icon: icons.CurrencyDollarSolid,
1509
- label: "ต้นทุนวัสดุ"
1556
+ icon: icons.HandTruck,
1557
+ label: "อัตราค่าขนส่ง"
1510
1558
  });
1511
- const CARRIER_TYPES$1 = [
1512
- { value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
1513
- { value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
1514
- { value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
1515
- ];
1516
- const SERVICE_CODES$1 = [
1517
- { value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม.", usesHours: true },
1518
- { value: "SAME_DAY", label: "ส่งด่วนภายในวัน", usesHours: true },
1519
- { value: "STANDARD_3_5D", label: "EMS 3-5 วัน", usesHours: false },
1520
- { value: "EXPRESS_1_2D", label: "Express 1-2 วัน", usesHours: false }
1559
+ const TH_PROVINCES = [
1560
+ "กรุงเทพมหานคร",
1561
+ "กระบี่",
1562
+ "กาญจนบุรี",
1563
+ "กาฬสินธุ์",
1564
+ "กำแพงเพชร",
1565
+ "ขอนแก่น",
1566
+ "จันทบุรี",
1567
+ "ฉะเชิงเทรา",
1568
+ "ชลบุรี",
1569
+ "ชัยนาท",
1570
+ "ชัยภูมิ",
1571
+ "ชุมพร",
1572
+ "เชียงราย",
1573
+ "เชียงใหม่",
1574
+ "ตรัง",
1575
+ "ตราด",
1576
+ "ตาก",
1577
+ "นครนายก",
1578
+ "นครปฐม",
1579
+ "นครพนม",
1580
+ "นครราชสีมา",
1581
+ "นครศรีธรรมราช",
1582
+ "นครสวรรค์",
1583
+ "นนทบุรี",
1584
+ "นราธิวาส",
1585
+ "น่าน",
1586
+ "บึงกาฬ",
1587
+ "บุรีรัมย์",
1588
+ "ปทุมธานี",
1589
+ "ประจวบคีรีขันธ์",
1590
+ "ปราจีนบุรี",
1591
+ "ปัตตานี",
1592
+ "พระนครศรีอยุธยา",
1593
+ "พะเยา",
1594
+ "พังงา",
1595
+ "พัทลุง",
1596
+ "พิจิตร",
1597
+ "พิษณุโลก",
1598
+ "เพชรบุรี",
1599
+ "เพชรบูรณ์",
1600
+ "แพร่",
1601
+ "ภูเก็ต",
1602
+ "มหาสารคาม",
1603
+ "มุกดาหาร",
1604
+ "แม่ฮ่องสอน",
1605
+ "ยโสธร",
1606
+ "ยะลา",
1607
+ "ร้อยเอ็ด",
1608
+ "ระนอง",
1609
+ "ระยอง",
1610
+ "ราชบุรี",
1611
+ "ลพบุรี",
1612
+ "ลำปาง",
1613
+ "ลำพูน",
1614
+ "เลย",
1615
+ "ศรีสะเกษ",
1616
+ "สกลนคร",
1617
+ "สงขลา",
1618
+ "สตูล",
1619
+ "สมุทรปราการ",
1620
+ "สมุทรสงคราม",
1621
+ "สมุทรสาคร",
1622
+ "สระแก้ว",
1623
+ "สระบุรี",
1624
+ "สิงห์บุรี",
1625
+ "สุโขทัย",
1626
+ "สุพรรณบุรี",
1627
+ "สุราษฎร์ธานี",
1628
+ "สุรินทร์",
1629
+ "หนองคาย",
1630
+ "หนองบัวลำภู",
1631
+ "อ่างทอง",
1632
+ "อำนาจเจริญ",
1633
+ "อุดรธานี",
1634
+ "อุตรดิตถ์",
1635
+ "อุทัยธานี",
1636
+ "อุบลราชธานี"
1521
1637
  ];
1522
- const ShippingRateModal = ({ rate, onClose }) => {
1638
+ const ServiceAreaModal = ({ area, onClose }) => {
1523
1639
  const [formData, setFormData] = react.useState({
1524
- carrier_type: "COMPANY_FLEET",
1525
- service_code: "MESSENGER_3H",
1526
- max_weight_kg: "",
1527
- price: "",
1528
- currency: "THB",
1529
- priority: "0",
1530
- eta_hours_min: "",
1531
- eta_hours_max: "",
1532
- eta_days_min: "",
1533
- eta_days_max: "",
1640
+ kind: "PROVINCE",
1641
+ value: "",
1534
1642
  active: true
1535
1643
  });
1536
1644
  const [isSaving, setIsSaving] = react.useState(false);
1537
- const selectedService = SERVICE_CODES$1.find((s) => s.value === formData.service_code);
1538
- const usesHours = (selectedService == null ? void 0 : selectedService.usesHours) ?? false;
1539
1645
  react.useEffect(() => {
1540
- var _a, _b, _c, _d;
1541
- if (rate) {
1646
+ if (area) {
1542
1647
  setFormData({
1543
- carrier_type: rate.carrier_type,
1544
- service_code: rate.service_code,
1545
- max_weight_kg: rate.max_weight_kg.toString(),
1546
- price: rate.price.toString(),
1547
- currency: rate.currency,
1548
- priority: (rate.priority ?? 0).toString(),
1549
- eta_hours_min: ((_a = rate.eta_hours_min) == null ? void 0 : _a.toString()) || "",
1550
- eta_hours_max: ((_b = rate.eta_hours_max) == null ? void 0 : _b.toString()) || "",
1551
- eta_days_min: ((_c = rate.eta_days_min) == null ? void 0 : _c.toString()) || "",
1552
- eta_days_max: ((_d = rate.eta_days_max) == null ? void 0 : _d.toString()) || "",
1553
- active: rate.active
1648
+ kind: area.kind,
1649
+ value: area.value,
1650
+ active: area.active
1554
1651
  });
1555
1652
  }
1556
- }, [rate]);
1653
+ }, [area]);
1557
1654
  const handleSubmit = async (e) => {
1558
1655
  e.preventDefault();
1559
1656
  const errors = [];
1560
- const maxWeight = parseFloat(formData.max_weight_kg);
1561
- const price = parseFloat(formData.price);
1562
- const priority = parseInt(formData.priority);
1563
- if (isNaN(maxWeight) || maxWeight <= 0) {
1564
- errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
1565
- }
1566
- if (isNaN(price) || price < 0) {
1567
- errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
1568
- }
1569
- if (isNaN(priority)) {
1570
- errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
1657
+ const trimmedValue = formData.value.trim();
1658
+ if (!trimmedValue) {
1659
+ errors.push("กรุณากรอกค่า");
1571
1660
  }
1572
- let etaHoursMin;
1573
- let etaHoursMax;
1574
- let etaDaysMin;
1575
- let etaDaysMax;
1576
- if (usesHours) {
1577
- etaHoursMin = formData.eta_hours_min ? parseFloat(formData.eta_hours_min) : void 0;
1578
- etaHoursMax = formData.eta_hours_max ? parseFloat(formData.eta_hours_max) : void 0;
1579
- if (etaHoursMin === void 0 || etaHoursMax === void 0) {
1580
- errors.push("กรุณากรอก ETA เป็นชั่วโมงให้ครบ");
1581
- } else if (etaHoursMin > etaHoursMax) {
1582
- errors.push("ETA ชั่วโมงต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
1661
+ if (formData.kind === "PROVINCE") {
1662
+ if (!TH_PROVINCES.includes(trimmedValue)) {
1663
+ errors.push("จังหวัดไม่ถูกต้อง กรุณาตรวจสอบการสะกดชื่อจังหวัด");
1583
1664
  }
1584
- } else {
1585
- etaDaysMin = formData.eta_days_min ? parseFloat(formData.eta_days_min) : void 0;
1586
- etaDaysMax = formData.eta_days_max ? parseFloat(formData.eta_days_max) : void 0;
1587
- if (etaDaysMin === void 0 || etaDaysMax === void 0) {
1588
- errors.push("กรุณากรอก ETA เป็นวันให้ครบ");
1589
- } else if (etaDaysMin > etaDaysMax) {
1590
- errors.push("ETA วันต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
1665
+ } else if (formData.kind === "POSTCODE_PREFIX") {
1666
+ if (!/^\d{1,5}$/.test(trimmedValue)) {
1667
+ errors.push("รหัสไปรษณีย์ต้องเป็นตัวเลข 1-5 หลัก");
1591
1668
  }
1592
1669
  }
1593
1670
  if (errors.length > 0) {
@@ -1599,23 +1676,12 @@ const ShippingRateModal = ({ rate, onClose }) => {
1599
1676
  setIsSaving(true);
1600
1677
  try {
1601
1678
  const payload = {
1602
- carrier_type: formData.carrier_type,
1603
- service_code: formData.service_code,
1604
- max_weight_kg: maxWeight,
1605
- price,
1606
- currency: formData.currency,
1607
- priority,
1679
+ kind: formData.kind,
1680
+ value: trimmedValue,
1608
1681
  active: formData.active
1609
1682
  };
1610
- if (usesHours) {
1611
- payload.eta_hours_min = etaHoursMin;
1612
- payload.eta_hours_max = etaHoursMax;
1613
- } else {
1614
- payload.eta_days_min = etaDaysMin;
1615
- payload.eta_days_max = etaDaysMax;
1616
- }
1617
- const url = rate ? `/admin/shipping-rates/${rate.id}` : "/admin/shipping-rates";
1618
- const method = rate ? "PUT" : "POST";
1683
+ const url = area ? `/admin/service-areas/${area.id}` : "/admin/service-areas";
1684
+ const method = area ? "PUT" : "POST";
1619
1685
  const response = await fetch(url, {
1620
1686
  method,
1621
1687
  headers: { "Content-Type": "application/json" },
@@ -1624,7 +1690,7 @@ const ShippingRateModal = ({ rate, onClose }) => {
1624
1690
  });
1625
1691
  if (response.ok) {
1626
1692
  ui.toast.success("สำเร็จ", {
1627
- description: rate ? "แก้ไขอัตราค่าขนส่งแล้ว" : "สร้างอัตราค่าขนส่งใหม่แล้ว"
1693
+ description: area ? "แก้ไขพื้นที่บริการแล้ว" : "สร้างพื้นที่บริการใหม่แล้ว"
1628
1694
  });
1629
1695
  onClose();
1630
1696
  } else {
@@ -1640,139 +1706,53 @@ const ShippingRateModal = ({ rate, onClose }) => {
1640
1706
  }
1641
1707
  };
1642
1708
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
1643
- /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: rate ? "แก้ไขอัตราค่าขนส่ง" : "สร้างอัตราค่าขนส่งใหม่" }) }),
1709
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: area ? "แก้ไขพื้นที่บริการ" : "สร้างพื้นที่บริการใหม่" }) }),
1644
1710
  /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
1645
1711
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1646
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "carrier", children: "ประเภทขนส่ง *" }),
1712
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "kind", children: "ประเภท *" }),
1647
1713
  /* @__PURE__ */ jsxRuntime.jsxs(
1648
1714
  ui.Select,
1649
1715
  {
1650
- value: formData.carrier_type,
1651
- onValueChange: (value) => setFormData({ ...formData, carrier_type: value }),
1716
+ value: formData.kind,
1717
+ onValueChange: (value) => setFormData({ ...formData, kind: value, value: "" }),
1718
+ required: true,
1652
1719
  children: [
1653
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกประเภทขนส่ง" }) }),
1654
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CARRIER_TYPES$1.map((carrier) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: carrier.value, children: carrier.label }, carrier.value)) })
1720
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกประเภท" }) }),
1721
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
1722
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "PROVINCE", children: "จังหวัด" }),
1723
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "POSTCODE_PREFIX", children: "รหัสไปรษณีย์" })
1724
+ ] })
1655
1725
  ]
1656
1726
  }
1657
1727
  )
1658
1728
  ] }),
1659
1729
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1660
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "service", children: "บริการจัดส่ง *" }),
1661
- /* @__PURE__ */ jsxRuntime.jsxs(
1730
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "value", children: formData.kind === "PROVINCE" ? "ชื่อจังหวัด *" : "รหัสไปรษณีย์ (1-5 หลัก) *" }),
1731
+ formData.kind === "PROVINCE" ? /* @__PURE__ */ jsxRuntime.jsxs(
1662
1732
  ui.Select,
1663
1733
  {
1664
- value: formData.service_code,
1665
- onValueChange: (value) => setFormData({
1666
- ...formData,
1667
- service_code: value,
1668
- eta_hours_min: "",
1669
- eta_hours_max: "",
1670
- eta_days_min: "",
1671
- eta_days_max: ""
1672
- }),
1734
+ value: formData.value,
1735
+ onValueChange: (value) => setFormData({ ...formData, value }),
1736
+ required: true,
1673
1737
  children: [
1674
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกบริการ" }) }),
1675
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: SERVICE_CODES$1.map((service) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: service.value, children: service.label }, service.value)) })
1738
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกจังหวัด" }) }),
1739
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: TH_PROVINCES.map((province) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: province, children: province }, province)) })
1676
1740
  ]
1677
1741
  }
1678
- )
1679
- ] }),
1680
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
1681
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1682
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
1683
- /* @__PURE__ */ jsxRuntime.jsx(
1684
- ui.Input,
1685
- {
1686
- id: "maxWeight",
1687
- type: "number",
1688
- step: "0.1",
1689
- min: "0",
1690
- value: formData.max_weight_kg,
1691
- onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
1692
- placeholder: "เช่น 3",
1693
- required: true
1694
- }
1695
- )
1696
- ] }),
1697
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1698
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
1699
- /* @__PURE__ */ jsxRuntime.jsx(
1700
- ui.Input,
1701
- {
1702
- id: "price",
1703
- type: "number",
1704
- step: "0.01",
1705
- min: "0",
1706
- value: formData.price,
1707
- onChange: (e) => setFormData({ ...formData, price: e.target.value }),
1708
- placeholder: "เช่น 80",
1709
- required: true
1710
- }
1711
- )
1712
- ] })
1713
- ] }),
1714
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1715
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
1716
- /* @__PURE__ */ jsxRuntime.jsx(
1742
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1717
1743
  ui.Input,
1718
1744
  {
1719
- id: "priority",
1720
- type: "number",
1721
- value: formData.priority,
1722
- onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
1723
- placeholder: "0"
1724
- }
1725
- ),
1726
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ตัวเลขสูงกว่าจะถูกเสนอให้เลือกก่อน (ค่าเริ่มต้น 0)" })
1727
- ] }),
1728
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1729
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { children: [
1730
- usesHours ? "ETA (ชั่วโมง)" : "ETA (วัน)",
1731
- " *"
1732
- ] }),
1733
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-x-4 mt-2", children: usesHours ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1734
- /* @__PURE__ */ jsxRuntime.jsx(
1735
- ui.Input,
1736
- {
1737
- type: "number",
1738
- placeholder: "ต่ำสุด",
1739
- value: formData.eta_hours_min,
1740
- onChange: (e) => setFormData({ ...formData, eta_hours_min: e.target.value }),
1741
- required: true
1742
- }
1743
- ),
1744
- /* @__PURE__ */ jsxRuntime.jsx(
1745
- ui.Input,
1746
- {
1747
- type: "number",
1748
- placeholder: "สูงสุด",
1749
- value: formData.eta_hours_max,
1750
- onChange: (e) => setFormData({ ...formData, eta_hours_max: e.target.value }),
1751
- required: true
1752
- }
1753
- )
1754
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1755
- /* @__PURE__ */ jsxRuntime.jsx(
1756
- ui.Input,
1757
- {
1758
- type: "number",
1759
- placeholder: "ต่ำสุด",
1760
- value: formData.eta_days_min,
1761
- onChange: (e) => setFormData({ ...formData, eta_days_min: e.target.value }),
1762
- required: true
1763
- }
1764
- ),
1765
- /* @__PURE__ */ jsxRuntime.jsx(
1766
- ui.Input,
1767
- {
1768
- type: "number",
1769
- placeholder: "สูงสุด",
1770
- value: formData.eta_days_max,
1771
- onChange: (e) => setFormData({ ...formData, eta_days_max: e.target.value }),
1772
- required: true
1773
- }
1774
- )
1775
- ] }) })
1745
+ id: "value",
1746
+ type: "text",
1747
+ value: formData.value,
1748
+ onChange: (e) => setFormData({ ...formData, value: e.target.value }),
1749
+ placeholder: "เช่น 10, 102, 10200",
1750
+ maxLength: 5,
1751
+ pattern: "\\d{1,5}",
1752
+ required: true
1753
+ }
1754
+ ),
1755
+ formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
1776
1756
  ] }),
1777
1757
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
1778
1758
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1786,168 +1766,181 @@ const ShippingRateModal = ({ rate, onClose }) => {
1786
1766
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
1787
1767
  ] }),
1788
1768
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
1789
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
1769
+ /* @__PURE__ */ jsxRuntime.jsx(
1770
+ ui.Button,
1771
+ {
1772
+ type: "button",
1773
+ variant: "secondary",
1774
+ onClick: onClose,
1775
+ disabled: isSaving,
1776
+ children: "ยกเลิก"
1777
+ }
1778
+ ),
1790
1779
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
1791
1780
  ] })
1792
1781
  ] }) })
1793
1782
  ] }) });
1794
1783
  };
1795
- const CARRIER_TYPES = [
1796
- { value: "ALL", label: "ทั้งหมด" },
1797
- { value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
1798
- { value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
1799
- { value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
1800
- ];
1801
- const SERVICE_CODES = [
1784
+ const AREA_KINDS = [
1802
1785
  { value: "ALL", label: "ทั้งหมด" },
1803
- { value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม." },
1804
- { value: "SAME_DAY", label: "ส่งด่วนภายในวัน" },
1805
- { value: "STANDARD_3_5D", label: "EMS 3-5 วัน" },
1806
- { value: "EXPRESS_1_2D", label: "Express 1-2 วัน" }
1786
+ { value: "PROVINCE", label: "จังหวัด" },
1787
+ { value: "POSTCODE_PREFIX", label: "รหัสไปรษณีย์" }
1807
1788
  ];
1808
- const ShippingRatesTable = () => {
1809
- const [rates, setRates] = react.useState([]);
1810
- const [filteredRates, setFilteredRates] = react.useState([]);
1811
- const [carrierFilter, setCarrierFilter] = react.useState("ALL");
1812
- const [serviceFilter, setServiceFilter] = react.useState("ALL");
1789
+ const ServiceAreasTable = () => {
1790
+ const [areas, setAreas] = react.useState([]);
1791
+ const [filteredAreas, setFilteredAreas] = react.useState([]);
1792
+ const [searchTerm, setSearchTerm] = react.useState("");
1793
+ const [kindFilter, setKindFilter] = react.useState("ALL");
1813
1794
  const [isModalOpen, setIsModalOpen] = react.useState(false);
1814
- const [editingRate, setEditingRate] = react.useState(null);
1795
+ const [editingArea, setEditingArea] = react.useState(null);
1815
1796
  const [isLoading, setIsLoading] = react.useState(false);
1816
- const [deleteRateId, setDeleteRateId] = react.useState(null);
1797
+ const [deleteAreaId, setDeleteAreaId] = react.useState(null);
1798
+ const [deleteAreaValue, setDeleteAreaValue] = react.useState("");
1817
1799
  react.useEffect(() => {
1818
- fetchRates();
1800
+ fetchAreas();
1819
1801
  }, []);
1820
1802
  react.useEffect(() => {
1821
- let filtered = rates;
1822
- if (carrierFilter !== "ALL") {
1823
- filtered = filtered.filter((rate) => rate.carrier_type === carrierFilter);
1803
+ let filtered = areas;
1804
+ if (kindFilter !== "ALL") {
1805
+ filtered = filtered.filter((area) => area.kind === kindFilter);
1824
1806
  }
1825
- if (serviceFilter !== "ALL") {
1826
- filtered = filtered.filter((rate) => rate.service_code === serviceFilter);
1807
+ if (searchTerm) {
1808
+ filtered = filtered.filter(
1809
+ (area) => area.value.toLowerCase().includes(searchTerm.toLowerCase())
1810
+ );
1827
1811
  }
1828
- setFilteredRates(filtered);
1829
- }, [carrierFilter, serviceFilter, rates]);
1830
- const fetchRates = async () => {
1812
+ setFilteredAreas(filtered);
1813
+ }, [searchTerm, kindFilter, areas]);
1814
+ const fetchAreas = async () => {
1831
1815
  setIsLoading(true);
1832
1816
  try {
1833
- const response = await fetch("/admin/shipping-rates", {
1817
+ const response = await fetch("/admin/service-areas", {
1834
1818
  credentials: "include"
1835
1819
  });
1836
1820
  const data = await response.json();
1837
- setRates(data.rates || []);
1821
+ console.log("Fetched service areas:", data);
1822
+ setAreas(data.service_areas || []);
1838
1823
  } catch (error) {
1839
1824
  ui.toast.error("ข้อผิดพลาด", {
1840
- description: "ไม่สามารถโหลดข้อมูลอัตราค่าขนส่งได้"
1825
+ description: "ไม่สามารถโหลดข้อมูลพื้นที่บริการได้"
1841
1826
  });
1842
1827
  } finally {
1843
1828
  setIsLoading(false);
1844
1829
  }
1845
1830
  };
1831
+ const handleToggleActive = async (area) => {
1832
+ try {
1833
+ const response = await fetch(`/admin/service-areas/${area.id}`, {
1834
+ method: "PUT",
1835
+ headers: { "Content-Type": "application/json" },
1836
+ credentials: "include",
1837
+ body: JSON.stringify({ ...area, active: !area.active })
1838
+ });
1839
+ if (response.ok) {
1840
+ ui.toast.success("สำเร็จ", {
1841
+ description: `${area.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}พื้นที่ ${area.value} แล้ว`
1842
+ });
1843
+ fetchAreas();
1844
+ } else {
1845
+ throw new Error("Failed to update");
1846
+ }
1847
+ } catch (error) {
1848
+ ui.toast.error("ข้อผิดพลาด", {
1849
+ description: "ไม่สามารถอัพเดทสถานะได้"
1850
+ });
1851
+ }
1852
+ };
1846
1853
  const handleDelete = async () => {
1847
- if (!deleteRateId) return;
1854
+ if (!deleteAreaId) return;
1848
1855
  try {
1849
- const response = await fetch(`/admin/shipping-rates/${deleteRateId}`, {
1856
+ const response = await fetch(`/admin/service-areas/${deleteAreaId}`, {
1850
1857
  method: "DELETE",
1851
1858
  credentials: "include"
1852
1859
  });
1853
1860
  if (response.ok) {
1854
1861
  ui.toast.success("สำเร็จ", {
1855
- description: "ลบอัตราค่าขนส่งแล้ว"
1862
+ description: `ลบพื้นที่ ${deleteAreaValue} แล้ว`
1856
1863
  });
1857
- fetchRates();
1864
+ fetchAreas();
1858
1865
  } else {
1859
1866
  throw new Error("Failed to delete");
1860
1867
  }
1861
1868
  } catch (error) {
1862
1869
  ui.toast.error("ข้อผิดพลาด", {
1863
- description: "ไม่สามารถลบอัตราค่าขนส่งได้"
1870
+ description: "ไม่สามารถลบพื้นที่ได้"
1864
1871
  });
1865
1872
  } finally {
1866
- setDeleteRateId(null);
1873
+ setDeleteAreaId(null);
1874
+ setDeleteAreaValue("");
1867
1875
  }
1868
1876
  };
1869
- const handleEdit = (rate) => {
1870
- setEditingRate(rate);
1877
+ const handleEdit = (area) => {
1878
+ setEditingArea(area);
1871
1879
  setIsModalOpen(true);
1872
1880
  };
1873
1881
  const handleCreateNew = () => {
1874
- setEditingRate(null);
1882
+ setEditingArea(null);
1875
1883
  setIsModalOpen(true);
1876
1884
  };
1877
1885
  const handleModalClose = () => {
1878
1886
  setIsModalOpen(false);
1879
- setEditingRate(null);
1880
- fetchRates();
1881
- };
1882
- const getCarrierLabel = (type) => {
1883
- var _a;
1884
- return ((_a = CARRIER_TYPES.find((c) => c.value === type)) == null ? void 0 : _a.label) || type;
1885
- };
1886
- const getServiceLabel = (code) => {
1887
- var _a;
1888
- return ((_a = SERVICE_CODES.find((s) => s.value === code)) == null ? void 0 : _a.label) || code;
1887
+ setEditingArea(null);
1888
+ fetchAreas();
1889
1889
  };
1890
- const formatETA = (rate) => {
1891
- if (rate.eta_hours_min !== null && rate.eta_hours_min !== void 0 && rate.eta_hours_max !== null && rate.eta_hours_max !== void 0) {
1892
- return `${rate.eta_hours_min}-${rate.eta_hours_max} ชม.`;
1893
- }
1894
- if (rate.eta_days_min !== null && rate.eta_days_min !== void 0 && rate.eta_days_max !== null && rate.eta_days_max !== void 0) {
1895
- return `${rate.eta_days_min}-${rate.eta_days_max} วัน`;
1896
- }
1897
- return "-";
1890
+ const openDeletePrompt = (id, value) => {
1891
+ setDeleteAreaId(id);
1892
+ setDeleteAreaValue(value);
1898
1893
  };
1899
1894
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1900
1895
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
1901
1896
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
1902
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4 items-end", children: [
1903
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
1904
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "ประเภทขนส่ง" }),
1905
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: carrierFilter, onValueChange: setCarrierFilter, children: [
1906
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ทั้งหมด" }) }),
1907
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: CARRIER_TYPES.map((type) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: type.value, children: type.label }, type.value)) })
1908
- ] })
1909
- ] }),
1910
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
1911
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "บริการจัดส่ง" }),
1912
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: serviceFilter, onValueChange: setServiceFilter, children: [
1913
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ทั้งหมด" }) }),
1914
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: SERVICE_CODES.map((service) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: service.value, children: service.label }, service.value)) })
1915
- ] })
1897
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4 flex-1", children: [
1898
+ /* @__PURE__ */ jsxRuntime.jsx(
1899
+ ui.Input,
1900
+ {
1901
+ type: "search",
1902
+ placeholder: "ค้นหาจังหวัดหรือรหัสไปรษณีย์...",
1903
+ value: searchTerm,
1904
+ onChange: (e) => setSearchTerm(e.target.value),
1905
+ className: "max-w-md"
1906
+ }
1907
+ ),
1908
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: kindFilter, onValueChange: setKindFilter, children: [
1909
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ประเภท" }) }),
1910
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: AREA_KINDS.map((kind) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: kind.value, children: kind.label }, kind.value)) })
1916
1911
  ] })
1917
1912
  ] }),
1918
1913
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
1919
1914
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
1920
- "สร้างอัตราใหม่"
1915
+ "สร้างพื้นที่ใหม่"
1921
1916
  ] })
1922
1917
  ] }),
1923
1918
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
1924
1919
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1925
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ประเภทขนส่ง" }),
1926
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "บริการ" }),
1927
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "น้ำหนัก ≤ (kg)" }),
1928
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
1929
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ETA" }),
1920
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ประเภท" }),
1921
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ค่า" }),
1930
1922
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
1931
1923
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
1932
1924
  ] }) }),
1933
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredRates.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลอัตราค่าขนส่ง" }) }) : filteredRates.map((rate) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1934
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: getCarrierLabel(rate.carrier_type) }),
1935
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: getServiceLabel(rate.service_code) }),
1936
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
1937
- "≤ ",
1938
- rate.max_weight_kg,
1939
- " kg"
1940
- ] }),
1941
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: rate.price.toFixed(2) }),
1942
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "blue", children: formatETA(rate) }) }),
1943
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
1925
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredAreas.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลพื้นที่บริการ" }) }) : filteredAreas.map((area) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1926
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: area.kind === "PROVINCE" ? "blue" : "purple", children: area.kind === "PROVINCE" ? "จังหวัด" : "รหัสไปรษณีย์" }) }),
1927
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: area.value }),
1928
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
1929
+ ui.Badge,
1930
+ {
1931
+ color: area.active ? "green" : "grey",
1932
+ className: "cursor-pointer",
1933
+ onClick: () => handleToggleActive(area),
1934
+ children: area.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
1935
+ }
1936
+ ) }),
1944
1937
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
1945
1938
  /* @__PURE__ */ jsxRuntime.jsx(
1946
1939
  ui.Button,
1947
1940
  {
1948
1941
  variant: "transparent",
1949
1942
  size: "small",
1950
- onClick: () => handleEdit(rate),
1943
+ onClick: () => handleEdit(area),
1951
1944
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
1952
1945
  }
1953
1946
  ),
@@ -1956,25 +1949,32 @@ const ShippingRatesTable = () => {
1956
1949
  {
1957
1950
  variant: "transparent",
1958
1951
  size: "small",
1959
- onClick: () => setDeleteRateId(rate.id),
1952
+ onClick: () => openDeletePrompt(area.id, area.value),
1960
1953
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
1961
1954
  }
1962
1955
  )
1963
1956
  ] }) })
1964
- ] }, rate.id)) })
1957
+ ] }, area.id)) })
1965
1958
  ] })
1966
1959
  ] }),
1967
- isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ShippingRateModal, { rate: editingRate, onClose: handleModalClose }),
1960
+ isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ServiceAreaModal, { area: editingArea, onClose: handleModalClose }),
1968
1961
  /* @__PURE__ */ jsxRuntime.jsx(
1969
1962
  ui.Prompt,
1970
1963
  {
1971
1964
  variant: "confirmation",
1972
- open: !!deleteRateId,
1973
- onOpenChange: () => setDeleteRateId(null),
1965
+ open: !!deleteAreaId,
1966
+ onOpenChange: () => {
1967
+ setDeleteAreaId(null);
1968
+ setDeleteAreaValue("");
1969
+ },
1974
1970
  children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
1975
1971
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
1976
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบอัตราค่าขนส่ง" }),
1977
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: "คุณต้องการลบอัตราค่าขนส่งนี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
1972
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบพื้นที่บริการ" }),
1973
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
1974
+ 'คุณต้องการลบพื้นที่ "',
1975
+ deleteAreaValue,
1976
+ '" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
1977
+ ] })
1978
1978
  ] }),
1979
1979
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
1980
1980
  /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "ยกเลิก" }),
@@ -1985,15 +1985,15 @@ const ShippingRatesTable = () => {
1985
1985
  )
1986
1986
  ] });
1987
1987
  };
1988
- const RatesPage = () => {
1988
+ const AreasPage = () => {
1989
1989
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
1990
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "อัตราค่าขนส่ง" }) }),
1991
- /* @__PURE__ */ jsxRuntime.jsx(ShippingRatesTable, {})
1990
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "พื้นที่บริการ" }) }),
1991
+ /* @__PURE__ */ jsxRuntime.jsx(ServiceAreasTable, {})
1992
1992
  ] }) });
1993
1993
  };
1994
1994
  const config = adminSdk.defineRouteConfig({
1995
- icon: icons.HandTruck,
1996
- label: "อัตราค่าขนส่ง"
1995
+ icon: icons.MapPin,
1996
+ label: "พื้นที่บริการ"
1997
1997
  });
1998
1998
  const widgetModule = { widgets: [
1999
1999
  {
@@ -2003,10 +2003,6 @@ const widgetModule = { widgets: [
2003
2003
  ] };
2004
2004
  const routeModule = {
2005
2005
  routes: [
2006
- {
2007
- Component: AreasPage,
2008
- path: "/areas"
2009
- },
2010
2006
  {
2011
2007
  Component: BoxesPage,
2012
2008
  path: "/boxes"
@@ -2018,32 +2014,36 @@ const routeModule = {
2018
2014
  {
2019
2015
  Component: RatesPage,
2020
2016
  path: "/rates"
2017
+ },
2018
+ {
2019
+ Component: AreasPage,
2020
+ path: "/areas"
2021
2021
  }
2022
2022
  ]
2023
2023
  };
2024
2024
  const menuItemModule = {
2025
2025
  menuItems: [
2026
2026
  {
2027
- label: config$3.label,
2028
- icon: config$3.icon,
2027
+ label: config.label,
2028
+ icon: config.icon,
2029
2029
  path: "/areas",
2030
2030
  nested: void 0
2031
2031
  },
2032
2032
  {
2033
- label: config$2.label,
2034
- icon: config$2.icon,
2033
+ label: config$3.label,
2034
+ icon: config$3.icon,
2035
2035
  path: "/boxes",
2036
2036
  nested: void 0
2037
2037
  },
2038
2038
  {
2039
- label: config$1.label,
2040
- icon: config$1.icon,
2039
+ label: config$2.label,
2040
+ icon: config$2.icon,
2041
2041
  path: "/material-costs",
2042
2042
  nested: void 0
2043
2043
  },
2044
2044
  {
2045
- label: config.label,
2046
- icon: config.icon,
2045
+ label: config$1.label,
2046
+ icon: config$1.icon,
2047
2047
  path: "/rates",
2048
2048
  nested: void 0
2049
2049
  }