@lodashventure/medusa-parcel-shipping 0.4.30 → 0.4.34

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,49 +236,116 @@ const OrderShippingQuoteWidget = ({ data }) => {
236
236
  adminSdk.defineWidgetConfig({
237
237
  zone: "order.details.after"
238
238
  });
239
- const MessengerBaseRateModal = ({
240
- rate,
241
- onClose
242
- }) => {
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 }) => {
243
319
  const [formData, setFormData] = react.useState({
244
- min_distance_km: "0",
245
- max_distance_km: "",
246
- price: "",
247
- priority: "0",
320
+ kind: "PROVINCE",
321
+ value: "",
248
322
  active: true
249
323
  });
250
324
  const [isSaving, setIsSaving] = react.useState(false);
251
325
  react.useEffect(() => {
252
- var _a, _b;
253
- if (rate) {
326
+ if (area) {
254
327
  setFormData({
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
328
+ kind: area.kind,
329
+ value: area.value,
330
+ active: area.active
260
331
  });
261
332
  }
262
- }, [rate]);
333
+ }, [area]);
263
334
  const handleSubmit = async (e) => {
264
335
  e.preventDefault();
265
336
  const errors = [];
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("ระยะทางเริ่มต้นต้องไม่ติดลบ");
273
- }
274
- if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
275
- errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
276
- }
277
- if (isNaN(price) || price < 0) {
278
- errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
337
+ const trimmedValue = formData.value.trim();
338
+ if (!trimmedValue) {
339
+ errors.push("กรุณากรอกค่า");
279
340
  }
280
- if (isNaN(priority)) {
281
- errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
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
+ }
282
349
  }
283
350
  if (errors.length > 0) {
284
351
  ui.toast.error("ข้อมูลไม่ถูกต้อง", {
@@ -289,32 +356,27 @@ const MessengerBaseRateModal = ({
289
356
  setIsSaving(true);
290
357
  try {
291
358
  const payload = {
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,
359
+ kind: formData.kind,
360
+ value: trimmedValue,
300
361
  active: formData.active
301
362
  };
302
- const url = rate ? `/admin/base-shipping-prices/${rate.id}` : "/admin/base-shipping-prices";
303
- const method = rate ? "PUT" : "POST";
363
+ const url = area ? `/admin/service-areas/${area.id}` : "/admin/service-areas";
364
+ const method = area ? "PUT" : "POST";
304
365
  const response = await fetch(url, {
305
366
  method,
306
367
  headers: { "Content-Type": "application/json" },
307
368
  credentials: "include",
308
369
  body: JSON.stringify(payload)
309
370
  });
310
- if (!response.ok) {
311
- const error = await response.json().catch(() => ({}));
312
- throw new Error(error.message || "ไม่สามารถบันทึกข้อมูลได้");
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");
313
379
  }
314
- ui.toast.success("สำเร็จ", {
315
- description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
316
- });
317
- onClose();
318
380
  } catch (error) {
319
381
  ui.toast.error("ข้อผิดพลาด", {
320
382
  description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
@@ -324,69 +386,53 @@ const MessengerBaseRateModal = ({
324
386
  }
325
387
  };
326
388
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
327
- /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: rate ? "แก้ไขฐานราคาตามระยะทาง" : "สร้างฐานราคาตามระยะทาง" }) }),
389
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: area ? "แก้ไขพื้นที่บริการ" : "สร้างพื้นที่บริการใหม่" }) }),
328
390
  /* @__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
- ] }),
362
391
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
363
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
364
- /* @__PURE__ */ jsxRuntime.jsx(
365
- ui.Input,
392
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "kind", children: "ประเภท *" }),
393
+ /* @__PURE__ */ jsxRuntime.jsxs(
394
+ ui.Select,
366
395
  {
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
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
+ ]
374
406
  }
375
407
  )
376
408
  ] }),
377
409
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
378
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
379
- /* @__PURE__ */ jsxRuntime.jsx(
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(
380
423
  ui.Input,
381
424
  {
382
- id: "priority",
383
- type: "number",
384
- value: formData.priority,
385
- onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
386
- placeholder: "0"
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
387
433
  }
388
434
  ),
389
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
435
+ formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
390
436
  ] }),
391
437
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
392
438
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -415,148 +461,200 @@ const MessengerBaseRateModal = ({
415
461
  ] }) })
416
462
  ] }) });
417
463
  };
