@lodashventure/medusa-parcel-shipping 0.4.24 → 0.4.26

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.
@@ -236,116 +236,49 @@ const OrderShippingQuoteWidget = ({ data }) => {
236
236
  adminSdk.defineWidgetConfig({
237
237
  zone: "order.details.after"
238
238
  });
239
- const TH_PROVINCES = [
240
- "กรุงเทพมหานคร",
241
- "กระบี่",
242
- "กาญจนบุรี",
243
- "กาฬสินธุ์",
244
- "กำแพงเพชร",
245
- "ขอนแก่น",
246
- "จันทบุรี",
247
- "ฉะเชิงเทรา",
248
- "ชลบุรี",
249
- "ชัยนาท",
250
- "ชัยภูมิ",
251
- "ชุมพร",
252
- "เชียงราย",
253
- "เชียงใหม่",
254
- "ตรัง",
255
- "ตราด",
256
- "ตาก",
257
- "นครนายก",
258
- "นครปฐม",
259
- "นครพนม",
260
- "นครราชสีมา",
261
- "นครศรีธรรมราช",
262
- "นครสวรรค์",
263
- "นนทบุรี",
264
- "นราธิวาส",
265
- "น่าน",
266
- "บึงกาฬ",
267
- "บุรีรัมย์",
268
- "ปทุมธานี",
269
- "ประจวบคีรีขันธ์",
270
- "ปราจีนบุรี",
271
- "ปัตตานี",
272
- "พระนครศรีอยุธยา",
273
- "พะเยา",
274
- "พังงา",
275
- "พัทลุง",
276
- "พิจิตร",
277
- "พิษณุโลก",
278
- "เพชรบุรี",
279
- "เพชรบูรณ์",
280
- "แพร่",
281
- "ภูเก็ต",
282
- "มหาสารคาม",
283
- "มุกดาหาร",
284
- "แม่ฮ่องสอน",
285
- "ยโสธร",
286
- "ยะลา",
287
- "ร้อยเอ็ด",
288
- "ระนอง",
289
- "ระยอง",
290
- "ราชบุรี",
291
- "ลพบุรี",
292
- "ลำปาง",
293
- "ลำพูน",
294
- "เลย",
295
- "ศรีสะเกษ",
296
- "สกลนคร",
297
- "สงขลา",
298
- "สตูล",
299
- "สมุทรปราการ",
300
- "สมุทรสงคราม",
301
- "สมุทรสาคร",
302
- "สระแก้ว",
303
- "สระบุรี",
304
- "สิงห์บุรี",
305
- "สุโขทัย",
306
- "สุพรรณบุรี",
307
- "สุราษฎร์ธานี",
308
- "สุรินทร์",
309
- "หนองคาย",
310
- "หนองบัวลำภู",
311
- "อ่างทอง",
312
- "อำนาจเจริญ",
313
- "อุดรธานี",
314
- "อุตรดิตถ์",
315
- "อุทัยธานี",
316
- "อุบลราชธานี"
317
- ];
318
- const ServiceAreaModal = ({ area, onClose }) => {
239
+ const MessengerBaseRateModal = ({
240
+ rate,
241
+ onClose
242
+ }) => {
319
243
  const [formData, setFormData] = react.useState({
320
- kind: "PROVINCE",
321
- value: "",
244
+ min_distance_km: "0",
245
+ max_distance_km: "",
246
+ price: "",
247
+ priority: "0",
322
248
  active: true
323
249
  });
324
250
  const [isSaving, setIsSaving] = react.useState(false);
325
251
  react.useEffect(() => {
326
- if (area) {
252
+ var _a, _b;
253
+ if (rate) {
327
254
  setFormData({
328
- kind: area.kind,
329
- value: area.value,
330
- active: area.active
255
+ min_distance_km: ((_a = rate.min_distance_km) == null ? void 0 : _a.toString()) ?? "0",
256
+ max_distance_km: ((_b = rate.max_distance_km) == null ? void 0 : _b.toString()) || "",
257
+ price: rate.price.toString(),
258
+ priority: (rate.priority ?? 0).toString(),
259
+ active: rate.active
331
260
  });
332
261
  }
333
- }, [area]);
262
+ }, [rate]);
334
263
  const handleSubmit = async (e) => {
335
264
  e.preventDefault();
336
265
  const errors = [];
337
- const trimmedValue = formData.value.trim();
338
- if (!trimmedValue) {
339
- errors.push("กรุณากรอกค่า");
266
+ const minDistance = parseFloat(formData.min_distance_km || "0");
267
+ const hasMaxDistance = formData.max_distance_km !== "";
268
+ const maxDistance = hasMaxDistance ? parseFloat(formData.max_distance_km) : void 0;
269
+ const price = parseFloat(formData.price);
270
+ const priority = parseInt(formData.priority || "0", 10);
271
+ if (isNaN(minDistance) || minDistance < 0) {
272
+ errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
340
273
  }
341
- if (formData.kind === "PROVINCE") {
342
- if (!TH_PROVINCES.includes(trimmedValue)) {
343
- errors.push("จังหวัดไม่ถูกต้อง กรุณาตรวจสอบการสะกดชื่อจังหวัด");
344
- }
345
- } else if (formData.kind === "POSTCODE_PREFIX") {
346
- if (!/^\d{1,5}$/.test(trimmedValue)) {
347
- errors.push("รหัสไปรษณีย์ต้องเป็นตัวเลข 1-5 หลัก");
348
- }
274
+ if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
275
+ errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
276
+ }
277
+ if (isNaN(price) || price < 0) {
278
+ errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
279
+ }
280
+ if (isNaN(priority)) {
281
+ errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
349
282
  }
350
283
  if (errors.length > 0) {
351
284
  ui.toast.error("ข้อมูลไม่ถูกต้อง", {
@@ -356,27 +289,32 @@ const ServiceAreaModal = ({ area, onClose }) => {
356
289
  setIsSaving(true);
357
290
  try {
358
291
  const payload = {
359
- kind: formData.kind,
360
- value: trimmedValue,
292
+ carrier_type: "COMPANY_FLEET",
293
+ carrier: "COMPANY_FLEET",
294
+ service_code: "MESSENGER_3H",
295
+ min_distance_km: minDistance,
296
+ max_distance_km: maxDistance === void 0 || isNaN(maxDistance) ? null : maxDistance,
297
+ price,
298
+ currency: "THB",
299
+ priority,
361
300
  active: formData.active
362
301
  };
363
- const url = area ? `/admin/service-areas/${area.id}` : "/admin/service-areas";
364
- const method = area ? "PUT" : "POST";
302
+ const url = rate ? `/admin/base-shipping-prices/${rate.id}` : "/admin/base-shipping-prices";
303
+ const method = rate ? "PUT" : "POST";
365
304
  const response = await fetch(url, {
366
305
  method,
367
306
  headers: { "Content-Type": "application/json" },
368
307
  credentials: "include",
369
308
  body: JSON.stringify(payload)
370
309
  });
371
- if (response.ok) {
372
- ui.toast.success("สำเร็จ", {
373
- description: area ? "แก้ไขพื้นที่บริการแล้ว" : "สร้างพื้นที่บริการใหม่แล้ว"
374
- });
375
- onClose();
376
- } else {
377
- const error = await response.json();
378
- throw new Error(error.message || "Failed to save");
310
+ if (!response.ok) {
311
+ const error = await response.json().catch(() => ({}));
312
+ throw new Error(error.message || "ไม่สามารถบันทึกข้อมูลได้");
379
313
  }
314
+ ui.toast.success("สำเร็จ", {
315
+ description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
316
+ });
317
+ onClose();
380
318
  } catch (error) {
381
319
  ui.toast.error("ข้อผิดพลาด", {
382
320
  description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
@@ -386,53 +324,69 @@ const ServiceAreaModal = ({ area, onClose }) => {
386
324
  }
387
325
  };
388
326
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
389
- /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: area ? "แก้ไขพื้นที่บริการ" : "สร้างพื้นที่บริการใหม่" }) }),
327
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: rate ? "แก้ไขฐานราคาตามระยะทาง" : "สร้างฐานราคาตามระยะทาง" }) }),
390
328
  /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
329
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
330
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
331
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "min_distance_km", children: "ระยะทางเริ่มต้น (กม.) *" }),
332
+ /* @__PURE__ */ jsxRuntime.jsx(
333
+ ui.Input,
334
+ {
335
+ id: "min_distance_km",
336
+ type: "number",
337
+ min: "0",
338
+ step: "0.1",
339
+ value: formData.min_distance_km,
340
+ onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
341
+ required: true
342
+ }
343
+ )
344
+ ] }),
345
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
346
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "max_distance_km", children: "ระยะทางสูงสุด (กม.)" }),
347
+ /* @__PURE__ */ jsxRuntime.jsx(
348
+ ui.Input,
349
+ {
350
+ id: "max_distance_km",
351
+ type: "number",
352
+ min: "0",
353
+ step: "0.1",
354
+ placeholder: "ปล่อยว่างหากไม่มีที่สิ้นสุด",
355
+ value: formData.max_distance_km,
356
+ onChange: (e) => setFormData({ ...formData, max_distance_km: e.target.value })
357
+ }
358
+ ),
359
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
360
+ ] })
361
+ ] }),
391
362
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
392
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "kind", children: "ประเภท *" }),
393
- /* @__PURE__ */ jsxRuntime.jsxs(
394
- ui.Select,
363
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
364
+ /* @__PURE__ */ jsxRuntime.jsx(
365
+ ui.Input,
395
366
  {
396
- value: formData.kind,
397
- onValueChange: (value) => setFormData({ ...formData, kind: value, value: "" }),
398
- required: true,
399
- children: [
400
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกประเภท" }) }),
401
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
402
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "PROVINCE", children: "จังหวัด" }),
403
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "POSTCODE_PREFIX", children: "รหัสไปรษณีย์" })
404
- ] })
405
- ]
367
+ id: "price",
368
+ type: "number",
369
+ min: "0",
370
+ step: "0.01",
371
+ value: formData.price,
372
+ onChange: (e) => setFormData({ ...formData, price: e.target.value }),
373
+ required: true
406
374
  }
