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