418
- const MessengerBasePricingTable = () => {
419
- const [rates, setRates] = react.useState([]);
420
- const [isLoading, setIsLoading] = react.useState(false);
421
- const [isModalOpen, setIsModalOpen] = react.useState(false);
422
- const [editingRate, setEditingRate] = react.useState(null);
423
- const [deleteId, setDeleteId] = react.useState(null);
424
- react.useEffect(() => {
425
- fetchRates();
426
- }, []);
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 () => {
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);
476
+ const [isLoading, setIsLoading] = react.useState(false);
477
+ const [deleteAreaId, setDeleteAreaId] = react.useState(null);
478
+ const [deleteAreaValue, setDeleteAreaValue] = react.useState("");
479
+ react.useEffect(() => {
480
+ fetchAreas();
481
+ }, []);
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 () => {
438
495
  setIsLoading(true);
439
496
  try {
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
- }
497
+ const response = await fetch("/admin/service-areas", {
498
+ credentials: "include"
499
+ });
449
500
  const data = await response.json();
450
- setRates(data.base_rates || []);
501
+ console.log("Fetched service areas:", data);
502
+ setAreas(data.service_areas || []);
451
503
  } catch (error) {
452
504
  ui.toast.error("ข้อผิดพลาด", {
453
- description: "ไม่สามารถโหลดฐานราคาตามระยะทางได้"
505
+ description: "ไม่สามารถโหลดข้อมูลพื้นที่บริการได้"
454
506
  });
455
507
  } finally {
456
508
  setIsLoading(false);
457
509
  }
458
510
  };
459
- const handleDelete = async () => {
460
- if (!deleteId) return;
511
+ const handleToggleActive = async (area) => {
461
512
  try {
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");
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");
471
526
  }
472
- ui.toast.success("สำเร็จ", {
473
- description: "ลบฐานราคาสำเร็จ"
527
+ } catch (error) {
528
+ ui.toast.error("ข้อผิดพลาด", {
529
+ description: "ไม่สามารถอัพเดทสถานะได้"
474
530
  });
475
- fetchRates();
531
+ }
532
+ };
533
+ const handleDelete = async () => {
534
+ if (!deleteAreaId) return;
535
+ 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");
547
+ }
476
548
  } catch (error) {
477
549
  ui.toast.error("ข้อผิดพลาด", {
478
- description: "ไม่สามารถลบฐานราคาได้"
550
+ description: "ไม่สามารถลบพื้นที่ได้"
479
551
  });
480
552
  } finally {
481
- setDeleteId(null);
553
+ setDeleteAreaId(null);
554
+ setDeleteAreaValue("");
482
555
  }
483
556
  };