407
375
  )
408
376
  ] }),
409
377
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
410
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "value", children: formData.kind === "PROVINCE" ? "ชื่อจังหวัด *" : "รหัสไปรษณีย์ (1-5 หลัก) *" }),
411
- formData.kind === "PROVINCE" ? /* @__PURE__ */ jsxRuntime.jsxs(
412
- ui.Select,
413
- {
414
- value: formData.value,
415
- onValueChange: (value) => setFormData({ ...formData, value }),
416
- required: true,
417
- children: [
418
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกจังหวัด" }) }),
419
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: TH_PROVINCES.map((province) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: province, children: province }, province)) })
420
- ]
421
- }
422
- ) : /* @__PURE__ */ jsxRuntime.jsx(
378
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
379
+ /* @__PURE__ */ jsxRuntime.jsx(
423
380
  ui.Input,
424
381
  {
425
- id: "value",
426
- type: "text",
427
- value: formData.value,
428
- onChange: (e) => setFormData({ ...formData, value: e.target.value }),
429
- placeholder: "เช่น 10, 102, 10200",
430
- maxLength: 5,
431
- pattern: "\\d{1,5}",
432
- required: true
382
+ id: "priority",
383
+ type: "number",
384
+ value: formData.priority,
385
+ onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
386
+ placeholder: "0"
433
387
  }
434
388
  ),
435
- formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
389
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
436
390
  ] }),
437
391
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
438
392
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -461,200 +415,148 @@ const ServiceAreaModal = ({ area, onClose }) => {
461
415
  ] }) })
462
416
  ] }) });
463
417
  };
464
- const AREA_KINDS = [
465
- { value: "ALL", label: "ทั้งหมด" },
466
- { value: "PROVINCE", label: "จังหวัด" },
467
- { value: "POSTCODE_PREFIX", label: "รหัสไปรษณีย์" }
468
- ];
469
- const ServiceAreasTable = () => {
470
- const [areas, setAreas] = react.useState([]);
471
- const [filteredAreas, setFilteredAreas] = react.useState([]);
472
- const [searchTerm, setSearchTerm] = react.useState("");
473
- const [kindFilter, setKindFilter] = react.useState("ALL");
474
- const [isModalOpen, setIsModalOpen] = react.useState(false);
475
- const [editingArea, setEditingArea] = react.useState(null);
418
+ const MessengerBasePricingTable = () => {
419
+ const [rates, setRates] = react.useState([]);
476
420
  const [isLoading, setIsLoading] = react.useState(false);
477
- const [deleteAreaId, setDeleteAreaId] = react.useState(null);
478
- const [deleteAreaValue, setDeleteAreaValue] = react.useState("");
421
+ const [isModalOpen, setIsModalOpen] = react.useState(false);
422
+ const [editingRate, setEditingRate] = react.useState(null);
423
+ const [deleteId, setDeleteId] = react.useState(null);
479
424
  react.useEffect(() => {
480
- fetchAreas();
425
+ fetchRates();
481
426
  }, []);
482
- react.useEffect(() => {
483
- let filtered = areas;
484
- if (kindFilter !== "ALL") {
485
- filtered = filtered.filter((area) => area.kind === kindFilter);
486
- }
487
- if (searchTerm) {
488
- filtered = filtered.filter(
489
- (area) => area.value.toLowerCase().includes(searchTerm.toLowerCase())
490
- );
491
- }
492
- setFilteredAreas(filtered);
493
- }, [searchTerm, kindFilter, areas]);
494
- const fetchAreas = async () => {
427
+ const orderedRates = react.useMemo(() => {
428
+ return [...rates].sort((a, b) => {
429
+ if (a.min_distance_km !== b.min_distance_km) {
430
+ return a.min_distance_km - b.min_distance_km;
431
+ }
432
+ const aMax = a.max_distance_km ?? Number.POSITIVE_INFINITY;
433
+ const bMax = b.max_distance_km ?? Number.POSITIVE_INFINITY;
434
+ return aMax - bMax;
435
+ });
436
+ }, [rates]);
437
+ const fetchRates = async () => {
495
438
  setIsLoading(true);
496
439
  try {
497
- const response = await fetch("/admin/service-areas", {
498
- credentials: "include"
499
- });
440
+ const response = await fetch(
441
+ "/admin/base-shipping-prices?carrier_type=COMPANY_FLEET&service_code=MESSENGER_3H",
442
+ {
443
+ credentials: "include"
444
+ }
445
+ );
446
+ if (!response.ok) {
447
+ throw new Error("failed_to_load_base_rates");
448
+ }
500
449
  const data = await response.json();
501
- console.log("Fetched service areas:", data);
502
- setAreas(data.service_areas || []);
450
+ setRates(data.base_rates || []);
503
451
  } catch (error) {
504
452
  ui.toast.error("ข้อผิดพลาด", {
505
- description: "ไม่สามารถโหลดข้อมูลพื้นที่บริการได้"
453
+ description: "ไม่สามารถโหลดฐานราคาตามระยะทางได้"
506
454
  });
507
455
  } finally {
508
456
  setIsLoading(false);
509
457
  }
510
458
  };
511
- const handleToggleActive = async (area) => {
512
- try {
513
- const response = await fetch(`/admin/service-areas/${area.id}`, {
514
- method: "PUT",
515
- headers: { "Content-Type": "application/json" },
516
- credentials: "include",
517
- body: JSON.stringify({ ...area, active: !area.active })
518
- });
519
- if (response.ok) {
520
- ui.toast.success("สำเร็จ", {
521
- description: `${area.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}พื้นที่ ${area.value} แล้ว`
522
- });
523
- fetchAreas();
524
- } else {
525
- throw new Error("Failed to update");
526
- }
527
- } catch (error) {
528
- ui.toast.error("ข้อผิดพลาด", {
529
- description: "ไม่สามารถอัพเดทสถานะได้"
530
- });
531
- }
532
- };
533
459
  const handleDelete = async () => {
534
- if (!deleteAreaId) return;
460
+ if (!deleteId) return;
535
461
  try {
536
- const response = await fetch(`/admin/service-areas/${deleteAreaId}`, {
537
- method: "DELETE",
538
- credentials: "include"
539
- });
540
- if (response.ok) {
541
- ui.toast.success("สำเร็จ", {
542
- description: `ลบพื้นที่ ${deleteAreaValue} แล้ว`
543
- });
544
- fetchAreas();
545
- } else {
546
- throw new Error("Failed to delete");
462
+ const response = await fetch(
463
+ `/admin/base-shipping-prices/${deleteId}`,
464
+ {
465
+ method: "DELETE",
466
+ credentials: "include"
467
+ }
468
+ );
469
+ if (!response.ok) {
470
+ throw new Error("failed_to_delete");
547
471
  }
472
+ ui.toast.success("สำเร็จ", {
473
+ description: "ลบฐานราคาสำเร็จ"
474
+ });
475
+ fetchRates();
548
476
  } catch (error) {
549
477
  ui.toast.error("ข้อผิดพลาด", {
550
- description: "ไม่สามารถลบพื้นที่ได้"
478
+ description: "ไม่สามารถลบฐานราคาได้"
551
479
  });
552
480
  } finally {
553
- setDeleteAreaId(null);
554
- setDeleteAreaValue("");
481
+ setDeleteId(null);
555
482
  }
556
483
  };
557
- const handleEdit = (area) => {
558
- setEditingArea(area);
559
- setIsModalOpen(true);
560
- };
561
- const handleCreateNew = () => {
562
- setEditingArea(null);
563
- setIsModalOpen(true);
564
- };
565
484
  const handleModalClose = () => {
566
485
  setIsModalOpen(false);
567
- setEditingArea(null);
568
- fetchAreas();
486
+ setEditingRate(null);
487
+ fetchRates();
569
488
  };
570
- const openDeletePrompt = (id, value) => {
571
- setDeleteAreaId(id);
572
- setDeleteAreaValue(value);
489
+ const formatDistanceRange = (rate) => {
490
+ const min = rate.min_distance_km ?? 0;
491
+ const max = rate.max_distance_km;
492
+ if (max === void 0 || max === null) {
493
+ return `${min}+ กม.`;
494
+ }
495
+ return `${min}-${max} กม.`;
573
496
  };
574
497
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
575
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
576
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
577
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4 flex-1", children: [
498
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
499
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
500
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: "ฐานราคาตามระยะทาง (Messenger 3H)" }),
501
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-fg-subtle text-sm", children: "กำหนดราคาเริ่มต้นตามระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
502
+ ] }),
503
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: () => setIsModalOpen(true), children: [
504
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
505
+ "เพิ่มช่วงราคา"
506
+ ] })
507
+ ] }),
508
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { className: "mt-4", children: [
509
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
510
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ช่วงระยะทาง" }),
511
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
512
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ลำดับ" }),
513
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
514
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
515
+ ] }) }),
516
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : orderedRates.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ยังไม่ได้ตั้งค่าฐานราคาตามระยะทาง" }) }) : orderedRates.map((rate) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
517
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatDistanceRange(rate) }),
518
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
519
+ "฿",
520
+ rate.price.toFixed(2)
521
+ ] }),
522
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: rate.priority ?? 0 }),
523
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
524
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
578
525
  /* @__PURE__ */ jsxRuntime.jsx(
579
- ui.Input,
526
+ ui.Button,
580
527
  {
581
- type: "search",
582
- placeholder: "ค้นหาจังหวัดหรือรหัสไปรษณีย์...",
583
- value: searchTerm,
584
- onChange: (e) => setSearchTerm(e.target.value),
585
- className: "max-w-md"
528
+ variant: "transparent",
529
+ size: "small",
530
+ onClick: () => {
531
+ setEditingRate(rate);
532
+ setIsModalOpen(true);
533
+ },
534
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
586
535
  }
587
536
  ),
