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