557
+ const handleEdit = (area) => {
558
+ setEditingArea(area);
559
+ setIsModalOpen(true);
560
+ };
561
+ const handleCreateNew = () => {
562
+ setEditingArea(null);
563
+ setIsModalOpen(true);
564
+ };
484
565
  const handleModalClose = () => {
485
566
  setIsModalOpen(false);
486
- setEditingRate(null);
487
- fetchRates();
567
+ setEditingArea(null);
568
+ fetchAreas();
488
569
  };
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} กม.`;
570
+ const openDeletePrompt = (id, value) => {
571
+ setDeleteAreaId(id);
572
+ setDeleteAreaValue(value);
496
573
  };
497
574
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { 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: [
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: [
525
578
  /* @__PURE__ */ jsxRuntime.jsx(
526
- ui.Button,
579
+ ui.Input,
527
580
  {
528
- variant: "transparent",
529
- size: "small",
530
- onClick: () => {
531
- setEditingRate(rate);
532
- setIsModalOpen(true);
533
- },
534
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
581
+ type: "search",
582
+ placeholder: "ค้นหาจังหวัดหรือรหัสไปรษณีย์...",
583
+ value: searchTerm,
584
+ onChange: (e) => setSearchTerm(e.target.value),
585
+ className: "max-w-md"
535
586
  }
536
587
  ),
537
- /* @__PURE__ */ jsxRuntime.jsx(
538
- ui.Button,
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,
539
610
  {
540
- variant: "transparent",
541
- size: "small",
542
- onClick: () => setDeleteId(rate.id),
543
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
611
+ color: area.active ? "green" : "grey",
612
+ className: "cursor-pointer",
613
+ onClick: () => handleToggleActive(area),
614
+ children: area.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
544
615
  }
545
- )
546
- ] }) })
547
- ] }, rate.id)) })
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
+ ] })
548
639
  ] }),
549
- isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(MessengerBaseRateModal, { rate: editingRate, onClose: handleModalClose }),
640
+ isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ServiceAreaModal, { area: editingArea, onClose: handleModalClose }),
550
641
  /* @__PURE__ */ jsxRuntime.jsx(
551
642
  ui.Prompt,
552
643
  {
553
644
  variant: "confirmation",
554
- open: !!deleteId,
555
- onOpenChange: () => setDeleteId(null),
645
+ open: !!deleteAreaId,
646
+ onOpenChange: () => {
647
+ setDeleteAreaId(null);
648
+ setDeleteAreaValue("");
649
+ },
556
650
  children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
557
651
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
558
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบฐานราคา" }),
559
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: "คุณต้องการลบช่วงราคานี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
652
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบพื้นที่บริการ" }),
653
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
654
+ 'คุณต้องการลบพื้นที่ "',
655
+ deleteAreaValue,
656
+ '" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
657
+ ] })
560
658
  ] }),
561
659
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
562
660
  /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "ยกเลิก" }),
@@ -567,69 +665,60 @@ const MessengerBasePricingTable = () => {
567
665
  )
568
666
  ] });
569
667
  };
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, {})
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, {})
577
672
  ] }) });
578
673
  };
579
674
  const config$4 = adminSdk.defineRouteConfig({
580
- icon: icons.Directions,
581
- label: "ฐานราคาตามระยะทาง"
675
+ icon: icons.MapPin,
676
+ label: "พื้นที่บริการ"
582
677
  });
583
- const ParcelBoxModal = ({ box, onClose }) => {
678
+ const MessengerBaseRateModal = ({
679
+ rate,
680
+ onClose
681
+ }) => {
584
682
  const [formData, setFormData] = react.useState({
585
- name: "",
586
- width_cm: "",
587
- length_cm: "",
588
- height_cm: "",
589
- max_weight_kg: "",
590
- price_thb: "",
683
+ min_distance_km: "0",
684
+ max_distance_km: "",
685
+ price: "",
686
+ priority: "0",
591
687
  active: true
592
688
  });
593
689
  const [isSaving, setIsSaving] = react.useState(false);
594
690
  react.useEffect(() => {
595
- if (box) {
691
+ var _a, _b;
692
+ if (rate) {
596
693
  setFormData({
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
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
604
699
  });
605
700
  }
606
- }, [box]);
701
+ }, [rate]);
607
702
  const handleSubmit = async (e) => {
608
703
  e.preventDefault();
609
704
  const errors = [];
610
- if (!formData.name.trim()) {
611
- errors.push("กรุณากรอกชื่อกล่อง");
612
- }
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");
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("ระยะทางเริ่มต้นต้องไม่ติดลบ");
626
712
  }
627
- if (isNaN(maxWeight) || maxWeight <= 0) {
628
- errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
713
+ if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
714
+ errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
629
715
  }
630
716
  if (isNaN(price) || price < 0) {
631
717
  errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
632
718
  }
719
+ if (isNaN(priority)) {
720
+ errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
721
+ }
633
722
  if (errors.length > 0) {
634
723
  ui.toast.error("ข้อมูลไม่ถูกต้อง", {
635
724
  description: errors.join(", ")
@@ -639,31 +728,32 @@ const ParcelBoxModal = ({ box, onClose }) => {
639
728
  setIsSaving(true);
640
729
  try {
641
730
  const payload = {
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,
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,
648
739
  active: formData.active
649
740
  };
650
- const url = box ? `/admin/boxes/${box.id}` : "/admin/boxes";
651
- const method = box ? "PUT" : "POST";
741
+ const url = rate ? `/admin/base-shipping-prices/${rate.id}` : "/admin/base-shipping-prices";
742
+ const method = rate ? "PUT" : "POST";
652
743
  const response = await fetch(url, {
653
744
  method,
654
745
  headers: { "Content-Type": "application/json" },
655
746
  credentials: "include",
656
747
  body: JSON.stringify(payload)
657
748
  });
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");
749
+ if (!response.ok) {
750
+ const error = await response.json().catch(() => ({}));
751
+ throw new Error(error.message || "ไม่สามารถบันทึกข้อมูลได้");
666
752
  }
753
+ ui.toast.success("สำเร็จ", {
754
+ description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
755
+ });
756
+ onClose();
667
757
  } catch (error) {
668
758
  ui.toast.error("ข้อผิดพลาด", {
669
759
  description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
@@ -673,82 +763,40 @@ const ParcelBoxModal = ({ box, onClose }) => {
673
763
  }
674
764
  };
675
765
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
676
- /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: box ? "แก้ไขกล่องพัสดุ" : "สร้างกล่องพัสดุใหม่" }) }),
766
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: rate ? "แก้ไขฐานราคาตามระยะทาง" : "สร้างฐานราคาตามระยะทาง" }) }),
677
767
  /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-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: [
692
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
693
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "width", children: "ความกว้าง (cm) *" }),
694
- /* @__PURE__ */ jsxRuntime.jsx(
695
- ui.Input,
696
- {
697
- id: "width",
698
- type: "number",
699
- step: "0.1",
700
- value: formData.width_cm,
701
- onChange: (e) => setFormData({ ...formData, width_cm: e.target.value }),
702
- placeholder: "20",
703
- required: true
704
- }
705
- )
706
- ] }),
768
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
707
769
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
708
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "length", children: "ความยาว (cm) *" }),
770
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "min_distance_km", children: "ระยะทางเริ่มต้น (กม.) *" }),
709
771
  /* @__PURE__ */ jsxRuntime.jsx(
710
772
  ui.Input,
711
773
  {
712
- id: "length",
774
+ id: "min_distance_km",
713
775
  type: "number",
776
+ min: "0",
714
777
  step: "0.1",
715
- value: formData.length_cm,
716
- onChange: (e) => setFormData({ ...formData, length_cm: e.target.value }),
717
- placeholder: "30",
778
+ value: formData.min_distance_km,
779
+ onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
718
780
  required: true
719
781
  }
720
782
  )
721
783
  ] }),
722
784
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
723
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "height", children: "ความสูง (cm) *" }),
785
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "max_distance_km", children: "ระยะทางสูงสุด (กม.)" }),
724
786
  /* @__PURE__ */ jsxRuntime.jsx(
725
787
  ui.Input,
726
788
  {
727
- id: "height",
789
+ id: "max_distance_km",
728
790
  type: "number",
791
+ min: "0",
729
792
  step: "0.1",
730
- value: formData.height_cm,
731
- onChange: (e) => setFormData({ ...formData, height_cm: e.target.value }),
732
- placeholder: "15",
733
- required: true
793
+ placeholder: "ปล่อยว่างหากไม่มีที่สิ้นสุด",
794
+ value: formData.max_distance_km,
795
+ onChange: (e) => setFormData({ ...formData, max_distance_km: e.target.value })
734
796
  }
735
- )
736
- ] })
737
- ] }),
738
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
739
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
740
- /* @__PURE__ */ jsxRuntime.jsx(
741
- ui.Input,
742
- {
743
- id: "maxWeight",
744
- type: "number",
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",
749
- required: true
750
- }
751
- )
797
+ ),
798
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
799
+ ] })
752
800
  ] }),
753
801
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
754
802
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
@@ -757,14 +805,28 @@ const ParcelBoxModal = ({ box, onClose }) => {
757
805
  {
758
806
  id: "price",
759
807
  type: "number",
808
+ min: "0",
760
809
  step: "0.01",
761
- value: formData.price_thb,
762
- onChange: (e) => setFormData({ ...formData, price_thb: e.target.value }),
763
- placeholder: "10.00",
810
+ value: formData.price,
811
+ onChange: (e) => setFormData({ ...formData, price: e.target.value }),
764
812
  required: true
765
813
  }
766
814
  )
767
815
  ] }),
816
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
817
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
818
+ /* @__PURE__ */ jsxRuntime.jsx(
819
+ ui.Input,
820
+ {
821
+ id: "priority",
822
+ type: "number",
823
+ value: formData.priority,
824
+ onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
825
+ placeholder: "0"
826
+ }
827
+ ),
828
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
829
+ ] }),
768
830
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
769
831
  /* @__PURE__ */ jsxRuntime.jsx(
770
832
  ui.Switch,
@@ -777,197 +839,163 @@ const ParcelBoxModal = ({ box, onClose }) => {
777
839
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
778
840
  ] }),
779
841
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
780
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, 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
+ ),
781
852
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
782
853
  ] })
783
854
  ] }) })
784
855
  ] }) });
785
856
  };
786
- const ParcelBoxesTable = () => {
787
- const [boxes, setBoxes] = react.useState([]);
788
- const [filteredBoxes, setFilteredBoxes] = react.useState([]);
789
- const [searchTerm, setSearchTerm] = react.useState("");
790
- const [isModalOpen, setIsModalOpen] = react.useState(false);
791
- const [editingBox, setEditingBox] = react.useState(null);
857
+ const MessengerBasePricingTable = () => {
858
+ const [rates, setRates] = react.useState([]);
792
859
  const [isLoading, setIsLoading] = react.useState(false);
793
- const [deleteBoxId, setDeleteBoxId] = react.useState(null);
794
- const [deleteBoxName, setDeleteBoxName] = react.useState("");
860
+ const [isModalOpen, setIsModalOpen] = react.useState(false);
861
+ const [editingRate, setEditingRate] = react.useState(null);
862
+ const [deleteId, setDeleteId] = react.useState(null);
795
863
  react.useEffect(() => {
796
- fetchBoxes();
864
+ fetchRates();
797
865
  }, []);
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 () => {
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 () => {
805
877
  setIsLoading(true);
806
878
  try {
807
- const response = await fetch("/admin/boxes", {
808
- credentials: "include"
809
- });
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
+ }
810
888
  const data = await response.json();
811
- setBoxes(data.boxes || []);
889
+ setRates(data.base_rates || []);
812
890
  } catch (error) {
813
891
  ui.toast.error("ข้อผิดพลาด", {
814
- description: "ไม่สามารถโหลดข้อมูลกล่องพัสดุได้"
892
+ description: "ไม่สามารถโหลดฐานราคาตามระยะทางได้"
815
893
  });
816
894
  } finally {
817
895
  setIsLoading(false);
818
896
  }
819
897
  };
820
- const handleToggleActive = async (box) => {
821
- try {
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");
835
- }
836
- } catch (error) {
837
- ui.toast.error("ข้อผิดพลาด", {
838
- description: "ไม่สามารถอัพเดทสถานะได้"
839
- });
840
- }
841
- };
842
898
  const handleDelete = async () => {
843
- if (!deleteBoxId) return;
899
+ if (!deleteId) return;
844
900
  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");
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");
856
910
  }
911
+ ui.toast.success("สำเร็จ", {
912
+ description: "ลบฐานราคาสำเร็จ"
913
+ });
914
+ fetchRates();
857
915
  } catch (error) {
858
916
  ui.toast.error("ข้อผิดพลาด", {
859
- description: "ไม่สามารถลบกล่องได้"
917
+ description: "ไม่สามารถลบฐานราคาได้"
860
918
  });
861
919
  } finally {
862
- setDeleteBoxId(null);
863
- setDeleteBoxName("");
920
+ setDeleteId(null);
864
921
  }
865
922
  };
866
- const handleEdit = (box) => {
867
- setEditingBox(box);
868
- setIsModalOpen(true);
869
- };
870
- const handleCreateNew = () => {
871
- setEditingBox(null);
872
- setIsModalOpen(true);
873
- };
874
923
  const handleModalClose = () => {
875
924
  setIsModalOpen(false);
876
- setEditingBox(null);
877
- fetchBoxes();
925
+ setEditingRate(null);
926
+ fetchRates();
878
927
  };
879
- const openDeletePrompt = (id, name) => {
880
- setDeleteBoxId(id);
881
- setDeleteBoxName(name);
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} กม.`;
882
935
  };