588
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: kindFilter, onValueChange: setKindFilter, children: [
589
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ประเภท" }) }),
590
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: AREA_KINDS.map((kind) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: kind.value, children: kind.label }, kind.value)) })
591
- ] })
592
- ] }),
593
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
594
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
595
- "สร้างพื้นที่ใหม่"
596
- ] })
597
- ] }),
598
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
599
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
600
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ประเภท" }),
601
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ค่า" }),
602
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
603
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
604
- ] }) }),
605
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredAreas.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลพื้นที่บริการ" }) }) : filteredAreas.map((area) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
606
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: area.kind === "PROVINCE" ? "blue" : "purple", children: area.kind === "PROVINCE" ? "จังหวัด" : "รหัสไปรษณีย์" }) }),
607
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: area.value }),
608
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
609
- ui.Badge,
537
+ /* @__PURE__ */ jsxRuntime.jsx(
538
+ ui.Button,
610
539
  {
611
- color: area.active ? "green" : "grey",
612
- className: "cursor-pointer",
613
- onClick: () => handleToggleActive(area),
614
- children: area.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
540
+ variant: "transparent",
541
+ size: "small",
542
+ onClick: () => setDeleteId(rate.id),
543
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
615
544
  }
616
- ) }),
617
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
618
- /* @__PURE__ */ jsxRuntime.jsx(
619
- ui.Button,
620
- {
621
- variant: "transparent",
622
- size: "small",
623
- onClick: () => handleEdit(area),
624
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
625
- }
626
- ),
627
- /* @__PURE__ */ jsxRuntime.jsx(
628
- ui.Button,
629
- {
630
- variant: "transparent",
631
- size: "small",
632
- onClick: () => openDeletePrompt(area.id, area.value),
633
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
634
- }
635
- )
636
- ] }) })
637
- ] }, area.id)) })
638
- ] })
545
+ )
546
+ ] }) })
547
+ ] }, rate.id)) })
639
548
  ] }),
640
- isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ServiceAreaModal, { area: editingArea, onClose: handleModalClose }),
549
+ isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(MessengerBaseRateModal, { rate: editingRate, onClose: handleModalClose }),
641
550
  /* @__PURE__ */ jsxRuntime.jsx(
642
551
  ui.Prompt,
643
552
  {
644
553
  variant: "confirmation",
645
- open: !!deleteAreaId,
646
- onOpenChange: () => {
647
- setDeleteAreaId(null);
648
- setDeleteAreaValue("");
649
- },
554
+ open: !!deleteId,
555
+ onOpenChange: () => setDeleteId(null),
650
556
  children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
651
557
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
652
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบพื้นที่บริการ" }),
653
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
654
- 'คุณต้องการลบพื้นที่ "',
655
- deleteAreaValue,
656
- '" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
657
- ] })
558
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบฐานราคา" }),
559
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: "คุณต้องการลบช่วงราคานี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
658
560
  ] }),
659
561
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
660
562
  /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "ยกเลิก" }),
@@ -665,60 +567,69 @@ const ServiceAreasTable = () => {
665
567
  )
666
568
  ] });
667
569
  };
668
- const AreasPage = () => {
669
- return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
670
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "พื้นที่บริการ" }) }),
671
- /* @__PURE__ */ jsxRuntime.jsx(ServiceAreasTable, {})
570
+ const BasePricingPage = () => {
571
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-6", children: [
572
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
573
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "ฐานราคาตามระยะทาง (Messenger)" }),
574
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "ตั้งราคาฐานสำหรับบริการ Messenger 3 ชั่วโมงตามช่วงระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
575
+ ] }),
576
+ /* @__PURE__ */ jsxRuntime.jsx(MessengerBasePricingTable, {})
672
577
  ] }) });
673
578
  };
674
579
  const config$4 = adminSdk.defineRouteConfig({
675
- icon: icons.MapPin,
676
- label: "พื้นที่บริการ"
580
+ icon: icons.Directions,
581
+ label: "ฐานราคาตามระยะทาง"
677
582
  });
678
- const MessengerBaseRateModal = ({
679
- rate,
680
- onClose
681
- }) => {
583
+ const ParcelBoxModal = ({ box, onClose }) => {
682
584
  const [formData, setFormData] = react.useState({
683
- min_distance_km: "0",
684
- max_distance_km: "",
685
- price: "",
686
- priority: "0",
585
+ name: "",
586
+ width_cm: "",
587
+ length_cm: "",
588
+ height_cm: "",
589
+ max_weight_kg: "",
590
+ price_thb: "",
687
591
  active: true
688
592
  });
689
593
  const [isSaving, setIsSaving] = react.useState(false);
690
594
  react.useEffect(() => {
691
- var _a, _b;
692
- if (rate) {
595
+ if (box) {
693
596
  setFormData({
694
- min_distance_km: ((_a = rate.min_distance_km) == null ? void 0 : _a.toString()) ?? "0",
695
- max_distance_km: ((_b = rate.max_distance_km) == null ? void 0 : _b.toString()) || "",
696
- price: rate.price.toString(),
697
- priority: (rate.priority ?? 0).toString(),
698
- active: rate.active
597
+ name: box.name,
598
+ width_cm: box.width_cm.toString(),
599
+ length_cm: box.length_cm.toString(),
600
+ height_cm: box.height_cm.toString(),
601
+ max_weight_kg: box.max_weight_kg.toString(),
602
+ price_thb: box.price_thb.toString(),
603
+ active: box.active
699
604
  });
700
605
  }
701
- }, [rate]);
606
+ }, [box]);
702
607
  const handleSubmit = async (e) => {
703
608
  e.preventDefault();
704
609
  const errors = [];
705
- const minDistance = parseFloat(formData.min_distance_km || "0");
706
- const hasMaxDistance = formData.max_distance_km !== "";
707
- const maxDistance = hasMaxDistance ? parseFloat(formData.max_distance_km) : void 0;
708
- const price = parseFloat(formData.price);
709
- const priority = parseInt(formData.priority || "0", 10);
710
- if (isNaN(minDistance) || minDistance < 0) {
711
- errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
610
+ if (!formData.name.trim()) {
611
+ errors.push("กรุณากรอกชื่อกล่อง");
712
612
  }
713
- if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
714
- errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
613
+ const width = parseFloat(formData.width_cm);
614
+ const length = parseFloat(formData.length_cm);
615
+ const height = parseFloat(formData.height_cm);
616
+ const maxWeight = parseFloat(formData.max_weight_kg);
617
+ const price = parseFloat(formData.price_thb);
618
+ if (isNaN(width) || width <= 0) {
619
+ errors.push("ความกว้างต้องเป็นตัวเลขมากกว่า 0");
620
+ }
621
+ if (isNaN(length) || length <= 0) {
622
+ errors.push("ความยาวต้องเป็นตัวเลขมากกว่า 0");
623
+ }
624
+ if (isNaN(height) || height <= 0) {
625
+ errors.push("ความสูงต้องเป็นตัวเลขมากกว่า 0");
626
+ }
627
+ if (isNaN(maxWeight) || maxWeight <= 0) {
628
+ errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
715
629
  }
716
630
  if (isNaN(price) || price < 0) {
717
631
  errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
718
632
  }
719
- if (isNaN(priority)) {
720
- errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
721
- }
722
633
  if (errors.length > 0) {
723
634
  ui.toast.error("ข้อมูลไม่ถูกต้อง", {
724
635
  description: errors.join(", ")
@@ -728,32 +639,31 @@ const MessengerBaseRateModal = ({
728
639
  setIsSaving(true);
729
640
  try {
730
641
  const payload = {
731
- carrier_type: "COMPANY_FLEET",
732
- carrier: "COMPANY_FLEET",
733
- service_code: "MESSENGER_3H",
734
- min_distance_km: minDistance,
735
- max_distance_km: maxDistance === void 0 || isNaN(maxDistance) ? null : maxDistance,
736
- price,
737
- currency: "THB",
738
- priority,
642
+ name: formData.name.trim(),
643
+ width_cm: width,
644
+ length_cm: length,
645
+ height_cm: height,
646
+ max_weight_kg: maxWeight,
647
+ price_thb: price,
739
648
  active: formData.active
740
649
  };
741
- const url = rate ? `/admin/base-shipping-prices/${rate.id}` : "/admin/base-shipping-prices";
742
- const method = rate ? "PUT" : "POST";
650
+ const url = box ? `/admin/boxes/${box.id}` : "/admin/boxes";
651
+ const method = box ? "PUT" : "POST";
743
652
  const response = await fetch(url, {
744
653
  method,
745
654
  headers: { "Content-Type": "application/json" },
746
655
  credentials: "include",
747
656
  body: JSON.stringify(payload)
748
657
  });
749
- if (!response.ok) {
750
- const error = await response.json().catch(() => ({}));
751
- throw new Error(error.message || "ไม่สามารถบันทึกข้อมูลได้");
658
+ if (response.ok) {
659
+ ui.toast.success("สำเร็จ", {
660
+ description: box ? "แก้ไขกล่องพัสดุแล้ว" : "สร้างกล่องพัสดุใหม่แล้ว"
661
+ });
662
+ onClose();
663
+ } else {
664
+ const error = await response.json();
665
+ throw new Error(error.message || "Failed to save");
752
666
  }
753
- ui.toast.success("สำเร็จ", {
754
- description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
755
- });
756
- onClose();
757
667
  } catch (error) {
758
668
  ui.toast.error("ข้อผิดพลาด", {
759
669
  description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
@@ -763,69 +673,97 @@ const MessengerBaseRateModal = ({
763
673
  }
764
674
  };
765
675
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
766
- /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: rate ? "แก้ไขฐานราคาตามระยะทาง" : "สร้างฐานราคาตามระยะทาง" }) }),
676
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: box ? "แก้ไขกล่องพัสดุ" : "สร้างกล่องพัสดุใหม่" }) }),
767
677
  /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
768
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
678
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
679
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "name", children: "ชื่อกล่อง *" }),
680
+ /* @__PURE__ */ jsxRuntime.jsx(
681
+ ui.Input,
682
+ {
683
+ id: "name",
684
+ value: formData.name,
685
+ onChange: (e) => setFormData({ ...formData, name: e.target.value }),
686
+ placeholder: "เช่น กล่อง S, กล่อง M",
687
+ required: true
688
+ }
689
+ )
690
+ ] }),
691
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-x-4", children: [
769
692
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
770
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "min_distance_km", children: "ระยะทางเริ่มต้น (กม.) *" }),
693
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "width", children: "ความกว้าง (cm) *" }),
771
694
  /* @__PURE__ */ jsxRuntime.jsx(
772
695
  ui.Input,
773
696
  {
774
- id: "min_distance_km",
697
+ id: "width",
775
698
  type: "number",
776
- min: "0",
777
699
  step: "0.1",
778
- value: formData.min_distance_km,
779
- onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
700
+ value: formData.width_cm,
701
+ onChange: (e) => setFormData({ ...formData, width_cm: e.target.value }),
702
+ placeholder: "20",
780
703
  required: true
781
704
  }
782
705
  )
