@lodashventure/medusa-parcel-shipping 0.4.8 → 0.4.9

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