883
936
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
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
- ] })
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+ กม." })
900
941
  ] }),
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,
923
- {
924
- color: box.active ? "green" : "grey",
925
- className: "cursor-pointer",
926
- onClick: () => handleToggleActive(box),
927
- children: box.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
928
- }
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)) })
942
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: () => setIsModalOpen(true), children: [
943
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
944
+ "เพิ่มช่วงราคา"
951
945
  ] })
952
946
  ] }),
953
- isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ParcelBoxModal, { box: editingBox, onClose: handleModalClose }),
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,
978
+ {
979
+ variant: "transparent",
980
+ size: "small",
981
+ onClick: () => setDeleteId(rate.id),
982
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
983
+ }
984
+ )
985
+ ] }) })
986
+ ] }, rate.id)) })
987
+ ] }),
988
+ isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(MessengerBaseRateModal, { rate: editingRate, onClose: handleModalClose }),
954
989
  /* @__PURE__ */ jsxRuntime.jsx(
955
990
  ui.Prompt,
956
991
  {
957
992
  variant: "confirmation",
958
- open: !!deleteBoxId,
959
- onOpenChange: () => {
960
- setDeleteBoxId(null);
961
- setDeleteBoxName("");
962
- },
993
+ open: !!deleteId,
994
+ onOpenChange: () => setDeleteId(null),
963
995
  children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
964
996
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
965
- /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบกล่องพัสดุ" }),
966
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Description, { children: [
967
- 'คุณต้องการลบกล่อง "',
968
- deleteBoxName,
969
- '" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
970
- ] })
997
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "ลบฐานราคา" }),
998
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: "คุณต้องการลบช่วงราคานี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
971
999
  ] }),