783
706
  ] }),
784
707
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
785
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "max_distance_km", children: "ระยะทางสูงสุด (กม.)" }),
708
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "length", children: "ความยาว (cm) *" }),
786
709
  /* @__PURE__ */ jsxRuntime.jsx(
787
710
  ui.Input,
788
711
  {
789
- id: "max_distance_km",
712
+ id: "length",
790
713
  type: "number",
791
- min: "0",
792
714
  step: "0.1",
793
- placeholder: "ปล่อยว่างหากไม่มีที่สิ้นสุด",
794
- value: formData.max_distance_km,
795
- onChange: (e) => setFormData({ ...formData, max_distance_km: e.target.value })
715
+ value: formData.length_cm,
716
+ onChange: (e) => setFormData({ ...formData, length_cm: e.target.value }),
717
+ placeholder: "30",
718
+ required: true
796
719
  }
797
- ),
798
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
720
+ )
721
+ ] }),
722
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
723
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "height", children: "ความสูง (cm) *" }),
724
+ /* @__PURE__ */ jsxRuntime.jsx(
725
+ ui.Input,
726
+ {
727
+ id: "height",
728
+ type: "number",
729
+ step: "0.1",
730
+ value: formData.height_cm,
731
+ onChange: (e) => setFormData({ ...formData, height_cm: e.target.value }),
732
+ placeholder: "15",
733
+ required: true
734
+ }
735
+ )
799
736
  ] })
800
737
  ] }),
801
738
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
802
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
739
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
803
740
  /* @__PURE__ */ jsxRuntime.jsx(
804
741
  ui.Input,
805
742
  {
806
- id: "price",
743
+ id: "maxWeight",
807
744
  type: "number",
808
- min: "0",
809
- step: "0.01",
810
- value: formData.price,
811
- onChange: (e) => setFormData({ ...formData, price: e.target.value }),
745
+ step: "0.1",
746
+ value: formData.max_weight_kg,
747
+ onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
748
+ placeholder: "0.5",
812
749
  required: true
813
750
  }
814
751
  )
815
752
  ] }),
816
753
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
817
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
754
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
818
755
  /* @__PURE__ */ jsxRuntime.jsx(
819
756
  ui.Input,
820
757
  {
821
- id: "priority",
758
+ id: "price",
822
759
  type: "number",
823
- value: formData.priority,
824
- onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
825
- placeholder: "0"
760
+ step: "0.01",
761
+ value: formData.price_thb,
762
+ onChange: (e) => setFormData({ ...formData, price_thb: e.target.value }),
763
+ placeholder: "10.00",
764
+ required: true
826
765
  }
827
- ),
828
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
766
+ )
829
767
  ] }),
830
768
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
831
769
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -839,163 +777,197 @@ const MessengerBaseRateModal = ({
839
777
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
840
778
  ] }),
841
779
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
842
- /* @__PURE__ */ jsxRuntime.jsx(
843
- ui.Button,
844
- {
845
- type: "button",
846
- variant: "secondary",
847
- onClick: onClose,
848
- disabled: isSaving,
849
- children: "ยกเลิก"
850
- }
851
- ),
780
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
852
781
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
853
782
  ] })
854
783
  ] }) })
855
784
  ] }) });
856
785
  };