972
1000
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
973
1001
  /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "ยกเลิก" }),
@@ -978,126 +1006,68 @@ const ParcelBoxesTable = () => {
978
1006
  )
979
1007
  ] });
980
1008
  };
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, {})
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, {})
985
1016
  ] }) });
986
1017
  };
987
1018
  const config$3 = adminSdk.defineRouteConfig({
988
- icon: icons.ArchiveBox,
989
- label: "กล่องพัสดุ"
1019
+ icon: icons.Directions,
1020
+ label: "ฐานราคาตามระยะทาง"
990
1021
  });
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 }) => {
1022
+ const ParcelBoxModal = ({ box, onClose }) => {
1071
1023
  const [formData, setFormData] = react.useState({
1072
- kind: "PROVINCE",
1073
- value: "",
1024
+ name: "",
1025
+ width_cm: "",
1026
+ length_cm: "",
1027
+ height_cm: "",
1028
+ max_weight_kg: "",
1029
+ price_thb: "",
1074
1030
  active: true
1075
1031
  });
1076
1032
  const [isSaving, setIsSaving] = react.useState(false);
1077
1033
  react.useEffect(() => {
1078
- if (area) {
1034
+ if (box) {
1079
1035
  setFormData({
1080
- kind: area.kind,
1081
- value: area.value,
1082
- active: area.active
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
1083
1043
  });
1084
1044
  }
1085
- }, [area]);
1045
+ }, [box]);
1086
1046
  const handleSubmit = async (e) => {
1087
1047
  e.preventDefault();
1088
1048
  const errors = [];
1089
- const trimmedValue = formData.value.trim();
1090
- if (!trimmedValue) {
1091
- errors.push("กรุณากรอกค่า");
1049
+ if (!formData.name.trim()) {
1050
+ errors.push("กรุณากรอกชื่อกล่อง");
1092
1051
  }
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
- }
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");
1068
+ }
1069
+ if (isNaN(price) || price < 0) {
1070
+ errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
1101
1071
  }
1102
1072
  if (errors.length > 0) {
1103
1073
  ui.toast.error("ข้อมูลไม่ถูกต้อง", {
@@ -1108,12 +1078,16 @@ const ServiceAreaModal = ({ area, onClose }) => {
1108
1078
  setIsSaving(true);
1109
1079
  try {
1110
1080
  const payload = {
1111
- kind: formData.kind,
1112
- value: trimmedValue,
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,
1113
1087
  active: formData.active
1114
1088
  };
1115
- const url = area ? `/admin/service-areas/${area.id}` : "/admin/service-areas";
1116
- const method = area ? "PUT" : "POST";
1089
+ const url = box ? `/admin/boxes/${box.id}` : "/admin/boxes";
1090
+ const method = box ? "PUT" : "POST";
1117
1091
  const response = await fetch(url, {
1118
1092
  method,
1119
1093
  headers: { "Content-Type": "application/json" },
@@ -1122,7 +1096,7 @@ const ServiceAreaModal = ({ area, onClose }) => {
1122
1096
  });
1123
1097
  if (response.ok) {
1124
1098
  ui.toast.success("สำเร็จ", {
1125
- description: area ? "แก้ไขพื้นที่บริการแล้ว" : "สร้างพื้นที่บริการใหม่แล้ว"
1099
+ description: box ? "แก้ไขกล่องพัสดุแล้ว" : "สร้างกล่องพัสดุใหม่แล้ว"
1126
1100
  });
1127
1101
  onClose();
1128
1102
  } else {
@@ -1138,53 +1112,97 @@ const ServiceAreaModal = ({ area, onClose }) => {
1138
1112
  }
1139
1113
  };
1140
1114
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
1141
- /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: area ? "แก้ไขพื้นที่บริการ" : "สร้างพื้นที่บริการใหม่" }) }),
1115
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: box ? "แก้ไขกล่องพัสดุ" : "สร้างกล่องพัสดุใหม่" }) }),
1142
1116
  /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
1143
1117
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1144
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "kind", children: "ประเภท *" }),
1145
- /* @__PURE__ */ jsxRuntime.jsxs(
1146
- ui.Select,
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,
1147
1181
  {
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
- ]
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
1158
1189
  }
1159
1190
  )
1160
1191
  ] }),
1161
1192
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
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(
1193
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "price", children: "ราคา (THB) *" }),
1194
+ /* @__PURE__ */ jsxRuntime.jsx(
1175
1195
  ui.Input,
1176
1196
  {
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}",
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",
1184
1203
  required: true
1185
1204
  }
1186
- ),
1187
- formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
1205
+ )
1188
1206
  ] }),
1189
1207
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
1190
1208
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1198,81 +1216,59 @@ const ServiceAreaModal = ({ area, onClose }) => {
1198
1216
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "active", children: "เปิดใช้งาน" })
1199
1217
  ] }),
1200
1218
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { 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
- ),
1219
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
1211
1220
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
1212
1221
  ] })
1213
1222
  ] }) })
1214
1223
  ] }) });
1215
1224
  };
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([]);
1225
+ const ParcelBoxesTable = () => {
1226
+ const [boxes, setBoxes] = react.useState([]);
1227
+ const [filteredBoxes, setFilteredBoxes] = react.useState([]);
1224
1228
  const [searchTerm, setSearchTerm] = react.useState("");
1225
- const [kindFilter, setKindFilter] = react.useState("ALL");
1226
1229
  const [isModalOpen, setIsModalOpen] = react.useState(false);
1227
- const [editingArea, setEditingArea] = react.useState(null);
1230
+ const [editingBox, setEditingBox] = react.useState(null);
1228
1231
  const [isLoading, setIsLoading] = react.useState(false);
1229
- const [deleteAreaId, setDeleteAreaId] = react.useState(null);
1230
- const [deleteAreaValue, setDeleteAreaValue] = react.useState("");
1232
+ const [deleteBoxId, setDeleteBoxId] = react.useState(null);
1233
+ const [deleteBoxName, setDeleteBoxName] = react.useState("");
1231
1234
  react.useEffect(() => {
1232
- fetchAreas();
1235
+ fetchBoxes();
1233
1236
  }, []);
1234
1237
  react.useEffect(() => {
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 () => {
1238
+ const filtered = boxes.filter(
1239
+ (box) => box.name.toLowerCase().includes(searchTerm.toLowerCase())
1240
+ );
1241
+ setFilteredBoxes(filtered);
1242
+ }, [searchTerm, boxes]);
1243
+ const fetchBoxes = async () => {
1247
1244
  setIsLoading(true);
1248
1245
  try {
1249
- const response = await fetch("/admin/service-areas", {
1246
+ const response = await fetch("/admin/boxes", {
1250
1247
  credentials: "include"
1251
1248
  });
1252
1249
  const data = await response.json();
1253
- console.log("Fetched service areas:", data);
1254
- setAreas(data.service_areas || []);
1250
+ setBoxes(data.boxes || []);
1255
1251
  } catch (error) {
1256
1252
  ui.toast.error("ข้อผิดพลาด", {
1257
- description: "ไม่สามารถโหลดข้อมูลพื้นที่บริการได้"
1253
+ description: "ไม่สามารถโหลดข้อมูลกล่องพัสดุได้"
1258
1254
  });
1259
1255
  } finally {
1260
1256
  setIsLoading(false);
1261
1257
  }
1262
1258
  };
1263
- const handleToggleActive = async (area) => {
1259
+ const handleToggleActive = async (box) => {
1264
1260
  try {
1265
- const response = await fetch(`/admin/service-areas/${area.id}`, {
1261
+ const response = await fetch(`/admin/boxes/${box.id}`, {
1266
1262
  method: "PUT",
1267
1263
  headers: { "Content-Type": "application/json" },
1268
1264
  credentials: "include",
1269
- body: JSON.stringify({ ...area, active: !area.active })
1265
+ body: JSON.stringify({ ...box, active: !box.active })
1270
1266
  });
1271
1267
  if (response.ok) {
1272
1268
  ui.toast.success("สำเร็จ", {
1273
- description: `${area.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}พื้นที่ ${area.value} แล้ว`
1269
+ description: `${box.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}กล่อง ${box.name} แล้ว`
1274
1270
  });
1275
- fetchAreas();
1271
+ fetchBoxes();
1276
1272
  } else {
1277
1273
  throw new Error("Failed to update");
1278
1274
  }
@@ -1283,87 +1279,91 @@ const ServiceAreasTable = () => {
1283
1279
  }
1284
1280
  };
1285
1281
  const handleDelete = async () => {
1286
- if (!deleteAreaId) return;
1282
+ if (!deleteBoxId) return;
1287
1283
  try {
1288
- const response = await fetch(`/admin/service-areas/${deleteAreaId}`, {
1284
+ const response = await fetch(`/admin/boxes/${deleteBoxId}`, {
1289
1285
  method: "DELETE",
1290
1286
  credentials: "include"
1291
1287
  });
1292
1288
  if (response.ok) {
1293
1289
  ui.toast.success("สำเร็จ", {
1294
- description: `ลบพื้นที่ ${deleteAreaValue} แล้ว`
1290
+ description: `ลบกล่อง ${deleteBoxName} แล้ว`
1295
1291
  });
1296
- fetchAreas();
1292
+ fetchBoxes();
1297
1293
  } else {
1298
1294
  throw new Error("Failed to delete");
1299
1295
  }
1300
1296
  } catch (error) {
1301
1297
  ui.toast.error("ข้อผิดพลาด", {
1302
- description: "ไม่สามารถลบพื้นที่ได้"
1298
+ description: "ไม่สามารถลบกล่องได้"
1303
1299
  });
1304
1300
  } finally {
1305
- setDeleteAreaId(null);
1306
- setDeleteAreaValue("");
1301
+ setDeleteBoxId(null);
1302
+ setDeleteBoxName("");
1307
1303
  }
1308
1304
  };
1309
- const handleEdit = (area) => {
1310
- setEditingArea(area);
1305
+ const handleEdit = (box) => {
1306
+ setEditingBox(box);
1311
1307
  setIsModalOpen(true);
1312
1308
  };
1313
1309
  const handleCreateNew = () => {
1314
- setEditingArea(null);
1310
+ setEditingBox(null);
1315
1311
  setIsModalOpen(true);
1316
1312
  };
1317
1313
  const handleModalClose = () => {
1318
1314
  setIsModalOpen(false);
1319
- setEditingArea(null);
1320
- fetchAreas();
1315
+ setEditingBox(null);
1316
+ fetchBoxes();
1321
1317
  };
1322
- const openDeletePrompt = (id, value) => {
1323
- setDeleteAreaId(id);
1324
- setDeleteAreaValue(value);
1318
+ const openDeletePrompt = (id, name) => {
1319
+ setDeleteBoxId(id);
1320
+ setDeleteBoxName(name);
1325
1321
  };
1326
1322
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1327
1323
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
1328
1324
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
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
- ] }),
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
+ ),
1345
1335
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: handleCreateNew, children: [
1346
1336
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, {}),
1347
- "สร้างพื้นที่ใหม่"
1337
+ "สร้างกล่องใหม่"
1348
1338
  ] })
1349
1339
  ] }),
1350
1340
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
1351
1341
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1352
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "ประเภท" }),
1353
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { 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)" }),
1354
1346
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "สถานะ" }),
1355
1347
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
1356
1348
  ] }) }),
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 }),
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) }),
1360
1360
  /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