857
- const MessengerBasePricingTable = () => {
858
- const [rates, setRates] = react.useState([]);
859
- const [isLoading, setIsLoading] = react.useState(false);
786
+ const ParcelBoxesTable = () => {
787
+ const [boxes, setBoxes] = react.useState([]);
788
+ const [filteredBoxes, setFilteredBoxes] = react.useState([]);
789
+ const [searchTerm, setSearchTerm] = react.useState("");
860
790
  const [isModalOpen, setIsModalOpen] = react.useState(false);
861
- const [editingRate, setEditingRate] = react.useState(null);
862
- const [deleteId, setDeleteId] = react.useState(null);
791
+ const [editingBox, setEditingBox] = react.useState(null);
792
+ const [isLoading, setIsLoading] = react.useState(false);
793
+ const [deleteBoxId, setDeleteBoxId] = react.useState(null);
794
+ const [deleteBoxName, setDeleteBoxName] = react.useState("");
863
795
  react.useEffect(() => {
864
- fetchRates();
796
+ fetchBoxes();
865
797
  }, []);
866
- const orderedRates = react.useMemo(() => {
867
- return [...rates].sort((a, b) => {
868
- if (a.min_distance_km !== b.min_distance_km) {
869
- return a.min_distance_km - b.min_distance_km;
870
- }
871
- const aMax = a.max_distance_km ?? Number.POSITIVE_INFINITY;
872
- const bMax = b.max_distance_km ?? Number.POSITIVE_INFINITY;
873
- return aMax - bMax;
874
- });
875
- }, [rates]);
876
- const fetchRates = async () => {
798
+ react.useEffect(() => {
799
+ const filtered = boxes.filter(
800
+ (box) => box.name.toLowerCase().includes(searchTerm.toLowerCase())
801
+ );
802
+ setFilteredBoxes(filtered);
803
+ }, [searchTerm, boxes]);
804
+ const fetchBoxes = async () => {
877
805
  setIsLoading(true);
878
806
  try {
879
- const response = await fetch(
880
- "/admin/base-shipping-prices?carrier_type=COMPANY_FLEET&service_code=MESSENGER_3H",
881
- {
882
- credentials: "include"
883
- }
884
- );
885
- if (!response.ok) {
886
- throw new Error("failed_to_load_base_rates");
887
- }
807
+ const response = await fetch("/admin/boxes", {
808
+ credentials: "include"
809
+ });
888
810
  const data = await response.json();
889
- setRates(data.base_rates || []);
811
+ setBoxes(data.boxes || []);
890
812
  } catch (error) {
891
813
  ui.toast.error("ข้อผิดพลาด", {
892
- description: "ไม่สามารถโหลดฐานราคาตามระยะทางได้"
814
+ description: "ไม่สามารถโหลดข้อมูลกล่องพัสดุได้"
893
815
  });
894
816
  } finally {
895
817
  setIsLoading(false);
896
818
  }
897
819
  };
898
- const handleDelete = async () => {
899
- if (!deleteId) return;
820
+ const handleToggleActive = async (box) => {
900
821
  try {
901
- const response = await fetch(
902
- `/admin/base-shipping-prices/${deleteId}`,
903
- {
904
- method: "DELETE",
905
- credentials: "include"
906
- }
907
- );
908
- if (!response.ok) {
909
- throw new Error("failed_to_delete");
822
+ const response = await fetch(`/admin/boxes/${box.id}`, {
823
+ method: "PUT",
824
+ headers: { "Content-Type": "application/json" },
825
+ credentials: "include",
826
+ body: JSON.stringify({ ...box, active: !box.active })
827
+ });
828
+ if (response.ok) {
829
+ ui.toast.success("สำเร็จ", {
830
+ description: `${box.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}กล่อง ${box.name} แล้ว`
831
+ });
832
+ fetchBoxes();
833
+ } else {
834
+ throw new Error("Failed to update");
910
835
  }
911
- ui.toast.success("สำเร็จ", {
912
- description: "ลบฐานราคาสำเร็จ"
836
+ } catch (error) {
837
+ ui.toast.error("ข้อผิดพลาด", {
838
+ description: "ไม่สามารถอัพเดทสถานะได้"
913
839
  });
914
- fetchRates();
840
+ }
841
+ };
842
+ const handleDelete = async () => {
843
+ if (!deleteBoxId) return;
844
+ try {
845
+ const response = await fetch(`/admin/boxes/${deleteBoxId}`, {
846
+ method: "DELETE",
847
+ credentials: "include"
848
+ });
849
+ if (response.ok) {
850
+ ui.toast.success("สำเร็จ", {
851
+ description: `ลบกล่อง ${deleteBoxName} แล้ว`
852
+ });
853
+ fetchBoxes();
854
+ } else {
855
+ throw new Error("Failed to delete");
856
+ }
915
857
  } catch (error) {
916
858
  ui.toast.error("ข้อผิดพลาด", {
917
- description: "ไม่สามารถลบฐานราคาได้"
859
+ description: "ไม่สามารถลบกล่องได้"
918
860
  });
919
861
  } finally {
920
- setDeleteId(null);
862
+ setDeleteBoxId(null);
863
+ setDeleteBoxName("");
921
864
  }
922
865
  };
866
+ const handleEdit = (box) => {
867
+ setEditingBox(box);
868
+ setIsModalOpen(true);
869
+ };
870
+ const handleCreateNew = () => {
871
+ setEditingBox(null);
872
+ setIsModalOpen(true);
873
+ };
923
874
  const handleModalClose = () => {
924
875
  setIsModalOpen(false);
925
- setEditingRate(null);
926
- fetchRates();
876
+ setEditingBox(null);
877
+ fetchBoxes();
927
878
  };
928
- const formatDistanceRange = (rate) => {
929
- const min = rate.min_distance_km ?? 0;
930
- const max = rate.max_distance_km;
931
- if (max === void 0 || max === null) {
932
- return `${min}+ กม.`;
933
- }
934
- return `${min}-${max} กม.`;
879
+ const openDeletePrompt = (id, name) => {
880
+ setDeleteBoxId(id);
881
+ setDeleteBoxName(name);
935
882
  };
936
883
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
937
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
938
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
939
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: "ฐานราคาตามระยะทาง (Messenger 3H)" }),
940
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-fg-subtle text-sm", children: "กำหนดราคาเริ่มต้นตามระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
884
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
885
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
886
+ /* @__PURE__ */ jsxRuntime.jsx(
887
+ ui.Input,
888
+ {
889
+ type: "search",
890
+ placeholder: "ค้นหาชื่อกล่อง...",
891
+ value: searchTerm,
892
+ onChange: (e) => setSearchTerm(e.target.value),
893
+ className: "max-w-md"
894
+ }
895
+ ),
896
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
897
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
898
+ "สร้างกล่องใหม่"
899
+ ] })
941
900
  ] }),
942
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: () => setIsModalOpen(true), children: [
943
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
944
- "เพิ่มช่วงราคา"
945
- ] })
946
- ] }),
947
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { className: "mt-4", children: [
948
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
949
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ช่วงระยะทาง" }),
950
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
951
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ลำดับ" }),
952
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
953
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
954
- ] }) }),
955
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : orderedRates.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ยังไม่ได้ตั้งค่าฐานราคาตามระยะทาง" }) }) : orderedRates.map((rate) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
956
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatDistanceRange(rate) }),
957
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
958
- "฿",
959
- rate.price.toFixed(2)
960
- ] }),
961
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: rate.priority ?? 0 }),
962
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
963
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
964
- /* @__PURE__ */ jsxRuntime.jsx(
965
- ui.Button,
966
- {
967
- variant: "transparent",
968
- size: "small",
969
- onClick: () => {
970
- setEditingRate(rate);
971
- setIsModalOpen(true);
972
- },
973
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
974
- }
975
- ),
976
- /* @__PURE__ */ jsxRuntime.jsx(
977
- ui.Button,
901
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
902
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
903
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ชื่อ" }),
904
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ขนาด (กว้าง×ยาว×สูง cm)" }),
905
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "น้ำหนักสูงสุด (kg)" }),
906
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
907
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
908
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
909
+ ] }) }),
910
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredBoxes.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลกล่องพัสดุ" }) }) : filteredBoxes.map((box) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
911
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.name }),
912
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
913
+ box.width_cm,
914
+ " × ",
915
+ box.length_cm,
916
+ " × ",
917
+ box.height_cm
918
+ ] }),
919
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.max_weight_kg }),
920
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.price_thb.toFixed(2) }),
921
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
922
+ ui.Badge,
978
923
  {
979
- variant: "transparent",
980
- size: "small",
981
- onClick: () => setDeleteId(rate.id),
982
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
924
+ color: box.active ? "green" : "grey",
925
+ className: "cursor-pointer",
926
+ onClick: () => handleToggleActive(box),
927
+ children: box.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
983
928
  }
984
- )
985
- ] }) })
986
- ] }, rate.id)) })
929
+ ) }),
930
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
931
+ /* @__PURE__ */ jsxRuntime.jsx(
932
+ ui.Button,
933
+ {
934
+ variant: "transparent",
935
+ size: "small",
936
+ onClick: () => handleEdit(box),
937
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
938
+ }
939
+ ),
940
+ /* @__PURE__ */ jsxRuntime.jsx(
941
+ ui.Button,
942
+ {
943
+ variant: "transparent",
944
+ size: "small",
945
+ onClick: () => openDeletePrompt(box.id, box.name),
946
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
947
+ }
948
+ )
949
+ ] }) })
950
+ ] }, box.id)) })
951
+ ] })
987
952
  ] }),
988
- isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(MessengerBaseRateModal, { rate: editingRate, onClose: handleModalClose }),
953
+ isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ParcelBoxModal, { box: editingBox, onClose: handleModalClose }),
989
954
  /* @__PURE__ */ jsxRuntime.jsx(
990
955
  ui.Prompt,
991
956
  {
992
957
  variant: "confirmation",
993
- open: !!deleteId,
994
- onOpenChange: () => setDeleteId(null),
958
+ open: !!deleteBoxId,
959
+ onOpenChange: () => {
960
+ setDeleteBoxId(null);
961
+ setDeleteBoxName("");
962
+ },
995
963
  children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
996
964
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
997
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบฐานราคา" }),
998
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: "คุณต้องการลบช่วงราคานี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
965
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบกล่องพัสดุ" }),
966
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
967
+ 'คุณต้องการลบกล่อง "',
968
+ deleteBoxName,
969
+ '" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
970
+ ] })
999
971
  ] }),
1000
972
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
1001
973
  /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "ยกเลิก" }),
@@ -1006,68 +978,126 @@ const MessengerBasePricingTable = () => {
1006
978
  )
1007
979
  ] });
1008
980
  };
1009
- const BasePricingPage = () => {
1010
- return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-6", children: [
1011
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
1012
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "ฐานราคาตามระยะทาง (Messenger)" }),
1013
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "ตั้งราคาฐานสำหรับบริการ Messenger 3 ชั่วโมงตามช่วงระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
1014
- ] }),
1015
- /* @__PURE__ */ jsxRuntime.jsx(MessengerBasePricingTable, {})
981
+ const BoxesPage = () => {
982
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
983
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "กล่องพัสดุ" }) }),
984
+ /* @__PURE__ */ jsxRuntime.jsx(ParcelBoxesTable, {})
1016
985
  ] }) });
1017
986
  };
1018
987
  const config$3 = adminSdk.defineRouteConfig({
1019
- icon: icons.Route,
1020
- label: "ฐานราคาตามระยะทาง"
988
+ icon: icons.ArchiveBox,
989
+ label: "กล่องพัสดุ"
1021
990
  });
1022
- const ParcelBoxModal = ({ box, onClose }) => {
991
+ const TH_PROVINCES = [
992
+ "กรุงเทพมหานคร",
993
+ "กระบี่",
994
+ "กาญจนบุรี",
995
+ "กาฬสินธุ์",
996
+ "กำแพงเพชร",
997
+ "ขอนแก่น",
998
+ "จันทบุรี",
999
+ "ฉะเชิงเทรา",
1000
+ "ชลบุรี",
1001
+ "ชัยนาท",
1002
+ "ชัยภูมิ",
1003
+ "ชุมพร",
1004
+ "เชียงราย",
1005
+ "เชียงใหม่",
1006
+ "ตรัง",
1007
+ "ตราด",
1008
+ "ตาก",
1009
+ "นครนายก",
1010
+ "นครปฐม",
1011
+ "นครพนม",
1012
+ "นครราชสีมา",
1013
+ "นครศรีธรรมราช",
1014
+ "นครสวรรค์",
1015
+ "นนทบุรี",
1016
+ "นราธิวาส",
1017
+ "น่าน",
1018
+ "บึงกาฬ",
1019
+ "บุรีรัมย์",
1020
+ "ปทุมธานี",
1021
+ "ประจวบคีรีขันธ์",
1022
+ "ปราจีนบุรี",
1023
+ "ปัตตานี",
1024
+ "พระนครศรีอยุธยา",
1025
+ "พะเยา",
1026
+ "พังงา",
1027
+ "พัทลุง",
1028
+ "พิจิตร",
1029
+ "พิษณุโลก",
1030
+ "เพชรบุรี",
1031
+ "เพชรบูรณ์",
1032
+ "แพร่",
1033
+ "ภูเก็ต",
1034
+ "มหาสารคาม",
1035
+ "มุกดาหาร",
1036
+ "แม่ฮ่องสอน",
1037
+ "ยโสธร",
1038
+ "ยะลา",
1039
+ "ร้อยเอ็ด",
1040
+ "ระนอง",
1041
+ "ระยอง",
1042
+ "ราชบุรี",
1043
+ "ลพบุรี",
1044
+ "ลำปาง",
1045
+ "ลำพูน",
1046
+ "เลย",
1047
+ "ศรีสะเกษ",
1048
+ "สกลนคร",
1049
+ "สงขลา",
1050
+ "สตูล",
1051
+ "สมุทรปราการ",
1052
+ "สมุทรสงคราม",
1053
+ "สมุทรสาคร",
1054
+ "สระแก้ว",
1055
+ "สระบุรี",
1056
+ "สิงห์บุรี",
1057
+ "สุโขทัย",
1058
+ "สุพรรณบุรี",
1059
+ "สุราษฎร์ธานี",
1060
+ "สุรินทร์",
1061
+ "หนองคาย",
1062
+ "หนองบัวลำภู",
1063
+ "อ่างทอง",
1064
+ "อำนาจเจริญ",
1065
+ "อุดรธานี",
1066
+ "อุตรดิตถ์",
1067
+ "อุทัยธานี",
1068
+ "อุบลราชธานี"
1069
+ ];
1070
+ const ServiceAreaModal = ({ area, onClose }) => {
1023
1071
  const [formData, setFormData] = react.useState({
1024
- name: "",
1025
- width_cm: "",
1026
- length_cm: "",
1027
- height_cm: "",
1028
- max_weight_kg: "",
1029
- price_thb: "",
1072
+ kind: "PROVINCE",
1073
+ value: "",
1030
1074
  active: true
1031
1075
  });
1032
1076
  const [isSaving, setIsSaving] = react.useState(false);
1033
1077
  react.useEffect(() => {
1034
- if (box) {
1078
+ if (area) {
1035
1079
  setFormData({
1036
- name: box.name,
1037
- width_cm: box.width_cm.toString(),
1038
- length_cm: box.length_cm.toString(),
1039
- height_cm: box.height_cm.toString(),
1040
- max_weight_kg: box.max_weight_kg.toString(),
1041
- price_thb: box.price_thb.toString(),
1042
- active: box.active
1080
+ kind: area.kind,
1081
+ value: area.value,
1082
+ active: area.active
1043
1083
  });
1044
1084
  }
1045
- }, [box]);
1085
+ }, [area]);
1046
1086
  const handleSubmit = async (e) => {
1047
1087
  e.preventDefault();
1048
1088
  const errors = [];
1049
- if (!formData.name.trim()) {
1050
- errors.push("กรุณากรอกชื่อกล่อง");
1051
- }
1052
- const width = parseFloat(formData.width_cm);
1053
- const length = parseFloat(formData.length_cm);
1054
- const height = parseFloat(formData.height_cm);
1055
- const maxWeight = parseFloat(formData.max_weight_kg);
1056
- const price = parseFloat(formData.price_thb);
1057
- if (isNaN(width) || width <= 0) {
1058
- errors.push("ความกว้างต้องเป็นตัวเลขมากกว่า 0");
1059
- }
1060
- if (isNaN(length) || length <= 0) {
1061
- errors.push("ความยาวต้องเป็นตัวเลขมากกว่า 0");
1062
- }
1063
- if (isNaN(height) || height <= 0) {
1064
- errors.push("ความสูงต้องเป็นตัวเลขมากกว่า 0");
1065
- }
1066
- if (isNaN(maxWeight) || maxWeight <= 0) {
1067
- errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
1089
+ const trimmedValue = formData.value.trim();
1090
+ if (!trimmedValue) {
1091
+ errors.push("กรุณากรอกค่า");
1068
1092
  }
1069
- if (isNaN(price) || price < 0) {
1070
- errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
1093
+ if (formData.kind === "PROVINCE") {
1094
+ if (!TH_PROVINCES.includes(trimmedValue)) {
1095
+ errors.push("จังหวัดไม่ถูกต้อง กรุณาตรวจสอบการสะกดชื่อจังหวัด");
1096
+ }
1097
+ } else if (formData.kind === "POSTCODE_PREFIX") {
1098
+ if (!/^\d{1,5}$/.test(trimmedValue)) {
1099
+ errors.push("รหัสไปรษณีย์ต้องเป็นตัวเลข 1-5 หลัก");
1100
+ }
1071
1101
  }
1072
1102
  if (errors.length > 0) {
1073
1103
  ui.toast.error("ข้อมูลไม่ถูกต้อง", {
@@ -1078,16 +1108,12 @@ const ParcelBoxModal = ({ box, onClose }) => {
1078
1108
  setIsSaving(true);
1079
1109
  try {
1080
1110
  const payload = {
1081
- name: formData.name.trim(),
1082
- width_cm: width,
1083
- length_cm: length,
1084
- height_cm: height,
1085
- max_weight_kg: maxWeight,
1086
- price_thb: price,
1111
+ kind: formData.kind,
1112
+ value: trimmedValue,
1087
1113
  active: formData.active
1088
1114
  };
1089
- const url = box ? `/admin/boxes/${box.id}` : "/admin/boxes";
1090
- const method = box ? "PUT" : "POST";
1115
+ const url = area ? `/admin/service-areas/${area.id}` : "/admin/service-areas";
1116
+ const method = area ? "PUT" : "POST";
1091
1117
  const response = await fetch(url, {
1092
1118
  method,
1093
1119
  headers: { "Content-Type": "application/json" },
@@ -1096,7 +1122,7 @@ const ParcelBoxModal = ({ box, onClose }) => {
1096
1122
  });
1097
1123
  if (response.ok) {
1098
1124
  ui.toast.success("สำเร็จ", {
1099
- description: box ? "แก้ไขกล่องพัสดุแล้ว" : "สร้างกล่องพัสดุใหม่แล้ว"
1125
+ description: area ? "แก้ไขพื้นที่บริการแล้ว" : "สร้างพื้นที่บริการใหม่แล้ว"
1100
1126
  });
1101
1127
  onClose();
1102
1128
  } else {
@@ -1112,97 +1138,53 @@ const ParcelBoxModal = ({ box, onClose }) => {
1112
1138
  }
1113
1139
  };
1114
1140
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
1115
- /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: box ? "แก้ไขกล่องพัสดุ" : "สร้างกล่องพัสดุใหม่" }) }),
1141
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: area ? "แก้ไขพื้นที่บริการ" : "สร้างพื้นที่บริการใหม่" }) }),
1116
1142
  /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
1117
1143
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1118
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "name", children: "ชื่อกล่อง *" }),
1119
- /* @__PURE__ */ jsxRuntime.jsx(
1120
- ui.Input,
1121
- {
1122
- id: "name",
1123
- value: formData.name,
1124
- onChange: (e) => setFormData({ ...formData, name: e.target.value }),
1125
- placeholder: "เช่น กล่อง S, กล่อง M",
1126
- required: true
1127
- }
1128
- )
1129
- ] }),
1130
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-x-4", children: [
1131
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1132
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "width", children: "ความกว้าง (cm) *" }),
1133
- /* @__PURE__ */ jsxRuntime.jsx(
1134
- ui.Input,
1135
- {
1136
- id: "width",
1137
- type: "number",
1138
- step: "0.1",
1139
- value: formData.width_cm,
1140
- onChange: (e) => setFormData({ ...formData, width_cm: e.target.value }),
1141
- placeholder: "20",
1142
- required: true
1143
- }
1144
- )
1145
- ] }),
1146
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1147
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "length", children: "ความยาว (cm) *" }),
1148
- /* @__PURE__ */ jsxRuntime.jsx(
1149
- ui.Input,
1150
- {
1151
- id: "length",
1152
- type: "number",
1153
- step: "0.1",
1154
- value: formData.length_cm,
1155
- onChange: (e) => setFormData({ ...formData, length_cm: e.target.value }),
1156
- placeholder: "30",
1157
- required: true
1158
- }
1159
- )
1160
- ] }),
1161
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1162
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "height", children: "ความสูง (cm) *" }),
1163
- /* @__PURE__ */ jsxRuntime.jsx(
1164
- ui.Input,
1165
- {
1166
- id: "height",
1167
- type: "number",
1168
- step: "0.1",
1169
- value: formData.height_cm,
1170
- onChange: (e) => setFormData({ ...formData, height_cm: e.target.value }),
1171
- placeholder: "15",
1172
- required: true
1173
- }
1174
- )
1175
- ] })
1176
- ] }),
1177
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1178
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
1179
- /* @__PURE__ */ jsxRuntime.jsx(
1180
- ui.Input,
1144
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "kind", children: "ประเภท *" }),
1145
+ /* @__PURE__ */ jsxRuntime.jsxs(
1146
+ ui.Select,
1181
1147
  {
1182
- id: "maxWeight",
1183
- type: "number",
1184
- step: "0.1",
1185
- value: formData.max_weight_kg,
1186
- onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
1187
- placeholder: "0.5",
1188
- required: true
1148
+ value: formData.kind,
1149
+ onValueChange: (value) => setFormData({ ...formData, kind: value, value: "" }),
1150
+ required: true,
1151
+ children: [
1152
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกประเภท" }) }),
1153
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
1154
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "PROVINCE", children: "จังหวัด" }),
1155
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "POSTCODE_PREFIX", children: "รหัสไปรษณีย์" })
1156
+ ] })
1157
+ ]
1189
1158
  }