1361
1361
  ui.Badge,
1362
1362
  {
1363
- color: area.active ? "green" : "grey",
1363
+ color: box.active ? "green" : "grey",
1364
1364
  className: "cursor-pointer",
1365
- onClick: () => handleToggleActive(area),
1366
- children: area.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
1365
+ onClick: () => handleToggleActive(box),
1366
+ children: box.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 ServiceAreasTable = () => {
1372
1372
  {
1373
1373
  variant: "transparent",
1374
1374
  size: "small",
1375
- onClick: () => handleEdit(area),
1375
+ onClick: () => handleEdit(box),
1376
1376
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Edit, {})
1377
1377
  }
1378
1378
  ),
@@ -1381,30 +1381,30 @@ const ServiceAreasTable = () => {
1381
1381
  {
1382
1382
  variant: "transparent",
1383
1383
  size: "small",
1384
- onClick: () => openDeletePrompt(area.id, area.value),
1384
+ onClick: () => openDeletePrompt(box.id, box.name),
1385
1385
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash, {})
1386
1386
  }
1387
1387
  )
1388
1388
  ] }) })
1389
- ] }, area.id)) })
1389
+ ] }, box.id)) })
1390
1390
  ] })
1391
1391
  ] }),
1392
- isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ServiceAreaModal, { area: editingArea, onClose: handleModalClose }),
1392
+ isModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ParcelBoxModal, { box: editingBox, onClose: handleModalClose }),
1393
1393
  /* @__PURE__ */ jsxRuntime.jsx(
1394
1394
  ui.Prompt,
1395
1395
  {
1396
1396
  variant: "confirmation",
1397
- open: !!deleteAreaId,
1397
+ open: !!deleteBoxId,
1398
1398
  onOpenChange: () => {
1399
- setDeleteAreaId(null);
1400
- setDeleteAreaValue("");
1399
+ setDeleteBoxId(null);
1400
+ setDeleteBoxName("");
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
- deleteAreaValue,
1406
+ 'คุณต้องการลบกล่อง "',
1407
+ deleteBoxName,
1408
1408
  '" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
1409
1409
  ] })