1190
1159
  )
1191
1160
  ] }),
1192
1161
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1193
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
1194
- /* @__PURE__ */ jsxRuntime.jsx(
1162
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "value", children: formData.kind === "PROVINCE" ? "ชื่อจังหวัด *" : "รหัสไปรษณีย์ (1-5 หลัก) *" }),
1163
+ formData.kind === "PROVINCE" ? /* @__PURE__ */ jsxRuntime.jsxs(
1164
+ ui.Select,
1165
+ {
1166
+ value: formData.value,
1167
+ onValueChange: (value) => setFormData({ ...formData, value }),
1168
+ required: true,
1169
+ children: [
1170
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "เลือกจังหวัด" }) }),
1171
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: TH_PROVINCES.map((province) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: province, children: province }, province)) })
1172
+ ]
1173
+ }
1174
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1195
1175
  ui.Input,
1196
1176
  {
1197
- id: "price",
1198
- type: "number",
1199
- step: "0.01",
1200
- value: formData.price_thb,
1201
- onChange: (e) => setFormData({ ...formData, price_thb: e.target.value }),
1202
- placeholder: "10.00",
1177
+ id: "value",
1178
+ type: "text",
1179
+ value: formData.value,
1180
+ onChange: (e) => setFormData({ ...formData, value: e.target.value }),
1181
+ placeholder: "เช่น 10, 102, 10200",
1182
+ maxLength: 5,
1183
+ pattern: "\\d{1,5}",
1203
1184
  required: true
1204
1185
  }
1205
- )
1186
+ ),
1187
+ formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
1206
1188
  ] }),
1207
1189
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
1208
1190
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1216,59 +1198,81 @@ const ParcelBoxModal = ({ box, onClose }) => {
1216
1198
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
1217
1199
  ] }),
1218
1200
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
1219
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
1201
+ /* @__PURE__ */ jsxRuntime.jsx(
1202
+ ui.Button,
1203
+ {
1204
+ type: "button",
1205
+ variant: "secondary",
1206
+ onClick: onClose,
1207
+ disabled: isSaving,
1208
+ children: "ยกเลิก"
1209
+ }
1210
+ ),
1220
1211
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
1221
1212
  ] })
1222
1213
  ] }) })
1223
1214
  ] }) });
1224
1215
  };
1225
- const ParcelBoxesTable = () => {
1226
- const [boxes, setBoxes] = react.useState([]);
1227
- const [filteredBoxes, setFilteredBoxes] = react.useState([]);
1216
+ const AREA_KINDS = [
1217
+ { value: "ALL", label: "ทั้งหมด" },
1218
+ { value: "PROVINCE", label: "จังหวัด" },
1219
+ { value: "POSTCODE_PREFIX", label: "รหัสไปรษณีย์" }
1220
+ ];
1221
+ const ServiceAreasTable = () => {
1222
+ const [areas, setAreas] = react.useState([]);
1223
+ const [filteredAreas, setFilteredAreas] = react.useState([]);
1228
1224
  const [searchTerm, setSearchTerm] = react.useState("");
1225
+ const [kindFilter, setKindFilter] = react.useState("ALL");
1229
1226
  const [isModalOpen, setIsModalOpen] = react.useState(false);
1230
- const [editingBox, setEditingBox] = react.useState(null);
1227
+ const [editingArea, setEditingArea] = react.useState(null);
1231
1228
  const [isLoading, setIsLoading] = react.useState(false);
1232
- const [deleteBoxId, setDeleteBoxId] = react.useState(null);
1233
- const [deleteBoxName, setDeleteBoxName] = react.useState("");
1229
+ const [deleteAreaId, setDeleteAreaId] = react.useState(null);
1230
+ const [deleteAreaValue, setDeleteAreaValue] = react.useState("");
1234
1231
  react.useEffect(() => {
1235
- fetchBoxes();
1232
+ fetchAreas();
1236
1233
  }, []);
1237
1234
  react.useEffect(() => {
1238
- const filtered = boxes.filter(
1239
- (box) => box.name.toLowerCase().includes(searchTerm.toLowerCase())
1240
- );
1241
- setFilteredBoxes(filtered);
1242
- }, [searchTerm, boxes]);
1243
- const fetchBoxes = async () => {
1235
+ let filtered = areas;
1236
+ if (kindFilter !== "ALL") {
1237
+ filtered = filtered.filter((area) => area.kind === kindFilter);
1238
+ }
1239
+ if (searchTerm) {
1240
+ filtered = filtered.filter(
1241
+ (area) => area.value.toLowerCase().includes(searchTerm.toLowerCase())
1242
+ );
1243
+ }
1244
+ setFilteredAreas(filtered);
1245
+ }, [searchTerm, kindFilter, areas]);
1246
+ const fetchAreas = async () => {
1244
1247
  setIsLoading(true);
1245
1248
  try {
1246
- const response = await fetch("/admin/boxes", {
1249
+ const response = await fetch("/admin/service-areas", {
1247
1250
  credentials: "include"
1248
1251
  });
1249
1252
  const data = await response.json();
1250
- setBoxes(data.boxes || []);
1253
+ console.log("Fetched service areas:", data);
1254
+ setAreas(data.service_areas || []);
1251
1255
  } catch (error) {
1252
1256
  ui.toast.error("ข้อผิดพลาด", {
1253
- description: "ไม่สามารถโหลดข้อมูลกล่องพัสดุได้"
1257
+ description: "ไม่สามารถโหลดข้อมูลพื้นที่บริการได้"
1254
1258
  });
1255
1259
  } finally {
1256
1260
  setIsLoading(false);
1257
1261
  }
1258
1262
  };
1259
- const handleToggleActive = async (box) => {
1263
+ const handleToggleActive = async (area) => {
1260
1264
  try {
1261
- const response = await fetch(`/admin/boxes/${box.id}`, {
1265
+ const response = await fetch(`/admin/service-areas/${area.id}`, {
1262
1266
  method: "PUT",
1263
1267
  headers: { "Content-Type": "application/json" },
1264
1268
  credentials: "include",
1265
- body: JSON.stringify({ ...box, active: !box.active })
1269
+ body: JSON.stringify({ ...area, active: !area.active })
1266
1270
  });
1267
1271
  if (response.ok) {
1268
1272
  ui.toast.success("สำเร็จ", {
1269
- description: `${box.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}กล่อง ${box.name} แล้ว`
1273
+ description: `${area.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}พื้นที่ ${area.value} แล้ว`
1270
1274
  });
1271
- fetchBoxes();
1275
+ fetchAreas();
1272
1276
  } else {
1273
1277
  throw new Error("Failed to update");
1274
1278
  }
@@ -1279,91 +1283,87 @@ const ParcelBoxesTable = () => {
1279
1283
  }
1280
1284
  };
1281
1285
  const handleDelete = async () => {
1282
- if (!deleteBoxId) return;
1286
+ if (!deleteAreaId) return;
1283
1287
  try {
1284
- const response = await fetch(`/admin/boxes/${deleteBoxId}`, {
1288
+ const response = await fetch(`/admin/service-areas/${deleteAreaId}`, {
1285
1289
  method: "DELETE",
1286
1290
  credentials: "include"
1287
1291
  });
1288
1292
  if (response.ok) {
1289
1293
  ui.toast.success("สำเร็จ", {
1290
- description: `ลบกล่อง ${deleteBoxName} แล้ว`
1294
+ description: `ลบพื้นที่ ${deleteAreaValue} แล้ว`
1291
1295
  });
1292
- fetchBoxes();
1296
+ fetchAreas();
1293
1297
  } else {
1294
1298
  throw new Error("Failed to delete");
1295
1299
  }
1296
1300
  } catch (error) {
1297
1301
  ui.toast.error("ข้อผิดพลาด", {
1298
- description: "ไม่สามารถลบกล่องได้"
1302
+ description: "ไม่สามารถลบพื้นที่ได้"
1299
1303
  });
1300
1304
  } finally {
1301
- setDeleteBoxId(null);
1302
- setDeleteBoxName("");
1305
+ setDeleteAreaId(null);
1306
+ setDeleteAreaValue("");
1303
1307
  }
1304
1308
  };
1305
- const handleEdit = (box) => {
1306
- setEditingBox(box);
1309
+ const handleEdit = (area) => {
1310
+ setEditingArea(area);
1307
1311
  setIsModalOpen(true);
1308
1312
  };
1309
1313
  const handleCreateNew = () => {
1310
- setEditingBox(null);
1314
+ setEditingArea(null);
1311
1315
  setIsModalOpen(true);
1312
1316
  };
1313
1317
  const handleModalClose = () => {
1314
1318
  setIsModalOpen(false);
1315
- setEditingBox(null);
1316
- fetchBoxes();
1319
+ setEditingArea(null);
1320
+ fetchAreas();
1317
1321
  };
1318
- const openDeletePrompt = (id, name) => {
1319
- setDeleteBoxId(id);
1320
- setDeleteBoxName(name);
1322
+ const openDeletePrompt = (id, value) => {
1323
+ setDeleteAreaId(id);
1324
+ setDeleteAreaValue(value);
1321
1325
  };
1322
1326
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1323
1327
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
1324
1328
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
1325
- /* @__PURE__ */ jsxRuntime.jsx(
1326
- ui.Input,
1327
- {
1328
- type: "search",
1329
- placeholder: "ค้นหาชื่อกล่อง...",
1330
- value: searchTerm,
1331
- onChange: (e) => setSearchTerm(e.target.value),
1332
- className: "max-w-md"
1333
- }
1334
- ),
1329
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-4 flex-1", children: [
1330
+ /* @__PURE__ */ jsxRuntime.jsx(
1331
+ ui.Input,
1332
+ {
1333
+ type: "search",
1334
+ placeholder: "ค้นหาจังหวัดหรือรหัสไปรษณีย์...",
1335
+ value: searchTerm,
1336
+ onChange: (e) => setSearchTerm(e.target.value),
1337
+ className: "max-w-md"
1338
+ }
1339
+ ),
1340
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: kindFilter, onValueChange: setKindFilter, children: [
1341
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "ประเภท" }) }),
1342
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: AREA_KINDS.map((kind) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: kind.value, children: kind.label }, kind.value)) })
1343
+ ] })
1344
+ ] }),
1335
1345
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
1336
1346
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
1337
- "สร้างกล่องใหม่"
1347
+ "สร้างพื้นที่ใหม่"
1338
1348
  ] })