1410
1410
  ] }),
@@ -1417,15 +1417,15 @@ const ServiceAreasTable = () => {
1417
1417
  )
1418
1418
  ] });
1419
1419
  };
1420
- const AreasPage = () => {
1420
+ const BoxesPage = () => {
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(ServiceAreasTable, {})
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, {})
1424
1424
  ] }) });
1425
1425
  };
1426
1426
  const config$2 = adminSdk.defineRouteConfig({
1427
- icon: icons.MapPin,
1428
- label: "พื้นที่บริการ"
1427
+ icon: icons.ArchiveBox,
1428
+ label: "กล่องพัสดุ"
1429
1429
  });
1430
1430
  const CATEGORIES$1 = [
1431
1431
  { value: "PACKAGING", label: "วัสดุหีบห่อ" },
@@ -2713,6 +2713,10 @@ const widgetModule = { widgets: [
2713
2713
  ] };
2714
2714
  const routeModule = {
2715
2715
  routes: [
2716
+ {
2717
+ Component: AreasPage,
2718
+ path: "/areas"
2719
+ },
2716
2720
  {
2717
2721
  Component: BasePricingPage,
2718
2722
  path: "/base-pricing"
@@ -2721,10 +2725,6 @@ const routeModule = {
2721
2725
  Component: BoxesPage,
2722
2726
  path: "/boxes"
2723
2727
  },
2724
- {
2725
- Component: AreasPage,
2726
- path: "/areas"
2727
- },
2728
2728
  {
2729
2729
  Component: MaterialCostsPage,
2730
2730
  path: "/material-costs"
@@ -2738,24 +2738,24 @@ const routeModule = {
2738
2738
  const menuItemModule = {
2739
2739
  menuItems: [
2740
2740
  {
2741
- label: config$2.label,
2742
- icon: config$2.icon,
2741
+ label: config$4.label,
2742
+ icon: config$4.icon,
2743
2743
  path: "/areas",
2744
2744
  nested: void 0,
2745
2745
  rank: void 0,
2746
2746
  translationNs: void 0
2747
2747
  },
2748
2748
  {
2749
- label: config$4.label,
2750
- icon: config$4.icon,
2749
+ label: config$3.label,
2750
+ icon: config$3.icon,
2751
2751
  path: "/base-pricing",
2752
2752
  nested: void 0,
2753
2753
  rank: void 0,
2754
2754
  translationNs: void 0
2755
2755
  },
2756
2756
  {
2757
- label: config$3.label,
2758
- icon: config$3.icon,
2757
+ label: config$2.label,
2758
+ icon: config$2.icon,
2759
2759
  path: "/boxes",
2760
2760
  nested: void 0,
2761
2761
  rank: void 0,