1339
1349
  ] }),
1340
1350
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
1341
1351
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1342
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ชื่อ" }),
1343
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ขนาด (กว้าง×ยาว×สูง cm)" }),
1344
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "น้ำหนักสูงสุด (kg)" }),
1345
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ราคา (THB)" }),
1352
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ประเภท" }),
1353
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ค่า" }),
1346
1354
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
1347
1355
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
1348
1356
  ] }) }),
1349
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : 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: [
1350
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.name }),
1351
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Cell, { children: [
1352
- box.width_cm,
1353
- " × ",
1354
- box.length_cm,
1355
- " × ",
1356
- box.height_cm
1357
- ] }),
1358
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.max_weight_kg }),
1359
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: box.price_thb.toFixed(2) }),
1357
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : filteredAreas.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Row, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-center", children: "ไม่พบข้อมูลพื้นที่บริการ" }) }) : filteredAreas.map((area) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1358
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: area.kind === "PROVINCE" ? "blue" : "purple", children: area.kind === "PROVINCE" ? "จังหวัด" : "รหัสไปรษณีย์" }) }),
1359
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: area.value }),
1360
1360
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
1361
1361
  ui.Badge,
1362
1362
  {
1363
- color: box.active ? "green" : "grey",
1363
+ color: area.active ? "green" : "grey",
1364
1364
  className: "cursor-pointer",
1365
- onClick: () => handleToggleActive(box),
1366
- children: box.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
1365
+ onClick: () => handleToggleActive(area),
1366
+ children: area.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
1367
1367
  }
1368
1368
  ) }),
1369
1369
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
@@ -1372,7 +1372,7 @@ const ParcelBoxesTable = () => {
1372
1372
  {
1373
1373
  variant: "transparent",
1374
1374
  size: "small",
1375
- onClick: () => handleEdit(box),
1375
+ onClick: () => handleEdit(area),
1376
1376
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
1377
1377
  }
1378
1378
  ),
@@ -1381,30 +1381,30 @@ const ParcelBoxesTable = () => {
1381
1381
  {
1382
1382
  variant: "transparent",
1383
1383
  size: "small",
1384
- onClick: () => openDeletePrompt(box.id, box.name),
1384
+ onClick: () => openDeletePrompt(area.id, area.value),
1385
1385
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
1386
1386
  }
1387
1387
  )
1388
1388
  ] }) })
1389
- ] }, box.id)) })
1389
+ ] }, area.id)) })
1390
1390
  ] })
1391
1391
  ] }),
1392
- isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ParcelBoxModal, { box: editingBox, onClose: handleModalClose }),
1392
+ isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ServiceAreaModal, { area: editingArea, onClose: handleModalClose }),
1393
1393
  /* @__PURE__ */ jsxRuntime.jsx(
1394
1394
  ui.Prompt,
1395
1395
  {
1396
1396
  variant: "confirmation",
1397
- open: !!deleteBoxId,
1397
+ open: !!deleteAreaId,
1398
1398
  onOpenChange: () => {
1399
- setDeleteBoxId(null);
1400
- setDeleteBoxName("");
1399
+ setDeleteAreaId(null);
1400
+ setDeleteAreaValue("");
1401
1401
  },
1402
1402
  children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
1403
1403
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
1404
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบกล่องพัสดุ" }),
1404
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบพื้นที่บริการ" }),
1405
1405
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
1406
- 'คุณต้องการลบกล่อง "',
1407
- deleteBoxName,
1406
+ 'คุณต้องการลบพื้นที่ "',
1407
+ deleteAreaValue,
1408
1408
  '" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
1409
1409
  ] })
1410
1410
  ] }),
@@ -1417,15 +1417,15 @@ const ParcelBoxesTable = () => {
1417
1417
  )
1418
1418
  ] });
1419
1419
  };
1420
- const BoxesPage = () => {
1420
+ const AreasPage = () => {
1421
1421
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
1422
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "กล่องพัสดุ" }) }),
1423
- /* @__PURE__ */ jsxRuntime.jsx(ParcelBoxesTable, {})
1422
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "พื้นที่บริการ" }) }),
1423
+ /* @__PURE__ */ jsxRuntime.jsx(ServiceAreasTable, {})
1424
1424
  ] }) });
1425
1425
  };
1426
1426
  const config$2 = adminSdk.defineRouteConfig({
1427
- icon: icons.ArchiveBox,
1428
- label: "กล่องพัสดุ"
1427
+ icon: icons.MapPin,
1428
+ label: "พื้นที่บริการ"
1429
1429
  });
1430
1430
  const CATEGORIES$1 = [
1431
1431
  { value: "PACKAGING", label: "วัสดุหีบห่อ" },
@@ -2378,10 +2378,6 @@ const widgetModule = { widgets: [
2378
2378
  ] };
2379
2379
  const routeModule = {
2380
2380
  routes: [
2381
- {
2382
- Component: AreasPage,
2383
- path: "/areas"
2384
- },
2385
2381
  {
2386
2382
  Component: BasePricingPage,
2387
2383
  path: "/base-pricing"
@@ -2390,6 +2386,10 @@ const routeModule = {
2390
2386
  Component: BoxesPage,
2391
2387
  path: "/boxes"
2392
2388
  },
2389
+ {
2390
+ Component: AreasPage,
2391
+ path: "/areas"
2392
+ },
2393
2393
  {
2394
2394
  Component: MaterialCostsPage,
2395
2395
  path: "/material-costs"
@@ -2402,15 +2402,21 @@ const routeModule = {
2402
2402
  };
2403
2403
  const menuItemModule = {
2404
2404
  menuItems: [
2405
+ {
2406
+ label: config$2.label,
2407
+ icon: config$2.icon,
2408
+ path: "/areas",
2409
+ nested: void 0
2410
+ },
2405
2411
  {
2406
2412
  label: config$4.label,
2407
2413
  icon: config$4.icon,
2408
- path: "/areas",
2414
+ path: "/base-pricing",
2409
2415
  nested: void 0
2410
2416
  },
2411
2417
  {
2412
- label: config$2.label,
2413
- icon: config$2.icon,
2418
+ label: config$3.label,
2419
+ icon: config$3.icon,
2414
2420
  path: "/boxes",
2415
2421
  nested: void 0
2416
2422
  },
@@ -2425,12 +2431,6 @@ const menuItemModule = {
2425
2431
  icon: config.icon,
2426
2432
  path: "/rates",
2427
2433
  nested: void 0
2428
- },
2429
- {
2430
- label: config$3.label,
2431
- icon: config$3.icon,
2432
- path: "/base-pricing",
2433
- nested: void 0
2434
2434
  }
2435
2435
  ]
2436
2436
  };