@lodashventure/medusa-parcel-shipping 0.4.30 → 0.4.31
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 +1497 -1497
- package/.medusa/server/src/admin/index.mjs +1499 -1499
- 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, useMemo } from "react";
|
|
5
5
|
import { Package, AlertCircle, Truck, Clock, Plus, Edit, Trash, ExternalLink } from "lucide-react";
|
|
6
|
-
import { Directions, ArchiveBox,
|
|
6
|
+
import { MapPin, Directions, ArchiveBox, HandTruck, CurrencyDollarSolid } from "@medusajs/icons";
|
|
7
7
|
import "@medusajs/admin-shared";
|
|
8
8
|
const OrderShippingQuoteWidget = ({ data }) => {
|
|
9
9
|
const [quote, setQuote] = useState(null);
|
|
@@ -235,49 +235,116 @@ const OrderShippingQuoteWidget = ({ data }) => {
|
|
|
235
235
|
defineWidgetConfig({
|
|
236
236
|
zone: "order.details.after"
|
|
237
237
|
});
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
238
|
+
const TH_PROVINCES = [
|
|
239
|
+
"กรุงเทพมหานคร",
|
|
240
|
+
"กระบี่",
|
|
241
|
+
"กาญจนบุรี",
|
|
242
|
+
"กาฬสินธุ์",
|
|
243
|
+
"กำแพงเพชร",
|
|
244
|
+
"ขอนแก่น",
|
|
245
|
+
"จันทบุรี",
|
|
246
|
+
"ฉะเชิงเทรา",
|
|
247
|
+
"ชลบุรี",
|
|
248
|
+
"ชัยนาท",
|
|
249
|
+
"ชัยภูมิ",
|
|
250
|
+
"ชุมพร",
|
|
251
|
+
"เชียงราย",
|
|
252
|
+
"เชียงใหม่",
|
|
253
|
+
"ตรัง",
|
|
254
|
+
"ตราด",
|
|
255
|
+
"ตาก",
|
|
256
|
+
"นครนายก",
|
|
257
|
+
"นครปฐม",
|
|
258
|
+
"นครพนม",
|
|
259
|
+
"นครราชสีมา",
|
|
260
|
+
"นครศรีธรรมราช",
|
|
261
|
+
"นครสวรรค์",
|
|
262
|
+
"นนทบุรี",
|
|
263
|
+
"นราธิวาส",
|
|
264
|
+
"น่าน",
|
|
265
|
+
"บึงกาฬ",
|
|
266
|
+
"บุรีรัมย์",
|
|
267
|
+
"ปทุมธานี",
|
|
268
|
+
"ประจวบคีรีขันธ์",
|
|
269
|
+
"ปราจีนบุรี",
|
|
270
|
+
"ปัตตานี",
|
|
271
|
+
"พระนครศรีอยุธยา",
|
|
272
|
+
"พะเยา",
|
|
273
|
+
"พังงา",
|
|
274
|
+
"พัทลุง",
|
|
275
|
+
"พิจิตร",
|
|
276
|
+
"พิษณุโลก",
|
|
277
|
+
"เพชรบุรี",
|
|
278
|
+
"เพชรบูรณ์",
|
|
279
|
+
"แพร่",
|
|
280
|
+
"ภูเก็ต",
|
|
281
|
+
"มหาสารคาม",
|
|
282
|
+
"มุกดาหาร",
|
|
283
|
+
"แม่ฮ่องสอน",
|
|
284
|
+
"ยโสธร",
|
|
285
|
+
"ยะลา",
|
|
286
|
+
"ร้อยเอ็ด",
|
|
287
|
+
"ระนอง",
|
|
288
|
+
"ระยอง",
|
|
289
|
+
"ราชบุรี",
|
|
290
|
+
"ลพบุรี",
|
|
291
|
+
"ลำปาง",
|
|
292
|
+
"ลำพูน",
|
|
293
|
+
"เลย",
|
|
294
|
+
"ศรีสะเกษ",
|
|
295
|
+
"สกลนคร",
|
|
296
|
+
"สงขลา",
|
|
297
|
+
"สตูล",
|
|
298
|
+
"สมุทรปราการ",
|
|
299
|
+
"สมุทรสงคราม",
|
|
300
|
+
"สมุทรสาคร",
|
|
301
|
+
"สระแก้ว",
|
|
302
|
+
"สระบุรี",
|
|
303
|
+
"สิงห์บุรี",
|
|
304
|
+
"สุโขทัย",
|
|
305
|
+
"สุพรรณบุรี",
|
|
306
|
+
"สุราษฎร์ธานี",
|
|
307
|
+
"สุรินทร์",
|
|
308
|
+
"หนองคาย",
|
|
309
|
+
"หนองบัวลำภู",
|
|
310
|
+
"อ่างทอง",
|
|
311
|
+
"อำนาจเจริญ",
|
|
312
|
+
"อุดรธานี",
|
|
313
|
+
"อุตรดิตถ์",
|
|
314
|
+
"อุทัยธานี",
|
|
315
|
+
"อุบลราชธานี"
|
|
316
|
+
];
|
|
317
|
+
const ServiceAreaModal = ({ area, onClose }) => {
|
|
242
318
|
const [formData, setFormData] = useState({
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
price: "",
|
|
246
|
-
priority: "0",
|
|
319
|
+
kind: "PROVINCE",
|
|
320
|
+
value: "",
|
|
247
321
|
active: true
|
|
248
322
|
});
|
|
249
323
|
const [isSaving, setIsSaving] = useState(false);
|
|
250
324
|
useEffect(() => {
|
|
251
|
-
|
|
252
|
-
if (rate) {
|
|
325
|
+
if (area) {
|
|
253
326
|
setFormData({
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
priority: (rate.priority ?? 0).toString(),
|
|
258
|
-
active: rate.active
|
|
327
|
+
kind: area.kind,
|
|
328
|
+
value: area.value,
|
|
329
|
+
active: area.active
|
|
259
330
|
});
|
|
260
331
|
}
|
|
261
|
-
}, [
|
|
332
|
+
}, [area]);
|
|
262
333
|
const handleSubmit = async (e) => {
|
|
263
334
|
e.preventDefault();
|
|
264
335
|
const errors = [];
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const price = parseFloat(formData.price);
|
|
269
|
-
const priority = parseInt(formData.priority || "0", 10);
|
|
270
|
-
if (isNaN(minDistance) || minDistance < 0) {
|
|
271
|
-
errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
|
|
272
|
-
}
|
|
273
|
-
if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
|
|
274
|
-
errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
|
|
275
|
-
}
|
|
276
|
-
if (isNaN(price) || price < 0) {
|
|
277
|
-
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
336
|
+
const trimmedValue = formData.value.trim();
|
|
337
|
+
if (!trimmedValue) {
|
|
338
|
+
errors.push("กรุณากรอกค่า");
|
|
278
339
|
}
|
|
279
|
-
if (
|
|
280
|
-
|
|
340
|
+
if (formData.kind === "PROVINCE") {
|
|
341
|
+
if (!TH_PROVINCES.includes(trimmedValue)) {
|
|
342
|
+
errors.push("จังหวัดไม่ถูกต้อง กรุณาตรวจสอบการสะกดชื่อจังหวัด");
|
|
343
|
+
}
|
|
344
|
+
} else if (formData.kind === "POSTCODE_PREFIX") {
|
|
345
|
+
if (!/^\d{1,5}$/.test(trimmedValue)) {
|
|
346
|
+
errors.push("รหัสไปรษณีย์ต้องเป็นตัวเลข 1-5 หลัก");
|
|
347
|
+
}
|
|
281
348
|
}
|
|
282
349
|
if (errors.length > 0) {
|
|
283
350
|
toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
@@ -288,32 +355,27 @@ const MessengerBaseRateModal = ({
|
|
|
288
355
|
setIsSaving(true);
|
|
289
356
|
try {
|
|
290
357
|
const payload = {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
service_code: "MESSENGER_3H",
|
|
294
|
-
min_distance_km: minDistance,
|
|
295
|
-
max_distance_km: maxDistance === void 0 || isNaN(maxDistance) ? null : maxDistance,
|
|
296
|
-
price,
|
|
297
|
-
currency: "THB",
|
|
298
|
-
priority,
|
|
358
|
+
kind: formData.kind,
|
|
359
|
+
value: trimmedValue,
|
|
299
360
|
active: formData.active
|
|
300
361
|
};
|
|
301
|
-
const url =
|
|
302
|
-
const method =
|
|
362
|
+
const url = area ? `/admin/service-areas/${area.id}` : "/admin/service-areas";
|
|
363
|
+
const method = area ? "PUT" : "POST";
|
|
303
364
|
const response = await fetch(url, {
|
|
304
365
|
method,
|
|
305
366
|
headers: { "Content-Type": "application/json" },
|
|
306
367
|
credentials: "include",
|
|
307
368
|
body: JSON.stringify(payload)
|
|
308
369
|
});
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
|
|
370
|
+
if (response.ok) {
|
|
371
|
+
toast.success("สำเร็จ", {
|
|
372
|
+
description: area ? "แก้ไขพื้นที่บริการแล้ว" : "สร้างพื้นที่บริการใหม่แล้ว"
|
|
373
|
+
});
|
|
374
|
+
onClose();
|
|
375
|
+
} else {
|
|
376
|
+
const error = await response.json();
|
|
377
|
+
throw new Error(error.message || "Failed to save");
|
|
312
378
|
}
|
|
313
|
-
toast.success("สำเร็จ", {
|
|
314
|
-
description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
|
|
315
|
-
});
|
|
316
|
-
onClose();
|
|
317
379
|
} catch (error) {
|
|
318
380
|
toast.error("ข้อผิดพลาด", {
|
|
319
381
|
description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
|
|
@@ -323,69 +385,53 @@ const MessengerBaseRateModal = ({
|
|
|
323
385
|
}
|
|
324
386
|
};
|
|
325
387
|
return /* @__PURE__ */ jsx(Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(Drawer.Content, { children: [
|
|
326
|
-
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children:
|
|
388
|
+
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: area ? "แก้ไขพื้นที่บริการ" : "สร้างพื้นที่บริการใหม่" }) }),
|
|
327
389
|
/* @__PURE__ */ jsx(Drawer.Body, { children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
328
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
329
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
330
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "min_distance_km", children: "ระยะทางเริ่มต้น (กม.) *" }),
|
|
331
|
-
/* @__PURE__ */ jsx(
|
|
332
|
-
Input,
|
|
333
|
-
{
|
|
334
|
-
id: "min_distance_km",
|
|
335
|
-
type: "number",
|
|
336
|
-
min: "0",
|
|
337
|
-
step: "0.1",
|
|
338
|
-
value: formData.min_distance_km,
|
|
339
|
-
onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
|
|
340
|
-
required: true
|
|
341
|
-
}
|
|
342
|
-
)
|
|
343
|
-
] }),
|
|
344
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
345
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "max_distance_km", children: "ระยะทางสูงสุด (กม.)" }),
|
|
346
|
-
/* @__PURE__ */ jsx(
|
|
347
|
-
Input,
|
|
348
|
-
{
|
|
349
|
-
id: "max_distance_km",
|
|
350
|
-
type: "number",
|
|
351
|
-
min: "0",
|
|
352
|
-
step: "0.1",
|
|
353
|
-
placeholder: "ปล่อยว่างหากไม่มีที่สิ้นสุด",
|
|
354
|
-
value: formData.max_distance_km,
|
|
355
|
-
onChange: (e) => setFormData({ ...formData, max_distance_km: e.target.value })
|
|
356
|
-
}
|
|
357
|
-
),
|
|
358
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
|
|
359
|
-
] })
|
|
360
|
-
] }),
|
|
361
390
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
362
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
363
|
-
/* @__PURE__ */
|
|
364
|
-
|
|
391
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "kind", children: "ประเภท *" }),
|
|
392
|
+
/* @__PURE__ */ jsxs(
|
|
393
|
+
Select,
|
|
365
394
|
{
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
395
|
+
value: formData.kind,
|
|
396
|
+
onValueChange: (value) => setFormData({ ...formData, kind: value, value: "" }),
|
|
397
|
+
required: true,
|
|
398
|
+
children: [
|
|
399
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกประเภท" }) }),
|
|
400
|
+
/* @__PURE__ */ jsxs(Select.Content, { children: [
|
|
401
|
+
/* @__PURE__ */ jsx(Select.Item, { value: "PROVINCE", children: "จังหวัด" }),
|
|
402
|
+
/* @__PURE__ */ jsx(Select.Item, { value: "POSTCODE_PREFIX", children: "รหัสไปรษณีย์" })
|
|
403
|
+
] })
|
|
404
|
+
]
|
|
373
405
|
}
|
|
374
406
|
)
|
|
375
407
|
] }),
|
|
376
408
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
377
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
378
|
-
/* @__PURE__ */
|
|
409
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "value", children: formData.kind === "PROVINCE" ? "ชื่อจังหวัด *" : "รหัสไปรษณีย์ (1-5 หลัก) *" }),
|
|
410
|
+
formData.kind === "PROVINCE" ? /* @__PURE__ */ jsxs(
|
|
411
|
+
Select,
|
|
412
|
+
{
|
|
413
|
+
value: formData.value,
|
|
414
|
+
onValueChange: (value) => setFormData({ ...formData, value }),
|
|
415
|
+
required: true,
|
|
416
|
+
children: [
|
|
417
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกจังหวัด" }) }),
|
|
418
|
+
/* @__PURE__ */ jsx(Select.Content, { children: TH_PROVINCES.map((province) => /* @__PURE__ */ jsx(Select.Item, { value: province, children: province }, province)) })
|
|
419
|
+
]
|
|
420
|
+
}
|
|
421
|
+
) : /* @__PURE__ */ jsx(
|
|
379
422
|
Input,
|
|
380
423
|
{
|
|
381
|
-
id: "
|
|
382
|
-
type: "
|
|
383
|
-
value: formData.
|
|
384
|
-
onChange: (e) => setFormData({ ...formData,
|
|
385
|
-
placeholder: "
|
|
424
|
+
id: "value",
|
|
425
|
+
type: "text",
|
|
426
|
+
value: formData.value,
|
|
427
|
+
onChange: (e) => setFormData({ ...formData, value: e.target.value }),
|
|
428
|
+
placeholder: "เช่น 10, 102, 10200",
|
|
429
|
+
maxLength: 5,
|
|
430
|
+
pattern: "\\d{1,5}",
|
|
431
|
+
required: true
|
|
386
432
|
}
|
|
387
433
|
),
|
|
388
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children:
|
|
434
|
+
formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
|
|
389
435
|
] }),
|
|
390
436
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
391
437
|
/* @__PURE__ */ jsx(
|
|
@@ -414,148 +460,200 @@ const MessengerBaseRateModal = ({
|
|
|
414
460
|
] }) })
|
|
415
461
|
] }) });
|
|
416
462
|
};
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
463
|
+
const AREA_KINDS = [
|
|
464
|
+
{ value: "ALL", label: "ทั้งหมด" },
|
|
465
|
+
{ value: "PROVINCE", label: "จังหวัด" },
|
|
466
|
+
{ value: "POSTCODE_PREFIX", label: "รหัสไปรษณีย์" }
|
|
467
|
+
];
|
|
468
|
+
const ServiceAreasTable = () => {
|
|
469
|
+
const [areas, setAreas] = useState([]);
|
|
470
|
+
const [filteredAreas, setFilteredAreas] = useState([]);
|
|
471
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
472
|
+
const [kindFilter, setKindFilter] = useState("ALL");
|
|
473
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
474
|
+
const [editingArea, setEditingArea] = useState(null);
|
|
475
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
476
|
+
const [deleteAreaId, setDeleteAreaId] = useState(null);
|
|
477
|
+
const [deleteAreaValue, setDeleteAreaValue] = useState("");
|
|
478
|
+
useEffect(() => {
|
|
479
|
+
fetchAreas();
|
|
480
|
+
}, []);
|
|
481
|
+
useEffect(() => {
|
|
482
|
+
let filtered = areas;
|
|
483
|
+
if (kindFilter !== "ALL") {
|
|
484
|
+
filtered = filtered.filter((area) => area.kind === kindFilter);
|
|
485
|
+
}
|
|
486
|
+
if (searchTerm) {
|
|
487
|
+
filtered = filtered.filter(
|
|
488
|
+
(area) => area.value.toLowerCase().includes(searchTerm.toLowerCase())
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
setFilteredAreas(filtered);
|
|
492
|
+
}, [searchTerm, kindFilter, areas]);
|
|
493
|
+
const fetchAreas = async () => {
|
|
437
494
|
setIsLoading(true);
|
|
438
495
|
try {
|
|
439
|
-
const response = await fetch(
|
|
440
|
-
"
|
|
441
|
-
|
|
442
|
-
credentials: "include"
|
|
443
|
-
}
|
|
444
|
-
);
|
|
445
|
-
if (!response.ok) {
|
|
446
|
-
throw new Error("failed_to_load_base_rates");
|
|
447
|
-
}
|
|
496
|
+
const response = await fetch("/admin/service-areas", {
|
|
497
|
+
credentials: "include"
|
|
498
|
+
});
|
|
448
499
|
const data = await response.json();
|
|
449
|
-
|
|
500
|
+
console.log("Fetched service areas:", data);
|
|
501
|
+
setAreas(data.service_areas || []);
|
|
450
502
|
} catch (error) {
|
|
451
503
|
toast.error("ข้อผิดพลาด", {
|
|
452
|
-
description: "
|
|
504
|
+
description: "ไม่สามารถโหลดข้อมูลพื้นที่บริการได้"
|
|
453
505
|
});
|
|
454
506
|
} finally {
|
|
455
507
|
setIsLoading(false);
|
|
456
508
|
}
|
|
457
509
|
};
|
|
458
|
-
const
|
|
459
|
-
if (!deleteId) return;
|
|
510
|
+
const handleToggleActive = async (area) => {
|
|
460
511
|
try {
|
|
461
|
-
const response = await fetch(
|
|
462
|
-
|
|
463
|
-
{
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
)
|
|
468
|
-
|
|
469
|
-
|
|
512
|
+
const response = await fetch(`/admin/service-areas/${area.id}`, {
|
|
513
|
+
method: "PUT",
|
|
514
|
+
headers: { "Content-Type": "application/json" },
|
|
515
|
+
credentials: "include",
|
|
516
|
+
body: JSON.stringify({ ...area, active: !area.active })
|
|
517
|
+
});
|
|
518
|
+
if (response.ok) {
|
|
519
|
+
toast.success("สำเร็จ", {
|
|
520
|
+
description: `${area.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}พื้นที่ ${area.value} แล้ว`
|
|
521
|
+
});
|
|
522
|
+
fetchAreas();
|
|
523
|
+
} else {
|
|
524
|
+
throw new Error("Failed to update");
|
|
470
525
|
}
|
|
471
|
-
|
|
472
|
-
|
|
526
|
+
} catch (error) {
|
|
527
|
+
toast.error("ข้อผิดพลาด", {
|
|
528
|
+
description: "ไม่สามารถอัพเดทสถานะได้"
|
|
473
529
|
});
|
|
474
|
-
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
const handleDelete = async () => {
|
|
533
|
+
if (!deleteAreaId) return;
|
|
534
|
+
try {
|
|
535
|
+
const response = await fetch(`/admin/service-areas/${deleteAreaId}`, {
|
|
536
|
+
method: "DELETE",
|
|
537
|
+
credentials: "include"
|
|
538
|
+
});
|
|
539
|
+
if (response.ok) {
|
|
540
|
+
toast.success("สำเร็จ", {
|
|
541
|
+
description: `ลบพื้นที่ ${deleteAreaValue} แล้ว`
|
|
542
|
+
});
|
|
543
|
+
fetchAreas();
|
|
544
|
+
} else {
|
|
545
|
+
throw new Error("Failed to delete");
|
|
546
|
+
}
|
|
475
547
|
} catch (error) {
|
|
476
548
|
toast.error("ข้อผิดพลาด", {
|
|
477
|
-
description: "
|
|
549
|
+
description: "ไม่สามารถลบพื้นที่ได้"
|
|
478
550
|
});
|
|
479
551
|
} finally {
|
|
480
|
-
|
|
552
|
+
setDeleteAreaId(null);
|
|
553
|
+
setDeleteAreaValue("");
|
|
481
554
|
}
|
|
482
555
|
};
|
|
556
|
+
const handleEdit = (area) => {
|
|
557
|
+
setEditingArea(area);
|
|
558
|
+
setIsModalOpen(true);
|
|
559
|
+
};
|
|
560
|
+
const handleCreateNew = () => {
|
|
561
|
+
setEditingArea(null);
|
|
562
|
+
setIsModalOpen(true);
|
|
563
|
+
};
|
|
483
564
|
const handleModalClose = () => {
|
|
484
565
|
setIsModalOpen(false);
|
|
485
|
-
|
|
486
|
-
|
|
566
|
+
setEditingArea(null);
|
|
567
|
+
fetchAreas();
|
|
487
568
|
};
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
if (max === void 0 || max === null) {
|
|
492
|
-
return `${min}+ กม.`;
|
|
493
|
-
}
|
|
494
|
-
return `${min}-${max} กม.`;
|
|
569
|
+
const openDeletePrompt = (id, value) => {
|
|
570
|
+
setDeleteAreaId(id);
|
|
571
|
+
setDeleteAreaValue(value);
|
|
495
572
|
};
|
|
496
573
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
497
|
-
/* @__PURE__ */ jsxs("div", { className: "flex
|
|
498
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
499
|
-
/* @__PURE__ */
|
|
500
|
-
/* @__PURE__ */ jsx("p", { className: "text-ui-fg-subtle text-sm", children: "กำหนดราคาเริ่มต้นตามระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
|
|
501
|
-
] }),
|
|
502
|
-
/* @__PURE__ */ jsxs(Button, { onClick: () => setIsModalOpen(true), children: [
|
|
503
|
-
/* @__PURE__ */ jsx(Plus, {}),
|
|
504
|
-
"เพิ่มช่วงราคา"
|
|
505
|
-
] })
|
|
506
|
-
] }),
|
|
507
|
-
/* @__PURE__ */ jsxs(Table, { className: "mt-4", children: [
|
|
508
|
-
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
509
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ช่วงระยะทาง" }),
|
|
510
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
511
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ลำดับ" }),
|
|
512
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
513
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
514
|
-
] }) }),
|
|
515
|
-
/* @__PURE__ */ jsx(Table.Body, { children: isLoading ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : orderedRates.length === 0 ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "ยังไม่ได้ตั้งค่าฐานราคาตามระยะทาง" }) }) : orderedRates.map((rate) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
516
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: formatDistanceRange(rate) }),
|
|
517
|
-
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
518
|
-
"฿",
|
|
519
|
-
rate.price.toFixed(2)
|
|
520
|
-
] }),
|
|
521
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: rate.priority ?? 0 }),
|
|
522
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
523
|
-
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
574
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
575
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
576
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-x-4 flex-1", children: [
|
|
524
577
|
/* @__PURE__ */ jsx(
|
|
525
|
-
|
|
578
|
+
Input,
|
|
526
579
|
{
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
},
|
|
533
|
-
children: /* @__PURE__ */ jsx(Edit, {})
|
|
580
|
+
type: "search",
|
|
581
|
+
placeholder: "ค้นหาจังหวัดหรือรหัสไปรษณีย์...",
|
|
582
|
+
value: searchTerm,
|
|
583
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
584
|
+
className: "max-w-md"
|
|
534
585
|
}
|
|
535
586
|
),
|
|
536
|
-
/* @__PURE__ */
|
|
537
|
-
|
|
587
|
+
/* @__PURE__ */ jsxs(Select, { value: kindFilter, onValueChange: setKindFilter, children: [
|
|
588
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "ประเภท" }) }),
|
|
589
|
+
/* @__PURE__ */ jsx(Select.Content, { children: AREA_KINDS.map((kind) => /* @__PURE__ */ jsx(Select.Item, { value: kind.value, children: kind.label }, kind.value)) })
|
|
590
|
+
] })
|
|
591
|
+
] }),
|
|
592
|
+
/* @__PURE__ */ jsxs(Button, { onClick: handleCreateNew, children: [
|
|
593
|
+
/* @__PURE__ */ jsx(Plus, {}),
|
|
594
|
+
"สร้างพื้นที่ใหม่"
|
|
595
|
+
] })
|
|
596
|
+
] }),
|
|
597
|
+
/* @__PURE__ */ jsxs(Table, { children: [
|
|
598
|
+
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
599
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ประเภท" }),
|
|
600
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ค่า" }),
|
|
601
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
602
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
603
|
+
] }) }),
|
|
604
|
+
/* @__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: [
|
|
605
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: area.kind === "PROVINCE" ? "blue" : "purple", children: area.kind === "PROVINCE" ? "จังหวัด" : "รหัสไปรษณีย์" }) }),
|
|
606
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: area.value }),
|
|
607
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
|
|
608
|
+
Badge,
|
|
538
609
|
{
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
onClick: () =>
|
|
542
|
-
children:
|
|
610
|
+
color: area.active ? "green" : "grey",
|
|
611
|
+
className: "cursor-pointer",
|
|
612
|
+
onClick: () => handleToggleActive(area),
|
|
613
|
+
children: area.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
543
614
|
}
|
|
544
|
-
)
|
|
545
|
-
|
|
546
|
-
|
|
615
|
+
) }),
|
|
616
|
+
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
617
|
+
/* @__PURE__ */ jsx(
|
|
618
|
+
Button,
|
|
619
|
+
{
|
|
620
|
+
variant: "transparent",
|
|
621
|
+
size: "small",
|
|
622
|
+
onClick: () => handleEdit(area),
|
|
623
|
+
children: /* @__PURE__ */ jsx(Edit, {})
|
|
624
|
+
}
|
|
625
|
+
),
|
|
626
|
+
/* @__PURE__ */ jsx(
|
|
627
|
+
Button,
|
|
628
|
+
{
|
|
629
|
+
variant: "transparent",
|
|
630
|
+
size: "small",
|
|
631
|
+
onClick: () => openDeletePrompt(area.id, area.value),
|
|
632
|
+
children: /* @__PURE__ */ jsx(Trash, {})
|
|
633
|
+
}
|
|
634
|
+
)
|
|
635
|
+
] }) })
|
|
636
|
+
] }, area.id)) })
|
|
637
|
+
] })
|
|
547
638
|
] }),
|
|
548
|
-
isModalOpen && /* @__PURE__ */ jsx(
|
|
639
|
+
isModalOpen && /* @__PURE__ */ jsx(ServiceAreaModal, { area: editingArea, onClose: handleModalClose }),
|
|
549
640
|
/* @__PURE__ */ jsx(
|
|
550
641
|
Prompt,
|
|
551
642
|
{
|
|
552
643
|
variant: "confirmation",
|
|
553
|
-
open: !!
|
|
554
|
-
onOpenChange: () =>
|
|
644
|
+
open: !!deleteAreaId,
|
|
645
|
+
onOpenChange: () => {
|
|
646
|
+
setDeleteAreaId(null);
|
|
647
|
+
setDeleteAreaValue("");
|
|
648
|
+
},
|
|
555
649
|
children: /* @__PURE__ */ jsxs(Prompt.Content, { children: [
|
|
556
650
|
/* @__PURE__ */ jsxs(Prompt.Header, { children: [
|
|
557
|
-
/* @__PURE__ */ jsx(Prompt.Title, { children: "
|
|
558
|
-
/* @__PURE__ */
|
|
651
|
+
/* @__PURE__ */ jsx(Prompt.Title, { children: "ลบพื้นที่บริการ" }),
|
|
652
|
+
/* @__PURE__ */ jsxs(Prompt.Description, { children: [
|
|
653
|
+
'คุณต้องการลบพื้นที่ "',
|
|
654
|
+
deleteAreaValue,
|
|
655
|
+
'" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
|
|
656
|
+
] })
|
|
559
657
|
] }),
|
|
560
658
|
/* @__PURE__ */ jsxs(Prompt.Footer, { children: [
|
|
561
659
|
/* @__PURE__ */ jsx(Prompt.Cancel, { children: "ยกเลิก" }),
|
|
@@ -566,69 +664,60 @@ const MessengerBasePricingTable = () => {
|
|
|
566
664
|
)
|
|
567
665
|
] });
|
|
568
666
|
};
|
|
569
|
-
const
|
|
570
|
-
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-
|
|
571
|
-
/* @__PURE__ */
|
|
572
|
-
|
|
573
|
-
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "ตั้งราคาฐานสำหรับบริการ Messenger 3 ชั่วโมงตามช่วงระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
|
|
574
|
-
] }),
|
|
575
|
-
/* @__PURE__ */ jsx(MessengerBasePricingTable, {})
|
|
667
|
+
const AreasPage = () => {
|
|
668
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
669
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "พื้นที่บริการ" }) }),
|
|
670
|
+
/* @__PURE__ */ jsx(ServiceAreasTable, {})
|
|
576
671
|
] }) });
|
|
577
672
|
};
|
|
578
673
|
const config$4 = defineRouteConfig({
|
|
579
|
-
icon:
|
|
580
|
-
label: "
|
|
674
|
+
icon: MapPin,
|
|
675
|
+
label: "พื้นที่บริการ"
|
|
581
676
|
});
|
|
582
|
-
const
|
|
677
|
+
const MessengerBaseRateModal = ({
|
|
678
|
+
rate,
|
|
679
|
+
onClose
|
|
680
|
+
}) => {
|
|
583
681
|
const [formData, setFormData] = useState({
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
max_weight_kg: "",
|
|
589
|
-
price_thb: "",
|
|
682
|
+
min_distance_km: "0",
|
|
683
|
+
max_distance_km: "",
|
|
684
|
+
price: "",
|
|
685
|
+
priority: "0",
|
|
590
686
|
active: true
|
|
591
687
|
});
|
|
592
688
|
const [isSaving, setIsSaving] = useState(false);
|
|
593
689
|
useEffect(() => {
|
|
594
|
-
|
|
690
|
+
var _a, _b;
|
|
691
|
+
if (rate) {
|
|
595
692
|
setFormData({
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
price_thb: box.price_thb.toString(),
|
|
602
|
-
active: box.active
|
|
693
|
+
min_distance_km: ((_a = rate.min_distance_km) == null ? void 0 : _a.toString()) ?? "0",
|
|
694
|
+
max_distance_km: ((_b = rate.max_distance_km) == null ? void 0 : _b.toString()) || "",
|
|
695
|
+
price: rate.price.toString(),
|
|
696
|
+
priority: (rate.priority ?? 0).toString(),
|
|
697
|
+
active: rate.active
|
|
603
698
|
});
|
|
604
699
|
}
|
|
605
|
-
}, [
|
|
700
|
+
}, [rate]);
|
|
606
701
|
const handleSubmit = async (e) => {
|
|
607
702
|
e.preventDefault();
|
|
608
703
|
const errors = [];
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
const
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
const price = parseFloat(formData.price_thb);
|
|
617
|
-
if (isNaN(width) || width <= 0) {
|
|
618
|
-
errors.push("ความกว้างต้องเป็นตัวเลขมากกว่า 0");
|
|
619
|
-
}
|
|
620
|
-
if (isNaN(length) || length <= 0) {
|
|
621
|
-
errors.push("ความยาวต้องเป็นตัวเลขมากกว่า 0");
|
|
622
|
-
}
|
|
623
|
-
if (isNaN(height) || height <= 0) {
|
|
624
|
-
errors.push("ความสูงต้องเป็นตัวเลขมากกว่า 0");
|
|
704
|
+
const minDistance = parseFloat(formData.min_distance_km || "0");
|
|
705
|
+
const hasMaxDistance = formData.max_distance_km !== "";
|
|
706
|
+
const maxDistance = hasMaxDistance ? parseFloat(formData.max_distance_km) : void 0;
|
|
707
|
+
const price = parseFloat(formData.price);
|
|
708
|
+
const priority = parseInt(formData.priority || "0", 10);
|
|
709
|
+
if (isNaN(minDistance) || minDistance < 0) {
|
|
710
|
+
errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
|
|
625
711
|
}
|
|
626
|
-
if (isNaN(
|
|
627
|
-
errors.push("
|
|
712
|
+
if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
|
|
713
|
+
errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
|
|
628
714
|
}
|
|
629
715
|
if (isNaN(price) || price < 0) {
|
|
630
716
|
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
631
717
|
}
|
|
718
|
+
if (isNaN(priority)) {
|
|
719
|
+
errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
|
|
720
|
+
}
|
|
632
721
|
if (errors.length > 0) {
|
|
633
722
|
toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
634
723
|
description: errors.join(", ")
|
|
@@ -638,31 +727,32 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
638
727
|
setIsSaving(true);
|
|
639
728
|
try {
|
|
640
729
|
const payload = {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
730
|
+
carrier_type: "COMPANY_FLEET",
|
|
731
|
+
carrier: "COMPANY_FLEET",
|
|
732
|
+
service_code: "MESSENGER_3H",
|
|
733
|
+
min_distance_km: minDistance,
|
|
734
|
+
max_distance_km: maxDistance === void 0 || isNaN(maxDistance) ? null : maxDistance,
|
|
735
|
+
price,
|
|
736
|
+
currency: "THB",
|
|
737
|
+
priority,
|
|
647
738
|
active: formData.active
|
|
648
739
|
};
|
|
649
|
-
const url =
|
|
650
|
-
const method =
|
|
740
|
+
const url = rate ? `/admin/base-shipping-prices/${rate.id}` : "/admin/base-shipping-prices";
|
|
741
|
+
const method = rate ? "PUT" : "POST";
|
|
651
742
|
const response = await fetch(url, {
|
|
652
743
|
method,
|
|
653
744
|
headers: { "Content-Type": "application/json" },
|
|
654
745
|
credentials: "include",
|
|
655
746
|
body: JSON.stringify(payload)
|
|
656
747
|
});
|
|
657
|
-
if (response.ok) {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
});
|
|
661
|
-
onClose();
|
|
662
|
-
} else {
|
|
663
|
-
const error = await response.json();
|
|
664
|
-
throw new Error(error.message || "Failed to save");
|
|
748
|
+
if (!response.ok) {
|
|
749
|
+
const error = await response.json().catch(() => ({}));
|
|
750
|
+
throw new Error(error.message || "ไม่สามารถบันทึกข้อมูลได้");
|
|
665
751
|
}
|
|
752
|
+
toast.success("สำเร็จ", {
|
|
753
|
+
description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
|
|
754
|
+
});
|
|
755
|
+
onClose();
|
|
666
756
|
} catch (error) {
|
|
667
757
|
toast.error("ข้อผิดพลาด", {
|
|
668
758
|
description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
|
|
@@ -672,82 +762,40 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
672
762
|
}
|
|
673
763
|
};
|
|
674
764
|
return /* @__PURE__ */ jsx(Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(Drawer.Content, { children: [
|
|
675
|
-
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children:
|
|
765
|
+
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: rate ? "แก้ไขฐานราคาตามระยะทาง" : "สร้างฐานราคาตามระยะทาง" }) }),
|
|
676
766
|
/* @__PURE__ */ jsx(Drawer.Body, { children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
677
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
678
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "name", children: "ชื่อกล่อง *" }),
|
|
679
|
-
/* @__PURE__ */ jsx(
|
|
680
|
-
Input,
|
|
681
|
-
{
|
|
682
|
-
id: "name",
|
|
683
|
-
value: formData.name,
|
|
684
|
-
onChange: (e) => setFormData({ ...formData, name: e.target.value }),
|
|
685
|
-
placeholder: "เช่น กล่อง S, กล่อง M",
|
|
686
|
-
required: true
|
|
687
|
-
}
|
|
688
|
-
)
|
|
689
|
-
] }),
|
|
690
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-x-4", children: [
|
|
691
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
692
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "width", children: "ความกว้าง (cm) *" }),
|
|
693
|
-
/* @__PURE__ */ jsx(
|
|
694
|
-
Input,
|
|
695
|
-
{
|
|
696
|
-
id: "width",
|
|
697
|
-
type: "number",
|
|
698
|
-
step: "0.1",
|
|
699
|
-
value: formData.width_cm,
|
|
700
|
-
onChange: (e) => setFormData({ ...formData, width_cm: e.target.value }),
|
|
701
|
-
placeholder: "20",
|
|
702
|
-
required: true
|
|
703
|
-
}
|
|
704
|
-
)
|
|
705
|
-
] }),
|
|
767
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
706
768
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
707
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
769
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "min_distance_km", children: "ระยะทางเริ่มต้น (กม.) *" }),
|
|
708
770
|
/* @__PURE__ */ jsx(
|
|
709
771
|
Input,
|
|
710
772
|
{
|
|
711
|
-
id: "
|
|
773
|
+
id: "min_distance_km",
|
|
712
774
|
type: "number",
|
|
775
|
+
min: "0",
|
|
713
776
|
step: "0.1",
|
|
714
|
-
value: formData.
|
|
715
|
-
onChange: (e) => setFormData({ ...formData,
|
|
716
|
-
placeholder: "30",
|
|
777
|
+
value: formData.min_distance_km,
|
|
778
|
+
onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
|
|
717
779
|
required: true
|
|
718
780
|
}
|
|
719
781
|
)
|
|
720
782
|
] }),
|
|
721
783
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
722
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
784
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "max_distance_km", children: "ระยะทางสูงสุด (กม.)" }),
|
|
723
785
|
/* @__PURE__ */ jsx(
|
|
724
786
|
Input,
|
|
725
787
|
{
|
|
726
|
-
id: "
|
|
788
|
+
id: "max_distance_km",
|
|
727
789
|
type: "number",
|
|
790
|
+
min: "0",
|
|
728
791
|
step: "0.1",
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
required: true
|
|
792
|
+
placeholder: "ปล่อยว่างหากไม่มีที่สิ้นสุด",
|
|
793
|
+
value: formData.max_distance_km,
|
|
794
|
+
onChange: (e) => setFormData({ ...formData, max_distance_km: e.target.value })
|
|
733
795
|
}
|
|
734
|
-
)
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
738
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
|
|
739
|
-
/* @__PURE__ */ jsx(
|
|
740
|
-
Input,
|
|
741
|
-
{
|
|
742
|
-
id: "maxWeight",
|
|
743
|
-
type: "number",
|
|
744
|
-
step: "0.1",
|
|
745
|
-
value: formData.max_weight_kg,
|
|
746
|
-
onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
|
|
747
|
-
placeholder: "0.5",
|
|
748
|
-
required: true
|
|
749
|
-
}
|
|
750
|
-
)
|
|
796
|
+
),
|
|
797
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
|
|
798
|
+
] })
|
|
751
799
|
] }),
|
|
752
800
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
753
801
|
/* @__PURE__ */ jsx(Label, { htmlFor: "price", children: "ราคา (THB) *" }),
|
|
@@ -756,14 +804,28 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
756
804
|
{
|
|
757
805
|
id: "price",
|
|
758
806
|
type: "number",
|
|
807
|
+
min: "0",
|
|
759
808
|
step: "0.01",
|
|
760
|
-
value: formData.
|
|
761
|
-
onChange: (e) => setFormData({ ...formData,
|
|
762
|
-
placeholder: "10.00",
|
|
809
|
+
value: formData.price,
|
|
810
|
+
onChange: (e) => setFormData({ ...formData, price: e.target.value }),
|
|
763
811
|
required: true
|
|
764
812
|
}
|
|
765
813
|
)
|
|
766
814
|
] }),
|
|
815
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
816
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
|
|
817
|
+
/* @__PURE__ */ jsx(
|
|
818
|
+
Input,
|
|
819
|
+
{
|
|
820
|
+
id: "priority",
|
|
821
|
+
type: "number",
|
|
822
|
+
value: formData.priority,
|
|
823
|
+
onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
|
|
824
|
+
placeholder: "0"
|
|
825
|
+
}
|
|
826
|
+
),
|
|
827
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
|
|
828
|
+
] }),
|
|
767
829
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
768
830
|
/* @__PURE__ */ jsx(
|
|
769
831
|
Switch,
|
|
@@ -776,197 +838,163 @@ const ParcelBoxModal = ({ box, onClose }) => {
|
|
|
776
838
|
/* @__PURE__ */ jsx(Label, { htmlFor: "active", children: "เปิดใช้งาน" })
|
|
777
839
|
] }),
|
|
778
840
|
/* @__PURE__ */ jsxs(Drawer.Footer, { children: [
|
|
779
|
-
/* @__PURE__ */ jsx(
|
|
841
|
+
/* @__PURE__ */ jsx(
|
|
842
|
+
Button,
|
|
843
|
+
{
|
|
844
|
+
type: "button",
|
|
845
|
+
variant: "secondary",
|
|
846
|
+
onClick: onClose,
|
|
847
|
+
disabled: isSaving,
|
|
848
|
+
children: "ยกเลิก"
|
|
849
|
+
}
|
|
850
|
+
),
|
|
780
851
|
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
|
|
781
852
|
] })
|
|
782
853
|
] }) })
|
|
783
854
|
] }) });
|
|
784
855
|
};
|
|
785
|
-
const
|
|
786
|
-
const [
|
|
787
|
-
const [filteredBoxes, setFilteredBoxes] = useState([]);
|
|
788
|
-
const [searchTerm, setSearchTerm] = useState("");
|
|
789
|
-
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
790
|
-
const [editingBox, setEditingBox] = useState(null);
|
|
856
|
+
const MessengerBasePricingTable = () => {
|
|
857
|
+
const [rates, setRates] = useState([]);
|
|
791
858
|
const [isLoading, setIsLoading] = useState(false);
|
|
792
|
-
const [
|
|
793
|
-
const [
|
|
859
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
860
|
+
const [editingRate, setEditingRate] = useState(null);
|
|
861
|
+
const [deleteId, setDeleteId] = useState(null);
|
|
794
862
|
useEffect(() => {
|
|
795
|
-
|
|
863
|
+
fetchRates();
|
|
796
864
|
}, []);
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
(
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
865
|
+
const orderedRates = useMemo(() => {
|
|
866
|
+
return [...rates].sort((a, b) => {
|
|
867
|
+
if (a.min_distance_km !== b.min_distance_km) {
|
|
868
|
+
return a.min_distance_km - b.min_distance_km;
|
|
869
|
+
}
|
|
870
|
+
const aMax = a.max_distance_km ?? Number.POSITIVE_INFINITY;
|
|
871
|
+
const bMax = b.max_distance_km ?? Number.POSITIVE_INFINITY;
|
|
872
|
+
return aMax - bMax;
|
|
873
|
+
});
|
|
874
|
+
}, [rates]);
|
|
875
|
+
const fetchRates = async () => {
|
|
804
876
|
setIsLoading(true);
|
|
805
877
|
try {
|
|
806
|
-
const response = await fetch(
|
|
807
|
-
|
|
808
|
-
|
|
878
|
+
const response = await fetch(
|
|
879
|
+
"/admin/base-shipping-prices?carrier_type=COMPANY_FLEET&service_code=MESSENGER_3H",
|
|
880
|
+
{
|
|
881
|
+
credentials: "include"
|
|
882
|
+
}
|
|
883
|
+
);
|
|
884
|
+
if (!response.ok) {
|
|
885
|
+
throw new Error("failed_to_load_base_rates");
|
|
886
|
+
}
|
|
809
887
|
const data = await response.json();
|
|
810
|
-
|
|
888
|
+
setRates(data.base_rates || []);
|
|
811
889
|
} catch (error) {
|
|
812
890
|
toast.error("ข้อผิดพลาด", {
|
|
813
|
-
description: "
|
|
891
|
+
description: "ไม่สามารถโหลดฐานราคาตามระยะทางได้"
|
|
814
892
|
});
|
|
815
893
|
} finally {
|
|
816
894
|
setIsLoading(false);
|
|
817
895
|
}
|
|
818
896
|
};
|
|
819
|
-
const handleToggleActive = async (box) => {
|
|
820
|
-
try {
|
|
821
|
-
const response = await fetch(`/admin/boxes/${box.id}`, {
|
|
822
|
-
method: "PUT",
|
|
823
|
-
headers: { "Content-Type": "application/json" },
|
|
824
|
-
credentials: "include",
|
|
825
|
-
body: JSON.stringify({ ...box, active: !box.active })
|
|
826
|
-
});
|
|
827
|
-
if (response.ok) {
|
|
828
|
-
toast.success("สำเร็จ", {
|
|
829
|
-
description: `${box.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}กล่อง ${box.name} แล้ว`
|
|
830
|
-
});
|
|
831
|
-
fetchBoxes();
|
|
832
|
-
} else {
|
|
833
|
-
throw new Error("Failed to update");
|
|
834
|
-
}
|
|
835
|
-
} catch (error) {
|
|
836
|
-
toast.error("ข้อผิดพลาด", {
|
|
837
|
-
description: "ไม่สามารถอัพเดทสถานะได้"
|
|
838
|
-
});
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
897
|
const handleDelete = async () => {
|
|
842
|
-
if (!
|
|
898
|
+
if (!deleteId) return;
|
|
843
899
|
try {
|
|
844
|
-
const response = await fetch(
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
} else {
|
|
854
|
-
throw new Error("Failed to delete");
|
|
900
|
+
const response = await fetch(
|
|
901
|
+
`/admin/base-shipping-prices/${deleteId}`,
|
|
902
|
+
{
|
|
903
|
+
method: "DELETE",
|
|
904
|
+
credentials: "include"
|
|
905
|
+
}
|
|
906
|
+
);
|
|
907
|
+
if (!response.ok) {
|
|
908
|
+
throw new Error("failed_to_delete");
|
|
855
909
|
}
|
|
910
|
+
toast.success("สำเร็จ", {
|
|
911
|
+
description: "ลบฐานราคาสำเร็จ"
|
|
912
|
+
});
|
|
913
|
+
fetchRates();
|
|
856
914
|
} catch (error) {
|
|
857
915
|
toast.error("ข้อผิดพลาด", {
|
|
858
|
-
description: "
|
|
916
|
+
description: "ไม่สามารถลบฐานราคาได้"
|
|
859
917
|
});
|
|
860
918
|
} finally {
|
|
861
|
-
|
|
862
|
-
setDeleteBoxName("");
|
|
919
|
+
setDeleteId(null);
|
|
863
920
|
}
|
|
864
921
|
};
|
|
865
|
-
const handleEdit = (box) => {
|
|
866
|
-
setEditingBox(box);
|
|
867
|
-
setIsModalOpen(true);
|
|
868
|
-
};
|
|
869
|
-
const handleCreateNew = () => {
|
|
870
|
-
setEditingBox(null);
|
|
871
|
-
setIsModalOpen(true);
|
|
872
|
-
};
|
|
873
922
|
const handleModalClose = () => {
|
|
874
923
|
setIsModalOpen(false);
|
|
875
|
-
|
|
876
|
-
|
|
924
|
+
setEditingRate(null);
|
|
925
|
+
fetchRates();
|
|
877
926
|
};
|
|
878
|
-
const
|
|
879
|
-
|
|
880
|
-
|
|
927
|
+
const formatDistanceRange = (rate) => {
|
|
928
|
+
const min = rate.min_distance_km ?? 0;
|
|
929
|
+
const max = rate.max_distance_km;
|
|
930
|
+
if (max === void 0 || max === null) {
|
|
931
|
+
return `${min}+ กม.`;
|
|
932
|
+
}
|
|
933
|
+
return `${min}-${max} กม.`;
|
|
881
934
|
};
|
|
882
935
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
883
|
-
/* @__PURE__ */ jsxs("div", { className: "flex
|
|
884
|
-
/* @__PURE__ */ jsxs("div", {
|
|
885
|
-
/* @__PURE__ */ jsx(
|
|
886
|
-
|
|
887
|
-
{
|
|
888
|
-
type: "search",
|
|
889
|
-
placeholder: "ค้นหาชื่อกล่อง...",
|
|
890
|
-
value: searchTerm,
|
|
891
|
-
onChange: (e) => setSearchTerm(e.target.value),
|
|
892
|
-
className: "max-w-md"
|
|
893
|
-
}
|
|
894
|
-
),
|
|
895
|
-
/* @__PURE__ */ jsxs(Button, { onClick: handleCreateNew, children: [
|
|
896
|
-
/* @__PURE__ */ jsx(Plus, {}),
|
|
897
|
-
"สร้างกล่องใหม่"
|
|
898
|
-
] })
|
|
936
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
937
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
938
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: "ฐานราคาตามระยะทาง (Messenger 3H)" }),
|
|
939
|
+
/* @__PURE__ */ jsx("p", { className: "text-ui-fg-subtle text-sm", children: "กำหนดราคาเริ่มต้นตามระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
|
|
899
940
|
] }),
|
|
900
|
-
/* @__PURE__ */ jsxs(
|
|
901
|
-
/* @__PURE__ */ jsx(
|
|
902
|
-
|
|
903
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ขนาด (กว้าง×ยาว×สูง cm)" }),
|
|
904
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "น้ำหนักสูงสุด (kg)" }),
|
|
905
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
906
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
907
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
908
|
-
] }) }),
|
|
909
|
-
/* @__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: [
|
|
910
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: box.name }),
|
|
911
|
-
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
912
|
-
box.width_cm,
|
|
913
|
-
" × ",
|
|
914
|
-
box.length_cm,
|
|
915
|
-
" × ",
|
|
916
|
-
box.height_cm
|
|
917
|
-
] }),
|
|
918
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: box.max_weight_kg }),
|
|
919
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: box.price_thb.toFixed(2) }),
|
|
920
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
|
|
921
|
-
Badge,
|
|
922
|
-
{
|
|
923
|
-
color: box.active ? "green" : "grey",
|
|
924
|
-
className: "cursor-pointer",
|
|
925
|
-
onClick: () => handleToggleActive(box),
|
|
926
|
-
children: box.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
927
|
-
}
|
|
928
|
-
) }),
|
|
929
|
-
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
930
|
-
/* @__PURE__ */ jsx(
|
|
931
|
-
Button,
|
|
932
|
-
{
|
|
933
|
-
variant: "transparent",
|
|
934
|
-
size: "small",
|
|
935
|
-
onClick: () => handleEdit(box),
|
|
936
|
-
children: /* @__PURE__ */ jsx(Edit, {})
|
|
937
|
-
}
|
|
938
|
-
),
|
|
939
|
-
/* @__PURE__ */ jsx(
|
|
940
|
-
Button,
|
|
941
|
-
{
|
|
942
|
-
variant: "transparent",
|
|
943
|
-
size: "small",
|
|
944
|
-
onClick: () => openDeletePrompt(box.id, box.name),
|
|
945
|
-
children: /* @__PURE__ */ jsx(Trash, {})
|
|
946
|
-
}
|
|
947
|
-
)
|
|
948
|
-
] }) })
|
|
949
|
-
] }, box.id)) })
|
|
941
|
+
/* @__PURE__ */ jsxs(Button, { onClick: () => setIsModalOpen(true), children: [
|
|
942
|
+
/* @__PURE__ */ jsx(Plus, {}),
|
|
943
|
+
"เพิ่มช่วงราคา"
|
|
950
944
|
] })
|
|
951
945
|
] }),
|
|
952
|
-
|
|
946
|
+
/* @__PURE__ */ jsxs(Table, { className: "mt-4", children: [
|
|
947
|
+
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
948
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ช่วงระยะทาง" }),
|
|
949
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
950
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ลำดับ" }),
|
|
951
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
952
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
953
|
+
] }) }),
|
|
954
|
+
/* @__PURE__ */ jsx(Table.Body, { children: isLoading ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) : orderedRates.length === 0 ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "ยังไม่ได้ตั้งค่าฐานราคาตามระยะทาง" }) }) : orderedRates.map((rate) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
955
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: formatDistanceRange(rate) }),
|
|
956
|
+
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
957
|
+
"฿",
|
|
958
|
+
rate.price.toFixed(2)
|
|
959
|
+
] }),
|
|
960
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: rate.priority ?? 0 }),
|
|
961
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
962
|
+
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
963
|
+
/* @__PURE__ */ jsx(
|
|
964
|
+
Button,
|
|
965
|
+
{
|
|
966
|
+
variant: "transparent",
|
|
967
|
+
size: "small",
|
|
968
|
+
onClick: () => {
|
|
969
|
+
setEditingRate(rate);
|
|
970
|
+
setIsModalOpen(true);
|
|
971
|
+
},
|
|
972
|
+
children: /* @__PURE__ */ jsx(Edit, {})
|
|
973
|
+
}
|
|
974
|
+
),
|
|
975
|
+
/* @__PURE__ */ jsx(
|
|
976
|
+
Button,
|
|
977
|
+
{
|
|
978
|
+
variant: "transparent",
|
|
979
|
+
size: "small",
|
|
980
|
+
onClick: () => setDeleteId(rate.id),
|
|
981
|
+
children: /* @__PURE__ */ jsx(Trash, {})
|
|
982
|
+
}
|
|
983
|
+
)
|
|
984
|
+
] }) })
|
|
985
|
+
] }, rate.id)) })
|
|
986
|
+
] }),
|
|
987
|
+
isModalOpen && /* @__PURE__ */ jsx(MessengerBaseRateModal, { rate: editingRate, onClose: handleModalClose }),
|
|
953
988
|
/* @__PURE__ */ jsx(
|
|
954
989
|
Prompt,
|
|
955
990
|
{
|
|
956
991
|
variant: "confirmation",
|
|
957
|
-
open: !!
|
|
958
|
-
onOpenChange: () =>
|
|
959
|
-
setDeleteBoxId(null);
|
|
960
|
-
setDeleteBoxName("");
|
|
961
|
-
},
|
|
992
|
+
open: !!deleteId,
|
|
993
|
+
onOpenChange: () => setDeleteId(null),
|
|
962
994
|
children: /* @__PURE__ */ jsxs(Prompt.Content, { children: [
|
|
963
995
|
/* @__PURE__ */ jsxs(Prompt.Header, { children: [
|
|
964
|
-
/* @__PURE__ */ jsx(Prompt.Title, { children: "
|
|
965
|
-
/* @__PURE__ */
|
|
966
|
-
'คุณต้องการลบกล่อง "',
|
|
967
|
-
deleteBoxName,
|
|
968
|
-
'" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
|
|
969
|
-
] })
|
|
996
|
+
/* @__PURE__ */ jsx(Prompt.Title, { children: "ลบฐานราคา" }),
|
|
997
|
+
/* @__PURE__ */ jsx(Prompt.Description, { children: "คุณต้องการลบช่วงราคานี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
|
|
970
998
|
] }),
|
|
971
999
|
/* @__PURE__ */ jsxs(Prompt.Footer, { children: [
|
|
972
1000
|
/* @__PURE__ */ jsx(Prompt.Cancel, { children: "ยกเลิก" }),
|
|
@@ -977,126 +1005,68 @@ const ParcelBoxesTable = () => {
|
|
|
977
1005
|
)
|
|
978
1006
|
] });
|
|
979
1007
|
};
|
|
980
|
-
const
|
|
981
|
-
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-
|
|
982
|
-
/* @__PURE__ */
|
|
983
|
-
|
|
1008
|
+
const BasePricingPage = () => {
|
|
1009
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-6", children: [
|
|
1010
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
1011
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "ฐานราคาตามระยะทาง (Messenger)" }),
|
|
1012
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "ตั้งราคาฐานสำหรับบริการ Messenger 3 ชั่วโมงตามช่วงระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม." })
|
|
1013
|
+
] }),
|
|
1014
|
+
/* @__PURE__ */ jsx(MessengerBasePricingTable, {})
|
|
984
1015
|
] }) });
|
|
985
1016
|
};
|
|
986
1017
|
const config$3 = defineRouteConfig({
|
|
987
|
-
icon:
|
|
988
|
-
label: "
|
|
1018
|
+
icon: Directions,
|
|
1019
|
+
label: "ฐานราคาตามระยะทาง"
|
|
989
1020
|
});
|
|
990
|
-
const
|
|
991
|
-
"กรุงเทพมหานคร",
|
|
992
|
-
"กระบี่",
|
|
993
|
-
"กาญจนบุรี",
|
|
994
|
-
"กาฬสินธุ์",
|
|
995
|
-
"กำแพงเพชร",
|
|
996
|
-
"ขอนแก่น",
|
|
997
|
-
"จันทบุรี",
|
|
998
|
-
"ฉะเชิงเทรา",
|
|
999
|
-
"ชลบุรี",
|
|
1000
|
-
"ชัยนาท",
|
|
1001
|
-
"ชัยภูมิ",
|
|
1002
|
-
"ชุมพร",
|
|
1003
|
-
"เชียงราย",
|
|
1004
|
-
"เชียงใหม่",
|
|
1005
|
-
"ตรัง",
|
|
1006
|
-
"ตราด",
|
|
1007
|
-
"ตาก",
|
|
1008
|
-
"นครนายก",
|
|
1009
|
-
"นครปฐม",
|
|
1010
|
-
"นครพนม",
|
|
1011
|
-
"นครราชสีมา",
|
|
1012
|
-
"นครศรีธรรมราช",
|
|
1013
|
-
"นครสวรรค์",
|
|
1014
|
-
"นนทบุรี",
|
|
1015
|
-
"นราธิวาส",
|
|
1016
|
-
"น่าน",
|
|
1017
|
-
"บึงกาฬ",
|
|
1018
|
-
"บุรีรัมย์",
|
|
1019
|
-
"ปทุมธานี",
|
|
1020
|
-
"ประจวบคีรีขันธ์",
|
|
1021
|
-
"ปราจีนบุรี",
|
|
1022
|
-
"ปัตตานี",
|
|
1023
|
-
"พระนครศรีอยุธยา",
|
|
1024
|
-
"พะเยา",
|
|
1025
|
-
"พังงา",
|
|
1026
|
-
"พัทลุง",
|
|
1027
|
-
"พิจิตร",
|
|
1028
|
-
"พิษณุโลก",
|
|
1029
|
-
"เพชรบุรี",
|
|
1030
|
-
"เพชรบูรณ์",
|
|
1031
|
-
"แพร่",
|
|
1032
|
-
"ภูเก็ต",
|
|
1033
|
-
"มหาสารคาม",
|
|
1034
|
-
"มุกดาหาร",
|
|
1035
|
-
"แม่ฮ่องสอน",
|
|
1036
|
-
"ยโสธร",
|
|
1037
|
-
"ยะลา",
|
|
1038
|
-
"ร้อยเอ็ด",
|
|
1039
|
-
"ระนอง",
|
|
1040
|
-
"ระยอง",
|
|
1041
|
-
"ราชบุรี",
|
|
1042
|
-
"ลพบุรี",
|
|
1043
|
-
"ลำปาง",
|
|
1044
|
-
"ลำพูน",
|
|
1045
|
-
"เลย",
|
|
1046
|
-
"ศรีสะเกษ",
|
|
1047
|
-
"สกลนคร",
|
|
1048
|
-
"สงขลา",
|
|
1049
|
-
"สตูล",
|
|
1050
|
-
"สมุทรปราการ",
|
|
1051
|
-
"สมุทรสงคราม",
|
|
1052
|
-
"สมุทรสาคร",
|
|
1053
|
-
"สระแก้ว",
|
|
1054
|
-
"สระบุรี",
|
|
1055
|
-
"สิงห์บุรี",
|
|
1056
|
-
"สุโขทัย",
|
|
1057
|
-
"สุพรรณบุรี",
|
|
1058
|
-
"สุราษฎร์ธานี",
|
|
1059
|
-
"สุรินทร์",
|
|
1060
|
-
"หนองคาย",
|
|
1061
|
-
"หนองบัวลำภู",
|
|
1062
|
-
"อ่างทอง",
|
|
1063
|
-
"อำนาจเจริญ",
|
|
1064
|
-
"อุดรธานี",
|
|
1065
|
-
"อุตรดิตถ์",
|
|
1066
|
-
"อุทัยธานี",
|
|
1067
|
-
"อุบลราชธานี"
|
|
1068
|
-
];
|
|
1069
|
-
const ServiceAreaModal = ({ area, onClose }) => {
|
|
1021
|
+
const ParcelBoxModal = ({ box, onClose }) => {
|
|
1070
1022
|
const [formData, setFormData] = useState({
|
|
1071
|
-
|
|
1072
|
-
|
|
1023
|
+
name: "",
|
|
1024
|
+
width_cm: "",
|
|
1025
|
+
length_cm: "",
|
|
1026
|
+
height_cm: "",
|
|
1027
|
+
max_weight_kg: "",
|
|
1028
|
+
price_thb: "",
|
|
1073
1029
|
active: true
|
|
1074
1030
|
});
|
|
1075
1031
|
const [isSaving, setIsSaving] = useState(false);
|
|
1076
1032
|
useEffect(() => {
|
|
1077
|
-
if (
|
|
1033
|
+
if (box) {
|
|
1078
1034
|
setFormData({
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1035
|
+
name: box.name,
|
|
1036
|
+
width_cm: box.width_cm.toString(),
|
|
1037
|
+
length_cm: box.length_cm.toString(),
|
|
1038
|
+
height_cm: box.height_cm.toString(),
|
|
1039
|
+
max_weight_kg: box.max_weight_kg.toString(),
|
|
1040
|
+
price_thb: box.price_thb.toString(),
|
|
1041
|
+
active: box.active
|
|
1082
1042
|
});
|
|
1083
1043
|
}
|
|
1084
|
-
}, [
|
|
1044
|
+
}, [box]);
|
|
1085
1045
|
const handleSubmit = async (e) => {
|
|
1086
1046
|
e.preventDefault();
|
|
1087
1047
|
const errors = [];
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
errors.push("กรุณากรอกค่า");
|
|
1048
|
+
if (!formData.name.trim()) {
|
|
1049
|
+
errors.push("กรุณากรอกชื่อกล่อง");
|
|
1091
1050
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1051
|
+
const width = parseFloat(formData.width_cm);
|
|
1052
|
+
const length = parseFloat(formData.length_cm);
|
|
1053
|
+
const height = parseFloat(formData.height_cm);
|
|
1054
|
+
const maxWeight = parseFloat(formData.max_weight_kg);
|
|
1055
|
+
const price = parseFloat(formData.price_thb);
|
|
1056
|
+
if (isNaN(width) || width <= 0) {
|
|
1057
|
+
errors.push("ความกว้างต้องเป็นตัวเลขมากกว่า 0");
|
|
1058
|
+
}
|
|
1059
|
+
if (isNaN(length) || length <= 0) {
|
|
1060
|
+
errors.push("ความยาวต้องเป็นตัวเลขมากกว่า 0");
|
|
1061
|
+
}
|
|
1062
|
+
if (isNaN(height) || height <= 0) {
|
|
1063
|
+
errors.push("ความสูงต้องเป็นตัวเลขมากกว่า 0");
|
|
1064
|
+
}
|
|
1065
|
+
if (isNaN(maxWeight) || maxWeight <= 0) {
|
|
1066
|
+
errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
|
|
1067
|
+
}
|
|
1068
|
+
if (isNaN(price) || price < 0) {
|
|
1069
|
+
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
1100
1070
|
}
|
|
1101
1071
|
if (errors.length > 0) {
|
|
1102
1072
|
toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
@@ -1107,12 +1077,16 @@ const ServiceAreaModal = ({ area, onClose }) => {
|
|
|
1107
1077
|
setIsSaving(true);
|
|
1108
1078
|
try {
|
|
1109
1079
|
const payload = {
|
|
1110
|
-
|
|
1111
|
-
|
|
1080
|
+
name: formData.name.trim(),
|
|
1081
|
+
width_cm: width,
|
|
1082
|
+
length_cm: length,
|
|
1083
|
+
height_cm: height,
|
|
1084
|
+
max_weight_kg: maxWeight,
|
|
1085
|
+
price_thb: price,
|
|
1112
1086
|
active: formData.active
|
|
1113
1087
|
};
|
|
1114
|
-
const url =
|
|
1115
|
-
const method =
|
|
1088
|
+
const url = box ? `/admin/boxes/${box.id}` : "/admin/boxes";
|
|
1089
|
+
const method = box ? "PUT" : "POST";
|
|
1116
1090
|
const response = await fetch(url, {
|
|
1117
1091
|
method,
|
|
1118
1092
|
headers: { "Content-Type": "application/json" },
|
|
@@ -1121,7 +1095,7 @@ const ServiceAreaModal = ({ area, onClose }) => {
|
|
|
1121
1095
|
});
|
|
1122
1096
|
if (response.ok) {
|
|
1123
1097
|
toast.success("สำเร็จ", {
|
|
1124
|
-
description:
|
|
1098
|
+
description: box ? "แก้ไขกล่องพัสดุแล้ว" : "สร้างกล่องพัสดุใหม่แล้ว"
|
|
1125
1099
|
});
|
|
1126
1100
|
onClose();
|
|
1127
1101
|
} else {
|
|
@@ -1137,53 +1111,97 @@ const ServiceAreaModal = ({ area, onClose }) => {
|
|
|
1137
1111
|
}
|
|
1138
1112
|
};
|
|
1139
1113
|
return /* @__PURE__ */ jsx(Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(Drawer.Content, { children: [
|
|
1140
|
-
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children:
|
|
1114
|
+
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: box ? "แก้ไขกล่องพัสดุ" : "สร้างกล่องพัสดุใหม่" }) }),
|
|
1141
1115
|
/* @__PURE__ */ jsx(Drawer.Body, { children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
1142
1116
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1143
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
1144
|
-
/* @__PURE__ */
|
|
1145
|
-
|
|
1117
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "name", children: "ชื่อกล่อง *" }),
|
|
1118
|
+
/* @__PURE__ */ jsx(
|
|
1119
|
+
Input,
|
|
1120
|
+
{
|
|
1121
|
+
id: "name",
|
|
1122
|
+
value: formData.name,
|
|
1123
|
+
onChange: (e) => setFormData({ ...formData, name: e.target.value }),
|
|
1124
|
+
placeholder: "เช่น กล่อง S, กล่อง M",
|
|
1125
|
+
required: true
|
|
1126
|
+
}
|
|
1127
|
+
)
|
|
1128
|
+
] }),
|
|
1129
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-x-4", children: [
|
|
1130
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1131
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "width", children: "ความกว้าง (cm) *" }),
|
|
1132
|
+
/* @__PURE__ */ jsx(
|
|
1133
|
+
Input,
|
|
1134
|
+
{
|
|
1135
|
+
id: "width",
|
|
1136
|
+
type: "number",
|
|
1137
|
+
step: "0.1",
|
|
1138
|
+
value: formData.width_cm,
|
|
1139
|
+
onChange: (e) => setFormData({ ...formData, width_cm: e.target.value }),
|
|
1140
|
+
placeholder: "20",
|
|
1141
|
+
required: true
|
|
1142
|
+
}
|
|
1143
|
+
)
|
|
1144
|
+
] }),
|
|
1145
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1146
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "length", children: "ความยาว (cm) *" }),
|
|
1147
|
+
/* @__PURE__ */ jsx(
|
|
1148
|
+
Input,
|
|
1149
|
+
{
|
|
1150
|
+
id: "length",
|
|
1151
|
+
type: "number",
|
|
1152
|
+
step: "0.1",
|
|
1153
|
+
value: formData.length_cm,
|
|
1154
|
+
onChange: (e) => setFormData({ ...formData, length_cm: e.target.value }),
|
|
1155
|
+
placeholder: "30",
|
|
1156
|
+
required: true
|
|
1157
|
+
}
|
|
1158
|
+
)
|
|
1159
|
+
] }),
|
|
1160
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1161
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "height", children: "ความสูง (cm) *" }),
|
|
1162
|
+
/* @__PURE__ */ jsx(
|
|
1163
|
+
Input,
|
|
1164
|
+
{
|
|
1165
|
+
id: "height",
|
|
1166
|
+
type: "number",
|
|
1167
|
+
step: "0.1",
|
|
1168
|
+
value: formData.height_cm,
|
|
1169
|
+
onChange: (e) => setFormData({ ...formData, height_cm: e.target.value }),
|
|
1170
|
+
placeholder: "15",
|
|
1171
|
+
required: true
|
|
1172
|
+
}
|
|
1173
|
+
)
|
|
1174
|
+
] })
|
|
1175
|
+
] }),
|
|
1176
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1177
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
|
|
1178
|
+
/* @__PURE__ */ jsx(
|
|
1179
|
+
Input,
|
|
1146
1180
|
{
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
/* @__PURE__ */ jsx(Select.Item, { value: "POSTCODE_PREFIX", children: "รหัสไปรษณีย์" })
|
|
1155
|
-
] })
|
|
1156
|
-
]
|
|
1181
|
+
id: "maxWeight",
|
|
1182
|
+
type: "number",
|
|
1183
|
+
step: "0.1",
|
|
1184
|
+
value: formData.max_weight_kg,
|
|
1185
|
+
onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
|
|
1186
|
+
placeholder: "0.5",
|
|
1187
|
+
required: true
|
|
1157
1188
|
}
|
|
1158
1189
|
)
|
|
1159
1190
|
] }),
|
|
1160
1191
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1161
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
1162
|
-
|
|
1163
|
-
Select,
|
|
1164
|
-
{
|
|
1165
|
-
value: formData.value,
|
|
1166
|
-
onValueChange: (value) => setFormData({ ...formData, value }),
|
|
1167
|
-
required: true,
|
|
1168
|
-
children: [
|
|
1169
|
-
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกจังหวัด" }) }),
|
|
1170
|
-
/* @__PURE__ */ jsx(Select.Content, { children: TH_PROVINCES.map((province) => /* @__PURE__ */ jsx(Select.Item, { value: province, children: province }, province)) })
|
|
1171
|
-
]
|
|
1172
|
-
}
|
|
1173
|
-
) : /* @__PURE__ */ jsx(
|
|
1192
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "price", children: "ราคา (THB) *" }),
|
|
1193
|
+
/* @__PURE__ */ jsx(
|
|
1174
1194
|
Input,
|
|
1175
1195
|
{
|
|
1176
|
-
id: "
|
|
1177
|
-
type: "
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
pattern: "\\d{1,5}",
|
|
1196
|
+
id: "price",
|
|
1197
|
+
type: "number",
|
|
1198
|
+
step: "0.01",
|
|
1199
|
+
value: formData.price_thb,
|
|
1200
|
+
onChange: (e) => setFormData({ ...formData, price_thb: e.target.value }),
|
|
1201
|
+
placeholder: "10.00",
|
|
1183
1202
|
required: true
|
|
1184
1203
|
}
|
|
1185
|
-
)
|
|
1186
|
-
formData.kind === "POSTCODE_PREFIX" && /* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: 'ใส่รหัส 1-5 หลักเพื่อครอบคลุมหลายพื้นที่ เช่น "10" = กรุงเทพฯ ทั้งหมด' })
|
|
1204
|
+
)
|
|
1187
1205
|
] }),
|
|
1188
1206
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
1189
1207
|
/* @__PURE__ */ jsx(
|
|
@@ -1197,81 +1215,59 @@ const ServiceAreaModal = ({ area, onClose }) => {
|
|
|
1197
1215
|
/* @__PURE__ */ jsx(Label, { htmlFor: "active", children: "เปิดใช้งาน" })
|
|
1198
1216
|
] }),
|
|
1199
1217
|
/* @__PURE__ */ jsxs(Drawer.Footer, { children: [
|
|
1200
|
-
/* @__PURE__ */ jsx(
|
|
1201
|
-
Button,
|
|
1202
|
-
{
|
|
1203
|
-
type: "button",
|
|
1204
|
-
variant: "secondary",
|
|
1205
|
-
onClick: onClose,
|
|
1206
|
-
disabled: isSaving,
|
|
1207
|
-
children: "ยกเลิก"
|
|
1208
|
-
}
|
|
1209
|
-
),
|
|
1218
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
|
|
1210
1219
|
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
|
|
1211
1220
|
] })
|
|
1212
1221
|
] }) })
|
|
1213
1222
|
] }) });
|
|
1214
1223
|
};
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
{ value: "POSTCODE_PREFIX", label: "รหัสไปรษณีย์" }
|
|
1219
|
-
];
|
|
1220
|
-
const ServiceAreasTable = () => {
|
|
1221
|
-
const [areas, setAreas] = useState([]);
|
|
1222
|
-
const [filteredAreas, setFilteredAreas] = useState([]);
|
|
1224
|
+
const ParcelBoxesTable = () => {
|
|
1225
|
+
const [boxes, setBoxes] = useState([]);
|
|
1226
|
+
const [filteredBoxes, setFilteredBoxes] = useState([]);
|
|
1223
1227
|
const [searchTerm, setSearchTerm] = useState("");
|
|
1224
|
-
const [kindFilter, setKindFilter] = useState("ALL");
|
|
1225
1228
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
1226
|
-
const [
|
|
1229
|
+
const [editingBox, setEditingBox] = useState(null);
|
|
1227
1230
|
const [isLoading, setIsLoading] = useState(false);
|
|
1228
|
-
const [
|
|
1229
|
-
const [
|
|
1231
|
+
const [deleteBoxId, setDeleteBoxId] = useState(null);
|
|
1232
|
+
const [deleteBoxName, setDeleteBoxName] = useState("");
|
|
1230
1233
|
useEffect(() => {
|
|
1231
|
-
|
|
1234
|
+
fetchBoxes();
|
|
1232
1235
|
}, []);
|
|
1233
1236
|
useEffect(() => {
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
(area) => area.value.toLowerCase().includes(searchTerm.toLowerCase())
|
|
1241
|
-
);
|
|
1242
|
-
}
|
|
1243
|
-
setFilteredAreas(filtered);
|
|
1244
|
-
}, [searchTerm, kindFilter, areas]);
|
|
1245
|
-
const fetchAreas = async () => {
|
|
1237
|
+
const filtered = boxes.filter(
|
|
1238
|
+
(box) => box.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
1239
|
+
);
|
|
1240
|
+
setFilteredBoxes(filtered);
|
|
1241
|
+
}, [searchTerm, boxes]);
|
|
1242
|
+
const fetchBoxes = async () => {
|
|
1246
1243
|
setIsLoading(true);
|
|
1247
1244
|
try {
|
|
1248
|
-
const response = await fetch("/admin/
|
|
1245
|
+
const response = await fetch("/admin/boxes", {
|
|
1249
1246
|
credentials: "include"
|
|
1250
1247
|
});
|
|
1251
1248
|
const data = await response.json();
|
|
1252
|
-
|
|
1253
|
-
setAreas(data.service_areas || []);
|
|
1249
|
+
setBoxes(data.boxes || []);
|
|
1254
1250
|
} catch (error) {
|
|
1255
1251
|
toast.error("ข้อผิดพลาด", {
|
|
1256
|
-
description: "
|
|
1252
|
+
description: "ไม่สามารถโหลดข้อมูลกล่องพัสดุได้"
|
|
1257
1253
|
});
|
|
1258
1254
|
} finally {
|
|
1259
1255
|
setIsLoading(false);
|
|
1260
1256
|
}
|
|
1261
1257
|
};
|
|
1262
|
-
const handleToggleActive = async (
|
|
1258
|
+
const handleToggleActive = async (box) => {
|
|
1263
1259
|
try {
|
|
1264
|
-
const response = await fetch(`/admin/
|
|
1260
|
+
const response = await fetch(`/admin/boxes/${box.id}`, {
|
|
1265
1261
|
method: "PUT",
|
|
1266
1262
|
headers: { "Content-Type": "application/json" },
|
|
1267
1263
|
credentials: "include",
|
|
1268
|
-
body: JSON.stringify({ ...
|
|
1264
|
+
body: JSON.stringify({ ...box, active: !box.active })
|
|
1269
1265
|
});
|
|
1270
1266
|
if (response.ok) {
|
|
1271
1267
|
toast.success("สำเร็จ", {
|
|
1272
|
-
description: `${
|
|
1268
|
+
description: `${box.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}กล่อง ${box.name} แล้ว`
|
|
1273
1269
|
});
|
|
1274
|
-
|
|
1270
|
+
fetchBoxes();
|
|
1275
1271
|
} else {
|
|
1276
1272
|
throw new Error("Failed to update");
|
|
1277
1273
|
}
|
|
@@ -1282,87 +1278,91 @@ const ServiceAreasTable = () => {
|
|
|
1282
1278
|
}
|
|
1283
1279
|
};
|
|
1284
1280
|
const handleDelete = async () => {
|
|
1285
|
-
if (!
|
|
1281
|
+
if (!deleteBoxId) return;
|
|
1286
1282
|
try {
|
|
1287
|
-
const response = await fetch(`/admin/
|
|
1283
|
+
const response = await fetch(`/admin/boxes/${deleteBoxId}`, {
|
|
1288
1284
|
method: "DELETE",
|
|
1289
1285
|
credentials: "include"
|
|
1290
1286
|
});
|
|
1291
1287
|
if (response.ok) {
|
|
1292
1288
|
toast.success("สำเร็จ", {
|
|
1293
|
-
description:
|
|
1289
|
+
description: `ลบกล่อง ${deleteBoxName} แล้ว`
|
|
1294
1290
|
});
|
|
1295
|
-
|
|
1291
|
+
fetchBoxes();
|
|
1296
1292
|
} else {
|
|
1297
1293
|
throw new Error("Failed to delete");
|
|
1298
1294
|
}
|
|
1299
1295
|
} catch (error) {
|
|
1300
1296
|
toast.error("ข้อผิดพลาด", {
|
|
1301
|
-
description: "
|
|
1297
|
+
description: "ไม่สามารถลบกล่องได้"
|
|
1302
1298
|
});
|
|
1303
1299
|
} finally {
|
|
1304
|
-
|
|
1305
|
-
|
|
1300
|
+
setDeleteBoxId(null);
|
|
1301
|
+
setDeleteBoxName("");
|
|
1306
1302
|
}
|
|
1307
1303
|
};
|
|
1308
|
-
const handleEdit = (
|
|
1309
|
-
|
|
1304
|
+
const handleEdit = (box) => {
|
|
1305
|
+
setEditingBox(box);
|
|
1310
1306
|
setIsModalOpen(true);
|
|
1311
1307
|
};
|
|
1312
1308
|
const handleCreateNew = () => {
|
|
1313
|
-
|
|
1309
|
+
setEditingBox(null);
|
|
1314
1310
|
setIsModalOpen(true);
|
|
1315
1311
|
};
|
|
1316
1312
|
const handleModalClose = () => {
|
|
1317
1313
|
setIsModalOpen(false);
|
|
1318
|
-
|
|
1319
|
-
|
|
1314
|
+
setEditingBox(null);
|
|
1315
|
+
fetchBoxes();
|
|
1320
1316
|
};
|
|
1321
|
-
const openDeletePrompt = (id,
|
|
1322
|
-
|
|
1323
|
-
|
|
1317
|
+
const openDeletePrompt = (id, name) => {
|
|
1318
|
+
setDeleteBoxId(id);
|
|
1319
|
+
setDeleteBoxName(name);
|
|
1324
1320
|
};
|
|
1325
1321
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1326
1322
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1327
1323
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
1328
|
-
/* @__PURE__ */
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
),
|
|
1339
|
-
/* @__PURE__ */ jsxs(Select, { value: kindFilter, onValueChange: setKindFilter, children: [
|
|
1340
|
-
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "ประเภท" }) }),
|
|
1341
|
-
/* @__PURE__ */ jsx(Select.Content, { children: AREA_KINDS.map((kind) => /* @__PURE__ */ jsx(Select.Item, { value: kind.value, children: kind.label }, kind.value)) })
|
|
1342
|
-
] })
|
|
1343
|
-
] }),
|
|
1324
|
+
/* @__PURE__ */ jsx(
|
|
1325
|
+
Input,
|
|
1326
|
+
{
|
|
1327
|
+
type: "search",
|
|
1328
|
+
placeholder: "ค้นหาชื่อกล่อง...",
|
|
1329
|
+
value: searchTerm,
|
|
1330
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
1331
|
+
className: "max-w-md"
|
|
1332
|
+
}
|
|
1333
|
+
),
|
|
1344
1334
|
/* @__PURE__ */ jsxs(Button, { onClick: handleCreateNew, children: [
|
|
1345
1335
|
/* @__PURE__ */ jsx(Plus, {}),
|
|
1346
|
-
"
|
|
1336
|
+
"สร้างกล่องใหม่"
|
|
1347
1337
|
] })
|
|
1348
1338
|
] }),
|
|
1349
1339
|
/* @__PURE__ */ jsxs(Table, { children: [
|
|
1350
1340
|
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1351
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "
|
|
1352
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "
|
|
1341
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ชื่อ" }),
|
|
1342
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ขนาด (กว้าง×ยาว×สูง cm)" }),
|
|
1343
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "น้ำหนักสูงสุด (kg)" }),
|
|
1344
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
1353
1345
|
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
1354
1346
|
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
1355
1347
|
] }) }),
|
|
1356
|
-
/* @__PURE__ */ jsx(Table.Body, { children: isLoading ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) :
|
|
1357
|
-
/* @__PURE__ */ jsx(Table.Cell, { children:
|
|
1358
|
-
/* @__PURE__ */
|
|
1348
|
+
/* @__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: [
|
|
1349
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: box.name }),
|
|
1350
|
+
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
1351
|
+
box.width_cm,
|
|
1352
|
+
" × ",
|
|
1353
|
+
box.length_cm,
|
|
1354
|
+
" × ",
|
|
1355
|
+
box.height_cm
|
|
1356
|
+
] }),
|
|
1357
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: box.max_weight_kg }),
|
|
1358
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: box.price_thb.toFixed(2) }),
|
|
1359
1359
|
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
|
|
1360
1360
|
Badge,
|
|
1361
1361
|
{
|
|
1362
|
-
color:
|
|
1362
|
+
color: box.active ? "green" : "grey",
|
|
1363
1363
|
className: "cursor-pointer",
|
|
1364
|
-
onClick: () => handleToggleActive(
|
|
1365
|
-
children:
|
|
1364
|
+
onClick: () => handleToggleActive(box),
|
|
1365
|
+
children: box.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
1366
1366
|
}
|
|
1367
1367
|
) }),
|
|
1368
1368
|
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
@@ -1371,7 +1371,7 @@ const ServiceAreasTable = () => {
|
|
|
1371
1371
|
{
|
|
1372
1372
|
variant: "transparent",
|
|
1373
1373
|
size: "small",
|
|
1374
|
-
onClick: () => handleEdit(
|
|
1374
|
+
onClick: () => handleEdit(box),
|
|
1375
1375
|
children: /* @__PURE__ */ jsx(Edit, {})
|
|
1376
1376
|
}
|
|
1377
1377
|
),
|
|
@@ -1380,30 +1380,30 @@ const ServiceAreasTable = () => {
|
|
|
1380
1380
|
{
|
|
1381
1381
|
variant: "transparent",
|
|
1382
1382
|
size: "small",
|
|
1383
|
-
onClick: () => openDeletePrompt(
|
|
1383
|
+
onClick: () => openDeletePrompt(box.id, box.name),
|
|
1384
1384
|
children: /* @__PURE__ */ jsx(Trash, {})
|
|
1385
1385
|
}
|
|
1386
1386
|
)
|
|
1387
1387
|
] }) })
|
|
1388
|
-
] },
|
|
1388
|
+
] }, box.id)) })
|
|
1389
1389
|
] })
|
|
1390
1390
|
] }),
|
|
1391
|
-
isModalOpen && /* @__PURE__ */ jsx(
|
|
1391
|
+
isModalOpen && /* @__PURE__ */ jsx(ParcelBoxModal, { box: editingBox, onClose: handleModalClose }),
|
|
1392
1392
|
/* @__PURE__ */ jsx(
|
|
1393
1393
|
Prompt,
|
|
1394
1394
|
{
|
|
1395
1395
|
variant: "confirmation",
|
|
1396
|
-
open: !!
|
|
1396
|
+
open: !!deleteBoxId,
|
|
1397
1397
|
onOpenChange: () => {
|
|
1398
|
-
|
|
1399
|
-
|
|
1398
|
+
setDeleteBoxId(null);
|
|
1399
|
+
setDeleteBoxName("");
|
|
1400
1400
|
},
|
|
1401
1401
|
children: /* @__PURE__ */ jsxs(Prompt.Content, { children: [
|
|
1402
1402
|
/* @__PURE__ */ jsxs(Prompt.Header, { children: [
|
|
1403
|
-
/* @__PURE__ */ jsx(Prompt.Title, { children: "
|
|
1403
|
+
/* @__PURE__ */ jsx(Prompt.Title, { children: "ลบกล่องพัสดุ" }),
|
|
1404
1404
|
/* @__PURE__ */ jsxs(Prompt.Description, { children: [
|
|
1405
|
-
'
|
|
1406
|
-
|
|
1405
|
+
'คุณต้องการลบกล่อง "',
|
|
1406
|
+
deleteBoxName,
|
|
1407
1407
|
'" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
|
|
1408
1408
|
] })
|
|
1409
1409
|
] }),
|
|
@@ -1416,63 +1416,98 @@ const ServiceAreasTable = () => {
|
|
|
1416
1416
|
)
|
|
1417
1417
|
] });
|
|
1418
1418
|
};
|
|
1419
|
-
const
|
|
1419
|
+
const BoxesPage = () => {
|
|
1420
1420
|
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1421
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "
|
|
1422
|
-
/* @__PURE__ */ jsx(
|
|
1421
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "กล่องพัสดุ" }) }),
|
|
1422
|
+
/* @__PURE__ */ jsx(ParcelBoxesTable, {})
|
|
1423
1423
|
] }) });
|
|
1424
1424
|
};
|
|
1425
1425
|
const config$2 = defineRouteConfig({
|
|
1426
|
-
icon:
|
|
1427
|
-
label: "
|
|
1426
|
+
icon: ArchiveBox,
|
|
1427
|
+
label: "กล่องพัสดุ"
|
|
1428
1428
|
});
|
|
1429
|
-
const
|
|
1430
|
-
{ value: "
|
|
1431
|
-
{ value: "
|
|
1432
|
-
{ value: "
|
|
1433
|
-
{ value: "TAPE", label: "เทป/กาว" },
|
|
1434
|
-
{ value: "LABEL", label: "ฉลาก/สติกเกอร์" },
|
|
1435
|
-
{ value: "OTHER", label: "อื่นๆ" }
|
|
1429
|
+
const CARRIER_TYPES$1 = [
|
|
1430
|
+
{ value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
|
|
1431
|
+
{ value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
|
|
1432
|
+
{ value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
|
|
1436
1433
|
];
|
|
1437
|
-
const
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1434
|
+
const SERVICE_CODES$1 = [
|
|
1435
|
+
{ value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม.", usesHours: true },
|
|
1436
|
+
{ value: "SAME_DAY", label: "ส่งด่วนภายในวัน", usesHours: true },
|
|
1437
|
+
{ value: "STANDARD_3_5D", label: "EMS 3-5 วัน", usesHours: false },
|
|
1438
|
+
{ value: "EXPRESS_1_2D", label: "Express 1-2 วัน", usesHours: false }
|
|
1439
|
+
];
|
|
1440
|
+
const ShippingRateModal = ({ rate, onClose }) => {
|
|
1441
1441
|
const [formData, setFormData] = useState({
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1442
|
+
carrier_type: "COMPANY_FLEET",
|
|
1443
|
+
service_code: "MESSENGER_3H",
|
|
1444
|
+
max_weight_kg: "",
|
|
1445
|
+
price: "",
|
|
1445
1446
|
currency: "THB",
|
|
1446
|
-
|
|
1447
|
-
|
|
1447
|
+
priority: "0",
|
|
1448
|
+
eta_hours_min: "",
|
|
1449
|
+
eta_hours_max: "",
|
|
1450
|
+
eta_days_min: "",
|
|
1451
|
+
eta_days_max: "",
|
|
1448
1452
|
active: true
|
|
1449
1453
|
});
|
|
1450
1454
|
const [isSaving, setIsSaving] = useState(false);
|
|
1455
|
+
const selectedService = SERVICE_CODES$1.find((s) => s.value === formData.service_code);
|
|
1456
|
+
const usesHours = (selectedService == null ? void 0 : selectedService.usesHours) ?? false;
|
|
1457
|
+
const isMessenger = formData.carrier_type === "COMPANY_FLEET" && formData.service_code === "MESSENGER_3H";
|
|
1451
1458
|
useEffect(() => {
|
|
1452
|
-
|
|
1459
|
+
var _a, _b, _c, _d;
|
|
1460
|
+
if (rate) {
|
|
1453
1461
|
setFormData({
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1462
|
+
carrier_type: rate.carrier_type,
|
|
1463
|
+
service_code: rate.service_code,
|
|
1464
|
+
max_weight_kg: rate.max_weight_kg.toString(),
|
|
1465
|
+
price: rate.price.toString(),
|
|
1466
|
+
currency: rate.currency,
|
|
1467
|
+
priority: (rate.priority ?? 0).toString(),
|
|
1468
|
+
eta_hours_min: ((_a = rate.eta_hours_min) == null ? void 0 : _a.toString()) || "",
|
|
1469
|
+
eta_hours_max: ((_b = rate.eta_hours_max) == null ? void 0 : _b.toString()) || "",
|
|
1470
|
+
eta_days_min: ((_c = rate.eta_days_min) == null ? void 0 : _c.toString()) || "",
|
|
1471
|
+
eta_days_max: ((_d = rate.eta_days_max) == null ? void 0 : _d.toString()) || "",
|
|
1472
|
+
active: rate.active
|
|
1461
1473
|
});
|
|
1462
1474
|
}
|
|
1463
|
-
}, [
|
|
1475
|
+
}, [rate]);
|
|
1464
1476
|
const handleSubmit = async (e) => {
|
|
1465
1477
|
e.preventDefault();
|
|
1466
1478
|
const errors = [];
|
|
1467
|
-
|
|
1468
|
-
|
|
1479
|
+
const maxWeight = parseFloat(formData.max_weight_kg);
|
|
1480
|
+
const price = parseFloat(formData.price);
|
|
1481
|
+
const priority = parseInt(formData.priority);
|
|
1482
|
+
if (isNaN(maxWeight) || maxWeight <= 0) {
|
|
1483
|
+
errors.push("น้ำหนักสูงสุดต้องเป็นตัวเลขมากกว่า 0");
|
|
1469
1484
|
}
|
|
1470
|
-
if (
|
|
1471
|
-
errors.push("
|
|
1485
|
+
if (isNaN(price) || price < 0) {
|
|
1486
|
+
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
1472
1487
|
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1488
|
+
if (isNaN(priority)) {
|
|
1489
|
+
errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
|
|
1490
|
+
}
|
|
1491
|
+
let etaHoursMin;
|
|
1492
|
+
let etaHoursMax;
|
|
1493
|
+
let etaDaysMin;
|
|
1494
|
+
let etaDaysMax;
|
|
1495
|
+
if (usesHours) {
|
|
1496
|
+
etaHoursMin = formData.eta_hours_min ? parseFloat(formData.eta_hours_min) : void 0;
|
|
1497
|
+
etaHoursMax = formData.eta_hours_max ? parseFloat(formData.eta_hours_max) : void 0;
|
|
1498
|
+
if (etaHoursMin === void 0 || etaHoursMax === void 0) {
|
|
1499
|
+
errors.push("กรุณากรอก ETA เป็นชั่วโมงให้ครบ");
|
|
1500
|
+
} else if (etaHoursMin > etaHoursMax) {
|
|
1501
|
+
errors.push("ETA ชั่วโมงต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
|
|
1502
|
+
}
|
|
1503
|
+
} else {
|
|
1504
|
+
etaDaysMin = formData.eta_days_min ? parseFloat(formData.eta_days_min) : void 0;
|
|
1505
|
+
etaDaysMax = formData.eta_days_max ? parseFloat(formData.eta_days_max) : void 0;
|
|
1506
|
+
if (etaDaysMin === void 0 || etaDaysMax === void 0) {
|
|
1507
|
+
errors.push("กรุณากรอก ETA เป็นวันให้ครบ");
|
|
1508
|
+
} else if (etaDaysMin > etaDaysMax) {
|
|
1509
|
+
errors.push("ETA วันต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
|
|
1510
|
+
}
|
|
1476
1511
|
}
|
|
1477
1512
|
if (errors.length > 0) {
|
|
1478
1513
|
toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
@@ -1483,16 +1518,23 @@ const MaterialCostModal = ({
|
|
|
1483
1518
|
setIsSaving(true);
|
|
1484
1519
|
try {
|
|
1485
1520
|
const payload = {
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1521
|
+
carrier_type: formData.carrier_type,
|
|
1522
|
+
service_code: formData.service_code,
|
|
1523
|
+
max_weight_kg: maxWeight,
|
|
1524
|
+
price,
|
|
1489
1525
|
currency: formData.currency,
|
|
1490
|
-
|
|
1491
|
-
description: formData.description.trim() || void 0,
|
|
1526
|
+
priority,
|
|
1492
1527
|
active: formData.active
|
|
1493
1528
|
};
|
|
1494
|
-
|
|
1495
|
-
|
|
1529
|
+
if (usesHours) {
|
|
1530
|
+
payload.eta_hours_min = etaHoursMin;
|
|
1531
|
+
payload.eta_hours_max = etaHoursMax;
|
|
1532
|
+
} else {
|
|
1533
|
+
payload.eta_days_min = etaDaysMin;
|
|
1534
|
+
payload.eta_days_max = etaDaysMax;
|
|
1535
|
+
}
|
|
1536
|
+
const url = rate ? `/admin/shipping-rates/${rate.id}` : "/admin/shipping-rates";
|
|
1537
|
+
const method = rate ? "PUT" : "POST";
|
|
1496
1538
|
const response = await fetch(url, {
|
|
1497
1539
|
method,
|
|
1498
1540
|
headers: { "Content-Type": "application/json" },
|
|
@@ -1501,7 +1543,7 @@ const MaterialCostModal = ({
|
|
|
1501
1543
|
});
|
|
1502
1544
|
if (response.ok) {
|
|
1503
1545
|
toast.success("สำเร็จ", {
|
|
1504
|
-
description:
|
|
1546
|
+
description: rate ? "แก้ไขอัตราค่าขนส่งแล้ว" : "สร้างอัตราค่าขนส่งใหม่แล้ว"
|
|
1505
1547
|
});
|
|
1506
1548
|
onClose();
|
|
1507
1549
|
} else {
|
|
@@ -1517,89 +1559,147 @@ const MaterialCostModal = ({
|
|
|
1517
1559
|
}
|
|
1518
1560
|
};
|
|
1519
1561
|
return /* @__PURE__ */ jsx(Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(Drawer.Content, { children: [
|
|
1520
|
-
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children:
|
|
1562
|
+
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: rate ? "แก้ไขอัตราค่าขนส่ง" : "สร้างอัตราค่าขนส่งใหม่" }) }),
|
|
1521
1563
|
/* @__PURE__ */ jsx(Drawer.Body, { children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
1522
1564
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1523
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
1524
|
-
/* @__PURE__ */
|
|
1525
|
-
|
|
1565
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "carrier", children: "ประเภทขนส่ง *" }),
|
|
1566
|
+
/* @__PURE__ */ jsxs(
|
|
1567
|
+
Select,
|
|
1526
1568
|
{
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1569
|
+
value: formData.carrier_type,
|
|
1570
|
+
onValueChange: (value) => setFormData({ ...formData, carrier_type: value }),
|
|
1571
|
+
children: [
|
|
1572
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกประเภทขนส่ง" }) }),
|
|
1573
|
+
/* @__PURE__ */ jsx(Select.Content, { children: CARRIER_TYPES$1.map((carrier) => /* @__PURE__ */ jsx(Select.Item, { value: carrier.value, children: carrier.label }, carrier.value)) })
|
|
1574
|
+
]
|
|
1532
1575
|
}
|
|
1533
1576
|
)
|
|
1534
1577
|
] }),
|
|
1535
1578
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1536
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
1579
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "service", children: "บริการจัดส่ง *" }),
|
|
1537
1580
|
/* @__PURE__ */ jsxs(
|
|
1538
1581
|
Select,
|
|
1539
1582
|
{
|
|
1540
|
-
value: formData.
|
|
1541
|
-
onValueChange: (value) => setFormData({
|
|
1583
|
+
value: formData.service_code,
|
|
1584
|
+
onValueChange: (value) => setFormData({
|
|
1585
|
+
...formData,
|
|
1586
|
+
service_code: value,
|
|
1587
|
+
eta_hours_min: "",
|
|
1588
|
+
eta_hours_max: "",
|
|
1589
|
+
eta_days_min: "",
|
|
1590
|
+
eta_days_max: ""
|
|
1591
|
+
}),
|
|
1542
1592
|
children: [
|
|
1543
|
-
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "
|
|
1544
|
-
/* @__PURE__ */ jsx(Select.Content, { children:
|
|
1593
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกบริการ" }) }),
|
|
1594
|
+
/* @__PURE__ */ jsx(Select.Content, { children: SERVICE_CODES$1.map((service) => /* @__PURE__ */ jsx(Select.Item, { value: service.value, children: service.label }, service.value)) })
|
|
1545
1595
|
]
|
|
1546
1596
|
}
|
|
1547
1597
|
)
|
|
1548
1598
|
] }),
|
|
1599
|
+
isMessenger && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3 text-sm space-y-1", children: [
|
|
1600
|
+
/* @__PURE__ */ jsx("div", { className: "font-semibold text-ui-fg-base", children: "ฐานราคาตามระยะทาง (Messenger)" }),
|
|
1601
|
+
/* @__PURE__ */ jsx("div", { className: "text-ui-fg-subtle", children: "ตั้งช่วงราคาตามระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม. สำหรับบริการส่งด่วน 3 ชม." }),
|
|
1602
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, { asChild: true, size: "small", variant: "secondary", children: /* @__PURE__ */ jsxs("a", { href: "/base-pricing", target: "_blank", rel: "noreferrer", children: [
|
|
1603
|
+
"จัดการฐานราคา",
|
|
1604
|
+
/* @__PURE__ */ jsx(ExternalLink, { className: "ml-1 h-4 w-4" })
|
|
1605
|
+
] }) }) })
|
|
1606
|
+
] }),
|
|
1549
1607
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
1550
1608
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1551
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
1609
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "maxWeight", children: "น้ำหนักสูงสุด (kg) *" }),
|
|
1552
1610
|
/* @__PURE__ */ jsx(
|
|
1553
1611
|
Input,
|
|
1554
1612
|
{
|
|
1555
|
-
id: "
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1613
|
+
id: "maxWeight",
|
|
1614
|
+
type: "number",
|
|
1615
|
+
step: "0.1",
|
|
1616
|
+
min: "0",
|
|
1617
|
+
value: formData.max_weight_kg,
|
|
1618
|
+
onChange: (e) => setFormData({ ...formData, max_weight_kg: e.target.value }),
|
|
1619
|
+
placeholder: "เช่น 3",
|
|
1559
1620
|
required: true
|
|
1560
1621
|
}
|
|
1561
1622
|
)
|
|
1562
1623
|
] }),
|
|
1563
1624
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1564
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
1625
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "price", children: "ราคา (THB) *" }),
|
|
1565
1626
|
/* @__PURE__ */ jsx(
|
|
1566
1627
|
Input,
|
|
1567
1628
|
{
|
|
1568
|
-
id: "
|
|
1629
|
+
id: "price",
|
|
1569
1630
|
type: "number",
|
|
1570
1631
|
step: "0.01",
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1632
|
+
min: "0",
|
|
1633
|
+
value: formData.price,
|
|
1634
|
+
onChange: (e) => setFormData({ ...formData, price: e.target.value }),
|
|
1635
|
+
placeholder: "เช่น 80",
|
|
1574
1636
|
required: true
|
|
1575
1637
|
}
|
|
1576
1638
|
)
|
|
1577
1639
|
] })
|
|
1578
1640
|
] }),
|
|
1579
1641
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1580
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
1642
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
|
|
1581
1643
|
/* @__PURE__ */ jsx(
|
|
1582
1644
|
Input,
|
|
1583
1645
|
{
|
|
1584
|
-
id: "
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1646
|
+
id: "priority",
|
|
1647
|
+
type: "number",
|
|
1648
|
+
value: formData.priority,
|
|
1649
|
+
onChange: (e) => setFormData({ ...formData, priority: e.target.value }),
|
|
1650
|
+
placeholder: "0"
|
|
1588
1651
|
}
|
|
1589
|
-
)
|
|
1652
|
+
),
|
|
1653
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ตัวเลขสูงกว่าจะถูกเสนอให้เลือกก่อน (ค่าเริ่มต้น 0)" })
|
|
1590
1654
|
] }),
|
|
1591
1655
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1592
|
-
/* @__PURE__ */
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1656
|
+
/* @__PURE__ */ jsxs(Label, { children: [
|
|
1657
|
+
usesHours ? "ETA (ชั่วโมง)" : "ETA (วัน)",
|
|
1658
|
+
" *"
|
|
1659
|
+
] }),
|
|
1660
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-x-4 mt-2", children: usesHours ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1661
|
+
/* @__PURE__ */ jsx(
|
|
1662
|
+
Input,
|
|
1663
|
+
{
|
|
1664
|
+
type: "number",
|
|
1665
|
+
placeholder: "ต่ำสุด",
|
|
1666
|
+
value: formData.eta_hours_min,
|
|
1667
|
+
onChange: (e) => setFormData({ ...formData, eta_hours_min: e.target.value }),
|
|
1668
|
+
required: true
|
|
1669
|
+
}
|
|
1670
|
+
),
|
|
1671
|
+
/* @__PURE__ */ jsx(
|
|
1672
|
+
Input,
|
|
1673
|
+
{
|
|
1674
|
+
type: "number",
|
|
1675
|
+
placeholder: "สูงสุด",
|
|
1676
|
+
value: formData.eta_hours_max,
|
|
1677
|
+
onChange: (e) => setFormData({ ...formData, eta_hours_max: e.target.value }),
|
|
1678
|
+
required: true
|
|
1679
|
+
}
|
|
1680
|
+
)
|
|
1681
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1682
|
+
/* @__PURE__ */ jsx(
|
|
1683
|
+
Input,
|
|
1684
|
+
{
|
|
1685
|
+
type: "number",
|
|
1686
|
+
placeholder: "ต่ำสุด",
|
|
1687
|
+
value: formData.eta_days_min,
|
|
1688
|
+
onChange: (e) => setFormData({ ...formData, eta_days_min: e.target.value }),
|
|
1689
|
+
required: true
|
|
1690
|
+
}
|
|
1691
|
+
),
|
|
1692
|
+
/* @__PURE__ */ jsx(
|
|
1693
|
+
Input,
|
|
1694
|
+
{
|
|
1695
|
+
type: "number",
|
|
1696
|
+
placeholder: "สูงสุด",
|
|
1697
|
+
value: formData.eta_days_max,
|
|
1698
|
+
onChange: (e) => setFormData({ ...formData, eta_days_max: e.target.value }),
|
|
1699
|
+
required: true
|
|
1700
|
+
}
|
|
1701
|
+
)
|
|
1702
|
+
] }) })
|
|
1603
1703
|
] }),
|
|
1604
1704
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
1605
1705
|
/* @__PURE__ */ jsx(
|
|
@@ -1613,202 +1713,168 @@ const MaterialCostModal = ({
|
|
|
1613
1713
|
/* @__PURE__ */ jsx(Label, { htmlFor: "active", children: "เปิดใช้งาน" })
|
|
1614
1714
|
] }),
|
|
1615
1715
|
/* @__PURE__ */ jsxs(Drawer.Footer, { children: [
|
|
1616
|
-
/* @__PURE__ */ jsx(
|
|
1617
|
-
Button,
|
|
1618
|
-
{
|
|
1619
|
-
type: "button",
|
|
1620
|
-
variant: "secondary",
|
|
1621
|
-
onClick: onClose,
|
|
1622
|
-
disabled: isSaving,
|
|
1623
|
-
children: "ยกเลิก"
|
|
1624
|
-
}
|
|
1625
|
-
),
|
|
1716
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", onClick: onClose, disabled: isSaving, children: "ยกเลิก" }),
|
|
1626
1717
|
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
|
|
1627
1718
|
] })
|
|
1628
1719
|
] }) })
|
|
1629
1720
|
] }) });
|
|
1630
1721
|
};
|
|
1631
|
-
const
|
|
1722
|
+
const CARRIER_TYPES = [
|
|
1723
|
+
{ value: "ALL", label: "ทั้งหมด" },
|
|
1724
|
+
{ value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
|
|
1725
|
+
{ value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
|
|
1726
|
+
{ value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
|
|
1727
|
+
];
|
|
1728
|
+
const SERVICE_CODES = [
|
|
1632
1729
|
{ value: "ALL", label: "ทั้งหมด" },
|
|
1633
|
-
{ value: "
|
|
1634
|
-
{ value: "
|
|
1635
|
-
{ value: "
|
|
1636
|
-
{ value: "
|
|
1637
|
-
{ value: "LABEL", label: "ฉลาก/สติกเกอร์" },
|
|
1638
|
-
{ value: "OTHER", label: "อื่นๆ" }
|
|
1730
|
+
{ value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม." },
|
|
1731
|
+
{ value: "SAME_DAY", label: "ส่งด่วนภายในวัน" },
|
|
1732
|
+
{ value: "STANDARD_3_5D", label: "EMS 3-5 วัน" },
|
|
1733
|
+
{ value: "EXPRESS_1_2D", label: "Express 1-2 วัน" }
|
|
1639
1734
|
];
|
|
1640
|
-
const
|
|
1641
|
-
const [
|
|
1642
|
-
const [
|
|
1643
|
-
const [
|
|
1644
|
-
const [
|
|
1735
|
+
const ShippingRatesTable = () => {
|
|
1736
|
+
const [rates, setRates] = useState([]);
|
|
1737
|
+
const [filteredRates, setFilteredRates] = useState([]);
|
|
1738
|
+
const [carrierFilter, setCarrierFilter] = useState("ALL");
|
|
1739
|
+
const [serviceFilter, setServiceFilter] = useState("ALL");
|
|
1645
1740
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
1646
|
-
const [
|
|
1741
|
+
const [editingRate, setEditingRate] = useState(null);
|
|
1647
1742
|
const [isLoading, setIsLoading] = useState(false);
|
|
1648
|
-
const [
|
|
1649
|
-
const [deleteMaterialCostName, setDeleteMaterialCostName] = useState("");
|
|
1743
|
+
const [deleteRateId, setDeleteRateId] = useState(null);
|
|
1650
1744
|
useEffect(() => {
|
|
1651
|
-
|
|
1745
|
+
fetchRates();
|
|
1652
1746
|
}, []);
|
|
1653
1747
|
useEffect(() => {
|
|
1654
|
-
let filtered =
|
|
1655
|
-
if (
|
|
1656
|
-
filtered = filtered.filter((
|
|
1748
|
+
let filtered = rates;
|
|
1749
|
+
if (carrierFilter !== "ALL") {
|
|
1750
|
+
filtered = filtered.filter((rate) => rate.carrier_type === carrierFilter);
|
|
1657
1751
|
}
|
|
1658
|
-
if (
|
|
1659
|
-
filtered = filtered.filter(
|
|
1660
|
-
(mc) => mc.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
1661
|
-
);
|
|
1752
|
+
if (serviceFilter !== "ALL") {
|
|
1753
|
+
filtered = filtered.filter((rate) => rate.service_code === serviceFilter);
|
|
1662
1754
|
}
|
|
1663
|
-
|
|
1664
|
-
}, [
|
|
1665
|
-
const
|
|
1755
|
+
setFilteredRates(filtered);
|
|
1756
|
+
}, [carrierFilter, serviceFilter, rates]);
|
|
1757
|
+
const fetchRates = async () => {
|
|
1666
1758
|
setIsLoading(true);
|
|
1667
1759
|
try {
|
|
1668
|
-
const response = await fetch("/admin/
|
|
1760
|
+
const response = await fetch("/admin/shipping-rates", {
|
|
1669
1761
|
credentials: "include"
|
|
1670
1762
|
});
|
|
1671
1763
|
const data = await response.json();
|
|
1672
|
-
|
|
1764
|
+
setRates(data.rates || []);
|
|
1673
1765
|
} catch (error) {
|
|
1674
1766
|
toast.error("ข้อผิดพลาด", {
|
|
1675
|
-
description: "
|
|
1767
|
+
description: "ไม่สามารถโหลดข้อมูลอัตราค่าขนส่งได้"
|
|
1676
1768
|
});
|
|
1677
1769
|
} finally {
|
|
1678
1770
|
setIsLoading(false);
|
|
1679
1771
|
}
|
|
1680
1772
|
};
|
|
1681
|
-
const handleToggleActive = async (materialCost) => {
|
|
1682
|
-
try {
|
|
1683
|
-
const response = await fetch(`/admin/material-costs/${materialCost.id}`, {
|
|
1684
|
-
method: "PUT",
|
|
1685
|
-
headers: { "Content-Type": "application/json" },
|
|
1686
|
-
credentials: "include",
|
|
1687
|
-
body: JSON.stringify({ ...materialCost, active: !materialCost.active })
|
|
1688
|
-
});
|
|
1689
|
-
if (response.ok) {
|
|
1690
|
-
toast.success("สำเร็จ", {
|
|
1691
|
-
description: `${materialCost.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}วัสดุ ${materialCost.name} แล้ว`
|
|
1692
|
-
});
|
|
1693
|
-
fetchMaterialCosts();
|
|
1694
|
-
} else {
|
|
1695
|
-
throw new Error("Failed to update");
|
|
1696
|
-
}
|
|
1697
|
-
} catch (error) {
|
|
1698
|
-
toast.error("ข้อผิดพลาด", {
|
|
1699
|
-
description: "ไม่สามารถอัพเดทสถานะได้"
|
|
1700
|
-
});
|
|
1701
|
-
}
|
|
1702
|
-
};
|
|
1703
1773
|
const handleDelete = async () => {
|
|
1704
|
-
if (!
|
|
1774
|
+
if (!deleteRateId) return;
|
|
1705
1775
|
try {
|
|
1706
|
-
const response = await fetch(
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
credentials: "include"
|
|
1711
|
-
}
|
|
1712
|
-
);
|
|
1776
|
+
const response = await fetch(`/admin/shipping-rates/${deleteRateId}`, {
|
|
1777
|
+
method: "DELETE",
|
|
1778
|
+
credentials: "include"
|
|
1779
|
+
});
|
|
1713
1780
|
if (response.ok) {
|
|
1714
1781
|
toast.success("สำเร็จ", {
|
|
1715
|
-
description:
|
|
1782
|
+
description: "ลบอัตราค่าขนส่งแล้ว"
|
|
1716
1783
|
});
|
|
1717
|
-
|
|
1784
|
+
fetchRates();
|
|
1718
1785
|
} else {
|
|
1719
1786
|
throw new Error("Failed to delete");
|
|
1720
1787
|
}
|
|
1721
1788
|
} catch (error) {
|
|
1722
1789
|
toast.error("ข้อผิดพลาด", {
|
|
1723
|
-
description: "
|
|
1790
|
+
description: "ไม่สามารถลบอัตราค่าขนส่งได้"
|
|
1724
1791
|
});
|
|
1725
1792
|
} finally {
|
|
1726
|
-
|
|
1727
|
-
setDeleteMaterialCostName("");
|
|
1793
|
+
setDeleteRateId(null);
|
|
1728
1794
|
}
|
|
1729
1795
|
};
|
|
1730
|
-
const handleEdit = (
|
|
1731
|
-
|
|
1796
|
+
const handleEdit = (rate) => {
|
|
1797
|
+
setEditingRate(rate);
|
|
1732
1798
|
setIsModalOpen(true);
|
|
1733
1799
|
};
|
|
1734
1800
|
const handleCreateNew = () => {
|
|
1735
|
-
|
|
1801
|
+
setEditingRate(null);
|
|
1736
1802
|
setIsModalOpen(true);
|
|
1737
1803
|
};
|
|
1738
1804
|
const handleModalClose = () => {
|
|
1739
1805
|
setIsModalOpen(false);
|
|
1740
|
-
|
|
1741
|
-
|
|
1806
|
+
setEditingRate(null);
|
|
1807
|
+
fetchRates();
|
|
1742
1808
|
};
|
|
1743
|
-
const
|
|
1744
|
-
|
|
1745
|
-
|
|
1809
|
+
const getCarrierLabel = (type) => {
|
|
1810
|
+
var _a;
|
|
1811
|
+
return ((_a = CARRIER_TYPES.find((c) => c.value === type)) == null ? void 0 : _a.label) || type;
|
|
1746
1812
|
};
|
|
1747
|
-
const
|
|
1813
|
+
const getServiceLabel = (code) => {
|
|
1748
1814
|
var _a;
|
|
1749
|
-
|
|
1750
|
-
|
|
1815
|
+
return ((_a = SERVICE_CODES.find((s) => s.value === code)) == null ? void 0 : _a.label) || code;
|
|
1816
|
+
};
|
|
1817
|
+
const formatETA = (rate) => {
|
|
1818
|
+
if (rate.eta_hours_min !== null && rate.eta_hours_min !== void 0 && rate.eta_hours_max !== null && rate.eta_hours_max !== void 0) {
|
|
1819
|
+
return `${rate.eta_hours_min}-${rate.eta_hours_max} ชม.`;
|
|
1820
|
+
}
|
|
1821
|
+
if (rate.eta_days_min !== null && rate.eta_days_min !== void 0 && rate.eta_days_max !== null && rate.eta_days_max !== void 0) {
|
|
1822
|
+
return `${rate.eta_days_min}-${rate.eta_days_max} วัน`;
|
|
1823
|
+
}
|
|
1824
|
+
return "-";
|
|
1751
1825
|
};
|
|
1752
1826
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1753
1827
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1754
1828
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
1755
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-x-4
|
|
1756
|
-
/* @__PURE__ */
|
|
1757
|
-
|
|
1758
|
-
{
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
}
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1829
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-x-4 items-end", children: [
|
|
1830
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
1831
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "ประเภทขนส่ง" }),
|
|
1832
|
+
/* @__PURE__ */ jsxs(Select, { value: carrierFilter, onValueChange: setCarrierFilter, children: [
|
|
1833
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "ทั้งหมด" }) }),
|
|
1834
|
+
/* @__PURE__ */ jsx(Select.Content, { children: CARRIER_TYPES.map((type) => /* @__PURE__ */ jsx(Select.Item, { value: type.value, children: type.label }, type.value)) })
|
|
1835
|
+
] })
|
|
1836
|
+
] }),
|
|
1837
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
1838
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "บริการจัดส่ง" }),
|
|
1839
|
+
/* @__PURE__ */ jsxs(Select, { value: serviceFilter, onValueChange: setServiceFilter, children: [
|
|
1840
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "ทั้งหมด" }) }),
|
|
1841
|
+
/* @__PURE__ */ jsx(Select.Content, { children: SERVICE_CODES.map((service) => /* @__PURE__ */ jsx(Select.Item, { value: service.value, children: service.label }, service.value)) })
|
|
1842
|
+
] })
|
|
1769
1843
|
] })
|
|
1770
1844
|
] }),
|
|
1771
1845
|
/* @__PURE__ */ jsxs(Button, { onClick: handleCreateNew, children: [
|
|
1772
1846
|
/* @__PURE__ */ jsx(Plus, {}),
|
|
1773
|
-
"
|
|
1847
|
+
"สร้างอัตราใหม่"
|
|
1774
1848
|
] })
|
|
1775
1849
|
] }),
|
|
1776
1850
|
/* @__PURE__ */ jsxs(Table, { children: [
|
|
1777
1851
|
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1778
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "
|
|
1779
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "
|
|
1780
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "
|
|
1781
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "
|
|
1782
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "
|
|
1852
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ประเภทขนส่ง" }),
|
|
1853
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "บริการ" }),
|
|
1854
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "น้ำหนัก ≤ (kg)" }),
|
|
1855
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
1856
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ETA" }),
|
|
1783
1857
|
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
1784
1858
|
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
1785
1859
|
] }) }),
|
|
1786
|
-
/* @__PURE__ */ jsx(Table.Body, { children: isLoading ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { className: "text-center", children: "กำลังโหลด..." }) }) :
|
|
1787
|
-
/* @__PURE__ */ jsx(Table.Cell, { children:
|
|
1788
|
-
/* @__PURE__ */ jsx(Table.Cell, { children:
|
|
1789
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: materialCost.unit }),
|
|
1860
|
+
/* @__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: [
|
|
1861
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: getCarrierLabel(rate.carrier_type) }),
|
|
1862
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: getServiceLabel(rate.service_code) }),
|
|
1790
1863
|
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1864
|
+
"≤ ",
|
|
1865
|
+
rate.max_weight_kg,
|
|
1866
|
+
" kg"
|
|
1794
1867
|
] }),
|
|
1795
|
-
/* @__PURE__ */ jsx(Table.Cell, {
|
|
1796
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
|
|
1797
|
-
|
|
1798
|
-
{
|
|
1799
|
-
color: materialCost.active ? "green" : "grey",
|
|
1800
|
-
className: "cursor-pointer",
|
|
1801
|
-
onClick: () => handleToggleActive(materialCost),
|
|
1802
|
-
children: materialCost.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
1803
|
-
}
|
|
1804
|
-
) }),
|
|
1868
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: rate.price.toFixed(2) }),
|
|
1869
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: "blue", children: formatETA(rate) }) }),
|
|
1870
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
1805
1871
|
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
1806
1872
|
/* @__PURE__ */ jsx(
|
|
1807
1873
|
Button,
|
|
1808
1874
|
{
|
|
1809
1875
|
variant: "transparent",
|
|
1810
1876
|
size: "small",
|
|
1811
|
-
onClick: () => handleEdit(
|
|
1877
|
+
onClick: () => handleEdit(rate),
|
|
1812
1878
|
children: /* @__PURE__ */ jsx(Edit, {})
|
|
1813
1879
|
}
|
|
1814
1880
|
),
|
|
@@ -1817,101 +1883,55 @@ const MaterialCostsTable = () => {
|
|
|
1817
1883
|
{
|
|
1818
1884
|
variant: "transparent",
|
|
1819
1885
|
size: "small",
|
|
1820
|
-
onClick: () =>
|
|
1886
|
+
onClick: () => setDeleteRateId(rate.id),
|
|
1821
1887
|
children: /* @__PURE__ */ jsx(Trash, {})
|
|
1822
1888
|
}
|
|
1823
1889
|
)
|
|
1824
1890
|
] }) })
|
|
1825
|
-
] },
|
|
1891
|
+
] }, rate.id)) })
|
|
1826
1892
|
] })
|
|
1827
1893
|
] }),
|
|
1828
|
-
isModalOpen && /* @__PURE__ */ jsx(
|
|
1829
|
-
MaterialCostModal,
|
|
1830
|
-
{
|
|
1831
|
-
materialCost: editingMaterialCost,
|
|
1832
|
-
onClose: handleModalClose
|
|
1833
|
-
}
|
|
1834
|
-
),
|
|
1894
|
+
isModalOpen && /* @__PURE__ */ jsx(ShippingRateModal, { rate: editingRate, onClose: handleModalClose }),
|
|
1835
1895
|
/* @__PURE__ */ jsx(
|
|
1836
1896
|
Prompt,
|
|
1837
1897
|
{
|
|
1838
1898
|
variant: "confirmation",
|
|
1839
|
-
open: !!
|
|
1840
|
-
onOpenChange: () =>
|
|
1841
|
-
setDeleteMaterialCostId(null);
|
|
1842
|
-
setDeleteMaterialCostName("");
|
|
1843
|
-
},
|
|
1899
|
+
open: !!deleteRateId,
|
|
1900
|
+
onOpenChange: () => setDeleteRateId(null),
|
|
1844
1901
|
children: /* @__PURE__ */ jsxs(Prompt.Content, { children: [
|
|
1845
|
-
/* @__PURE__ */ jsxs(Prompt.Header, { children: [
|
|
1846
|
-
/* @__PURE__ */ jsx(Prompt.Title, { children: "
|
|
1847
|
-
/* @__PURE__ */
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
] })
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
}
|
|
1862
|
-
const MaterialCostsPage = () => {
|
|
1863
|
-
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1864
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "ต้นทุนวัสดุ" }) }),
|
|
1865
|
-
/* @__PURE__ */ jsx(MaterialCostsTable, {})
|
|
1866
|
-
] }) });
|
|
1867
|
-
};
|
|
1868
|
-
const config$1 = defineRouteConfig({
|
|
1869
|
-
icon: CurrencyDollarSolid,
|
|
1870
|
-
label: "ต้นทุนวัสดุ"
|
|
1871
|
-
});
|
|
1872
|
-
const CARRIER_TYPES$1 = [
|
|
1873
|
-
{ value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
|
|
1874
|
-
{ value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
|
|
1875
|
-
{ value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
|
|
1876
|
-
];
|
|
1877
|
-
const SERVICE_CODES$1 = [
|
|
1878
|
-
{ value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม.", usesHours: true },
|
|
1879
|
-
{ value: "SAME_DAY", label: "ส่งด่วนภายในวัน", usesHours: true },
|
|
1880
|
-
{ value: "STANDARD_3_5D", label: "EMS 3-5 วัน", usesHours: false },
|
|
1881
|
-
{ value: "EXPRESS_1_2D", label: "Express 1-2 วัน", usesHours: false }
|
|
1882
|
-
];
|
|
1883
|
-
const ShippingRateModal = ({ rate, onClose }) => {
|
|
1902
|
+
/* @__PURE__ */ jsxs(Prompt.Header, { children: [
|
|
1903
|
+
/* @__PURE__ */ jsx(Prompt.Title, { children: "ลบอัตราค่าขนส่ง" }),
|
|
1904
|
+
/* @__PURE__ */ jsx(Prompt.Description, { children: "คุณต้องการลบอัตราค่าขนส่งนี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
|
|
1905
|
+
] }),
|
|
1906
|
+
/* @__PURE__ */ jsxs(Prompt.Footer, { children: [
|
|
1907
|
+
/* @__PURE__ */ jsx(Prompt.Cancel, { children: "ยกเลิก" }),
|
|
1908
|
+
/* @__PURE__ */ jsx(Prompt.Action, { onClick: handleDelete, children: "ลบ" })
|
|
1909
|
+
] })
|
|
1910
|
+
] })
|
|
1911
|
+
}
|
|
1912
|
+
)
|
|
1913
|
+
] });
|
|
1914
|
+
};
|
|
1915
|
+
const CompanyTruckBaseRateModal = ({
|
|
1916
|
+
rate,
|
|
1917
|
+
onClose
|
|
1918
|
+
}) => {
|
|
1884
1919
|
const [formData, setFormData] = useState({
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
max_weight_kg: "",
|
|
1920
|
+
min_distance_km: "0",
|
|
1921
|
+
max_distance_km: "",
|
|
1888
1922
|
price: "",
|
|
1889
|
-
currency: "THB",
|
|
1890
1923
|
priority: "0",
|
|
1891
|
-
eta_hours_min: "",
|
|
1892
|
-
eta_hours_max: "",
|
|
1893
|
-
eta_days_min: "",
|
|
1894
|
-
eta_days_max: "",
|
|
1895
1924
|
active: true
|
|
1896
1925
|
});
|
|
1897
1926
|
const [isSaving, setIsSaving] = useState(false);
|
|
1898
|
-
const selectedService = SERVICE_CODES$1.find((s) => s.value === formData.service_code);
|
|
1899
|
-
const usesHours = (selectedService == null ? void 0 : selectedService.usesHours) ?? false;
|
|
1900
|
-
const isMessenger = formData.carrier_type === "COMPANY_FLEET" && formData.service_code === "MESSENGER_3H";
|
|
1901
1927
|
useEffect(() => {
|
|
1902
|
-
var _a, _b
|
|
1928
|
+
var _a, _b;
|
|
1903
1929
|
if (rate) {
|
|
1904
1930
|
setFormData({
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
max_weight_kg: rate.max_weight_kg.toString(),
|
|
1931
|
+
min_distance_km: ((_a = rate.min_distance_km) == null ? void 0 : _a.toString()) ?? "0",
|
|
1932
|
+
max_distance_km: ((_b = rate.max_distance_km) == null ? void 0 : _b.toString()) || "",
|
|
1908
1933
|
price: rate.price.toString(),
|
|
1909
|
-
currency: rate.currency,
|
|
1910
1934
|
priority: (rate.priority ?? 0).toString(),
|
|
1911
|
-
eta_hours_min: ((_a = rate.eta_hours_min) == null ? void 0 : _a.toString()) || "",
|
|
1912
|
-
eta_hours_max: ((_b = rate.eta_hours_max) == null ? void 0 : _b.toString()) || "",
|
|
1913
|
-
eta_days_min: ((_c = rate.eta_days_min) == null ? void 0 : _c.toString()) || "",
|
|
1914
|
-
eta_days_max: ((_d = rate.eta_days_max) == null ? void 0 : _d.toString()) || "",
|
|
1915
1935
|
active: rate.active
|
|
1916
1936
|
});
|
|
1917
1937
|
}
|
|
@@ -1919,11 +1939,16 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
1919
1939
|
const handleSubmit = async (e) => {
|
|
1920
1940
|
e.preventDefault();
|
|
1921
1941
|
const errors = [];
|
|
1922
|
-
const
|
|
1942
|
+
const minDistance = parseFloat(formData.min_distance_km || "0");
|
|
1943
|
+
const hasMaxDistance = formData.max_distance_km !== "";
|
|
1944
|
+
const maxDistance = hasMaxDistance ? parseFloat(formData.max_distance_km) : void 0;
|
|
1923
1945
|
const price = parseFloat(formData.price);
|
|
1924
|
-
const priority = parseInt(formData.priority);
|
|
1925
|
-
if (isNaN(
|
|
1926
|
-
errors.push("
|
|
1946
|
+
const priority = parseInt(formData.priority || "0", 10);
|
|
1947
|
+
if (isNaN(minDistance) || minDistance < 0) {
|
|
1948
|
+
errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
|
|
1949
|
+
}
|
|
1950
|
+
if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
|
|
1951
|
+
errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
|
|
1927
1952
|
}
|
|
1928
1953
|
if (isNaN(price) || price < 0) {
|
|
1929
1954
|
errors.push("ราคาต้องเป็นตัวเลขไม่ติดลบ");
|
|
@@ -1931,27 +1956,6 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
1931
1956
|
if (isNaN(priority)) {
|
|
1932
1957
|
errors.push("ลำดับความสำคัญต้องเป็นตัวเลข");
|
|
1933
1958
|
}
|
|
1934
|
-
let etaHoursMin;
|
|
1935
|
-
let etaHoursMax;
|
|
1936
|
-
let etaDaysMin;
|
|
1937
|
-
let etaDaysMax;
|
|
1938
|
-
if (usesHours) {
|
|
1939
|
-
etaHoursMin = formData.eta_hours_min ? parseFloat(formData.eta_hours_min) : void 0;
|
|
1940
|
-
etaHoursMax = formData.eta_hours_max ? parseFloat(formData.eta_hours_max) : void 0;
|
|
1941
|
-
if (etaHoursMin === void 0 || etaHoursMax === void 0) {
|
|
1942
|
-
errors.push("กรุณากรอก ETA เป็นชั่วโมงให้ครบ");
|
|
1943
|
-
} else if (etaHoursMin > etaHoursMax) {
|
|
1944
|
-
errors.push("ETA ชั่วโมงต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
|
|
1945
|
-
}
|
|
1946
|
-
} else {
|
|
1947
|
-
etaDaysMin = formData.eta_days_min ? parseFloat(formData.eta_days_min) : void 0;
|
|
1948
|
-
etaDaysMax = formData.eta_days_max ? parseFloat(formData.eta_days_max) : void 0;
|
|
1949
|
-
if (etaDaysMin === void 0 || etaDaysMax === void 0) {
|
|
1950
|
-
errors.push("กรุณากรอก ETA เป็นวันให้ครบ");
|
|
1951
|
-
} else if (etaDaysMin > etaDaysMax) {
|
|
1952
|
-
errors.push("ETA วันต่ำสุดต้องน้อยกว่าหรือเท่ากับสูงสุด");
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
1959
|
if (errors.length > 0) {
|
|
1956
1960
|
toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
1957
1961
|
description: errors.join(", ")
|
|
@@ -1961,22 +1965,17 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
1961
1965
|
setIsSaving(true);
|
|
1962
1966
|
try {
|
|
1963
1967
|
const payload = {
|
|
1964
|
-
carrier_type:
|
|
1965
|
-
|
|
1966
|
-
|
|
1968
|
+
carrier_type: "COMPANY_TRUCK",
|
|
1969
|
+
carrier: "COMPANY_TRUCK",
|
|
1970
|
+
service_code: "SAME_DAY",
|
|
1971
|
+
min_distance_km: minDistance,
|
|
1972
|
+
max_distance_km: maxDistance === void 0 || isNaN(maxDistance) ? null : maxDistance,
|
|
1967
1973
|
price,
|
|
1968
|
-
currency:
|
|
1974
|
+
currency: "THB",
|
|
1969
1975
|
priority,
|
|
1970
1976
|
active: formData.active
|
|
1971
1977
|
};
|
|
1972
|
-
|
|
1973
|
-
payload.eta_hours_min = etaHoursMin;
|
|
1974
|
-
payload.eta_hours_max = etaHoursMax;
|
|
1975
|
-
} else {
|
|
1976
|
-
payload.eta_days_min = etaDaysMin;
|
|
1977
|
-
payload.eta_days_max = etaDaysMax;
|
|
1978
|
-
}
|
|
1979
|
-
const url = rate ? `/admin/shipping-rates/${rate.id}` : "/admin/shipping-rates";
|
|
1978
|
+
const url = rate ? `/admin/base-shipping-prices/${rate.id}` : "/admin/base-shipping-prices";
|
|
1980
1979
|
const method = rate ? "PUT" : "POST";
|
|
1981
1980
|
const response = await fetch(url, {
|
|
1982
1981
|
method,
|
|
@@ -1984,15 +1983,14 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
1984
1983
|
credentials: "include",
|
|
1985
1984
|
body: JSON.stringify(payload)
|
|
1986
1985
|
});
|
|
1987
|
-
if (response.ok) {
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
});
|
|
1991
|
-
onClose();
|
|
1992
|
-
} else {
|
|
1993
|
-
const error = await response.json();
|
|
1994
|
-
throw new Error(error.message || "Failed to save");
|
|
1986
|
+
if (!response.ok) {
|
|
1987
|
+
const error = await response.json().catch(() => ({}));
|
|
1988
|
+
throw new Error(error.message || "ไม่สามารถบันทึกข้อมูลได้");
|
|
1995
1989
|
}
|
|
1990
|
+
toast.success("สำเร็จ", {
|
|
1991
|
+
description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
|
|
1992
|
+
});
|
|
1993
|
+
onClose();
|
|
1996
1994
|
} catch (error) {
|
|
1997
1995
|
toast.error("ข้อผิดพลาด", {
|
|
1998
1996
|
description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
|
|
@@ -2002,85 +2000,56 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
2002
2000
|
}
|
|
2003
2001
|
};
|
|
2004
2002
|
return /* @__PURE__ */ jsx(Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(Drawer.Content, { children: [
|
|
2005
|
-
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: rate ? "
|
|
2003
|
+
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: rate ? "แก้ไขฐานราคาตามระยะทาง (รถบริษัท)" : "สร้างฐานราคาตามระยะทาง (รถบริษัท)" }) }),
|
|
2006
2004
|
/* @__PURE__ */ jsx(Drawer.Body, { children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
2007
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
2008
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "carrier", children: "ประเภทขนส่ง *" }),
|
|
2009
|
-
/* @__PURE__ */ jsxs(
|
|
2010
|
-
Select,
|
|
2011
|
-
{
|
|
2012
|
-
value: formData.carrier_type,
|
|
2013
|
-
onValueChange: (value) => setFormData({ ...formData, carrier_type: value }),
|
|
2014
|
-
children: [
|
|
2015
|
-
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกประเภทขนส่ง" }) }),
|
|
2016
|
-
/* @__PURE__ */ jsx(Select.Content, { children: CARRIER_TYPES$1.map((carrier) => /* @__PURE__ */ jsx(Select.Item, { value: carrier.value, children: carrier.label }, carrier.value)) })
|
|
2017
|
-
]
|
|
2018
|
-
}
|
|
2019
|
-
)
|
|
2020
|
-
] }),
|
|
2021
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
2022
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "service", children: "บริการจัดส่ง *" }),
|
|
2023
|
-
/* @__PURE__ */ jsxs(
|
|
2024
|
-
Select,
|
|
2025
|
-
{
|
|
2026
|
-
value: formData.service_code,
|
|
2027
|
-
onValueChange: (value) => setFormData({
|
|
2028
|
-
...formData,
|
|
2029
|
-
service_code: value,
|
|
2030
|
-
eta_hours_min: "",
|
|
2031
|
-
eta_hours_max: "",
|
|
2032
|
-
eta_days_min: "",
|
|
2033
|
-
eta_days_max: ""
|
|
2034
|
-
}),
|
|
2035
|
-
children: [
|
|
2036
|
-
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกบริการ" }) }),
|
|
2037
|
-
/* @__PURE__ */ jsx(Select.Content, { children: SERVICE_CODES$1.map((service) => /* @__PURE__ */ jsx(Select.Item, { value: service.value, children: service.label }, service.value)) })
|
|
2038
|
-
]
|
|
2039
|
-
}
|
|
2040
|
-
)
|
|
2041
|
-
] }),
|
|
2042
|
-
isMessenger && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-subtle p-3 text-sm space-y-1", children: [
|
|
2043
|
-
/* @__PURE__ */ jsx("div", { className: "font-semibold text-ui-fg-base", children: "ฐานราคาตามระยะทาง (Messenger)" }),
|
|
2044
|
-
/* @__PURE__ */ jsx("div", { className: "text-ui-fg-subtle", children: "ตั้งช่วงราคาตามระยะทาง เช่น 0-5, 5-20, 20-30, 30+ กม. สำหรับบริการส่งด่วน 3 ชม." }),
|
|
2045
|
-
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, { asChild: true, size: "small", variant: "secondary", children: /* @__PURE__ */ jsxs("a", { href: "/base-pricing", target: "_blank", rel: "noreferrer", children: [
|
|
2046
|
-
"จัดการฐานราคา",
|
|
2047
|
-
/* @__PURE__ */ jsx(ExternalLink, { className: "ml-1 h-4 w-4" })
|
|
2048
|
-
] }) }) })
|
|
2049
|
-
] }),
|
|
2050
2005
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
2051
2006
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2052
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
2007
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "min_distance_km", children: "ระยะทางเริ่มต้น (กม.) *" }),
|
|
2053
2008
|
/* @__PURE__ */ jsx(
|
|
2054
2009
|
Input,
|
|
2055
2010
|
{
|
|
2056
|
-
id: "
|
|
2011
|
+
id: "min_distance_km",
|
|
2057
2012
|
type: "number",
|
|
2058
|
-
step: "0.1",
|
|
2059
2013
|
min: "0",
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2014
|
+
step: "0.1",
|
|
2015
|
+
value: formData.min_distance_km,
|
|
2016
|
+
onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
|
|
2063
2017
|
required: true
|
|
2064
2018
|
}
|
|
2065
2019
|
)
|
|
2066
2020
|
] }),
|
|
2067
2021
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2068
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
2022
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "max_distance_km", children: "ระยะทางสูงสุด (กม.)" }),
|
|
2069
2023
|
/* @__PURE__ */ jsx(
|
|
2070
2024
|
Input,
|
|
2071
2025
|
{
|
|
2072
|
-
id: "
|
|
2026
|
+
id: "max_distance_km",
|
|
2073
2027
|
type: "number",
|
|
2074
|
-
step: "0.01",
|
|
2075
2028
|
min: "0",
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2029
|
+
step: "0.1",
|
|
2030
|
+
placeholder: "ปล่อยว่างหากไม่มีที่สิ้นสุด",
|
|
2031
|
+
value: formData.max_distance_km,
|
|
2032
|
+
onChange: (e) => setFormData({ ...formData, max_distance_km: e.target.value })
|
|
2080
2033
|
}
|
|
2081
|
-
)
|
|
2034
|
+
),
|
|
2035
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
|
|
2082
2036
|
] })
|
|
2083
2037
|
] }),
|
|
2038
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2039
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "price", children: "ราคา (THB) *" }),
|
|
2040
|
+
/* @__PURE__ */ jsx(
|
|
2041
|
+
Input,
|
|
2042
|
+
{
|
|
2043
|
+
id: "price",
|
|
2044
|
+
type: "number",
|
|
2045
|
+
min: "0",
|
|
2046
|
+
step: "0.01",
|
|
2047
|
+
value: formData.price,
|
|
2048
|
+
onChange: (e) => setFormData({ ...formData, price: e.target.value }),
|
|
2049
|
+
required: true
|
|
2050
|
+
}
|
|
2051
|
+
)
|
|
2052
|
+
] }),
|
|
2084
2053
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2085
2054
|
/* @__PURE__ */ jsx(Label, { htmlFor: "priority", children: "ลำดับความสำคัญ" }),
|
|
2086
2055
|
/* @__PURE__ */ jsx(
|
|
@@ -2093,56 +2062,7 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
2093
2062
|
placeholder: "0"
|
|
2094
2063
|
}
|
|
2095
2064
|
),
|
|
2096
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "
|
|
2097
|
-
] }),
|
|
2098
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
2099
|
-
/* @__PURE__ */ jsxs(Label, { children: [
|
|
2100
|
-
usesHours ? "ETA (ชั่วโมง)" : "ETA (วัน)",
|
|
2101
|
-
" *"
|
|
2102
|
-
] }),
|
|
2103
|
-
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-x-4 mt-2", children: usesHours ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2104
|
-
/* @__PURE__ */ jsx(
|
|
2105
|
-
Input,
|
|
2106
|
-
{
|
|
2107
|
-
type: "number",
|
|
2108
|
-
placeholder: "ต่ำสุด",
|
|
2109
|
-
value: formData.eta_hours_min,
|
|
2110
|
-
onChange: (e) => setFormData({ ...formData, eta_hours_min: e.target.value }),
|
|
2111
|
-
required: true
|
|
2112
|
-
}
|
|
2113
|
-
),
|
|
2114
|
-
/* @__PURE__ */ jsx(
|
|
2115
|
-
Input,
|
|
2116
|
-
{
|
|
2117
|
-
type: "number",
|
|
2118
|
-
placeholder: "สูงสุด",
|
|
2119
|
-
value: formData.eta_hours_max,
|
|
2120
|
-
onChange: (e) => setFormData({ ...formData, eta_hours_max: e.target.value }),
|
|
2121
|
-
required: true
|
|
2122
|
-
}
|
|
2123
|
-
)
|
|
2124
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2125
|
-
/* @__PURE__ */ jsx(
|
|
2126
|
-
Input,
|
|
2127
|
-
{
|
|
2128
|
-
type: "number",
|
|
2129
|
-
placeholder: "ต่ำสุด",
|
|
2130
|
-
value: formData.eta_days_min,
|
|
2131
|
-
onChange: (e) => setFormData({ ...formData, eta_days_min: e.target.value }),
|
|
2132
|
-
required: true
|
|
2133
|
-
}
|
|
2134
|
-
),
|
|
2135
|
-
/* @__PURE__ */ jsx(
|
|
2136
|
-
Input,
|
|
2137
|
-
{
|
|
2138
|
-
type: "number",
|
|
2139
|
-
placeholder: "สูงสุด",
|
|
2140
|
-
value: formData.eta_days_max,
|
|
2141
|
-
onChange: (e) => setFormData({ ...formData, eta_days_max: e.target.value }),
|
|
2142
|
-
required: true
|
|
2143
|
-
}
|
|
2144
|
-
)
|
|
2145
|
-
] }) })
|
|
2065
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
|
|
2146
2066
|
] }),
|
|
2147
2067
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
2148
2068
|
/* @__PURE__ */ jsx(
|
|
@@ -2156,195 +2076,163 @@ const ShippingRateModal = ({ rate, onClose }) => {
|
|
|
2156
2076
|
/* @__PURE__ */ jsx(Label, { htmlFor: "active", children: "เปิดใช้งาน" })
|
|
2157
2077
|
] }),
|
|
2158
2078
|
/* @__PURE__ */ jsxs(Drawer.Footer, { children: [
|
|
2159
|
-
/* @__PURE__ */ jsx(
|
|
2079
|
+
/* @__PURE__ */ jsx(
|
|
2080
|
+
Button,
|
|
2081
|
+
{
|
|
2082
|
+
type: "button",
|
|
2083
|
+
variant: "secondary",
|
|
2084
|
+
onClick: onClose,
|
|
2085
|
+
disabled: isSaving,
|
|
2086
|
+
children: "ยกเลิก"
|
|
2087
|
+
}
|
|
2088
|
+
),
|
|
2160
2089
|
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: isSaving, children: isSaving ? "กำลังบันทึก..." : "บันทึก" })
|
|
2161
2090
|
] })
|
|
2162
2091
|
] }) })
|
|
2163
2092
|
] }) });
|
|
2164
2093
|
};
|
|
2165
|
-
const
|
|
2166
|
-
{ value: "ALL", label: "ทั้งหมด" },
|
|
2167
|
-
{ value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
|
|
2168
|
-
{ value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
|
|
2169
|
-
{ value: "PRIVATE_CARRIER", label: "ขนส่งเอกชน (EMS/Express)" }
|
|
2170
|
-
];
|
|
2171
|
-
const SERVICE_CODES = [
|
|
2172
|
-
{ value: "ALL", label: "ทั้งหมด" },
|
|
2173
|
-
{ value: "MESSENGER_3H", label: "ส่งด่วน 3 ชม." },
|
|
2174
|
-
{ value: "SAME_DAY", label: "ส่งด่วนภายในวัน" },
|
|
2175
|
-
{ value: "STANDARD_3_5D", label: "EMS 3-5 วัน" },
|
|
2176
|
-
{ value: "EXPRESS_1_2D", label: "Express 1-2 วัน" }
|
|
2177
|
-
];
|
|
2178
|
-
const ShippingRatesTable = () => {
|
|
2094
|
+
const CompanyTruckBasePricingTable = () => {
|
|
2179
2095
|
const [rates, setRates] = useState([]);
|
|
2180
|
-
const [
|
|
2181
|
-
const [carrierFilter, setCarrierFilter] = useState("ALL");
|
|
2182
|
-
const [serviceFilter, setServiceFilter] = useState("ALL");
|
|
2096
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
2183
2097
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
2184
2098
|
const [editingRate, setEditingRate] = useState(null);
|
|
2185
|
-
const [
|
|
2186
|
-
const [deleteRateId, setDeleteRateId] = useState(null);
|
|
2099
|
+
const [deleteId, setDeleteId] = useState(null);
|
|
2187
2100
|
useEffect(() => {
|
|
2188
2101
|
fetchRates();
|
|
2189
2102
|
}, []);
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
}, [
|
|
2103
|
+
const orderedRates = useMemo(() => {
|
|
2104
|
+
return [...rates].sort((a, b) => {
|
|
2105
|
+
if (a.min_distance_km !== b.min_distance_km) {
|
|
2106
|
+
return a.min_distance_km - b.min_distance_km;
|
|
2107
|
+
}
|
|
2108
|
+
const aMax = a.max_distance_km ?? Number.POSITIVE_INFINITY;
|
|
2109
|
+
const bMax = b.max_distance_km ?? Number.POSITIVE_INFINITY;
|
|
2110
|
+
return aMax - bMax;
|
|
2111
|
+
});
|
|
2112
|
+
}, [rates]);
|
|
2200
2113
|
const fetchRates = async () => {
|
|
2201
2114
|
setIsLoading(true);
|
|
2202
2115
|
try {
|
|
2203
|
-
const response = await fetch(
|
|
2204
|
-
|
|
2205
|
-
|
|
2116
|
+
const response = await fetch(
|
|
2117
|
+
"/admin/base-shipping-prices?carrier_type=COMPANY_TRUCK&service_code=SAME_DAY",
|
|
2118
|
+
{
|
|
2119
|
+
credentials: "include"
|
|
2120
|
+
}
|
|
2121
|
+
);
|
|
2122
|
+
if (!response.ok) {
|
|
2123
|
+
throw new Error("failed_to_load_base_rates");
|
|
2124
|
+
}
|
|
2206
2125
|
const data = await response.json();
|
|
2207
|
-
setRates(data.
|
|
2126
|
+
setRates(data.base_rates || []);
|
|
2208
2127
|
} catch (error) {
|
|
2209
2128
|
toast.error("ข้อผิดพลาด", {
|
|
2210
|
-
description: "
|
|
2129
|
+
description: "ไม่สามารถโหลดฐานราคาตามระยะทางได้"
|
|
2211
2130
|
});
|
|
2212
2131
|
} finally {
|
|
2213
2132
|
setIsLoading(false);
|
|
2214
2133
|
}
|
|
2215
2134
|
};
|
|
2216
2135
|
const handleDelete = async () => {
|
|
2217
|
-
if (!
|
|
2136
|
+
if (!deleteId) return;
|
|
2218
2137
|
try {
|
|
2219
|
-
const response = await fetch(
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
} else {
|
|
2229
|
-
throw new Error("Failed to delete");
|
|
2138
|
+
const response = await fetch(
|
|
2139
|
+
`/admin/base-shipping-prices/${deleteId}`,
|
|
2140
|
+
{
|
|
2141
|
+
method: "DELETE",
|
|
2142
|
+
credentials: "include"
|
|
2143
|
+
}
|
|
2144
|
+
);
|
|
2145
|
+
if (!response.ok) {
|
|
2146
|
+
throw new Error("failed_to_delete");
|
|
2230
2147
|
}
|
|
2148
|
+
toast.success("สำเร็จ", {
|
|
2149
|
+
description: "ลบฐานราคาสำเร็จ"
|
|
2150
|
+
});
|
|
2151
|
+
fetchRates();
|
|
2231
2152
|
} catch (error) {
|
|
2232
2153
|
toast.error("ข้อผิดพลาด", {
|
|
2233
|
-
description: "
|
|
2154
|
+
description: "ไม่สามารถลบฐานราคาได้"
|
|
2234
2155
|
});
|
|
2235
2156
|
} finally {
|
|
2236
|
-
|
|
2157
|
+
setDeleteId(null);
|
|
2237
2158
|
}
|
|
2238
2159
|
};
|
|
2239
|
-
const handleEdit = (rate) => {
|
|
2240
|
-
setEditingRate(rate);
|
|
2241
|
-
setIsModalOpen(true);
|
|
2242
|
-
};
|
|
2243
|
-
const handleCreateNew = () => {
|
|
2244
|
-
setEditingRate(null);
|
|
2245
|
-
setIsModalOpen(true);
|
|
2246
|
-
};
|
|
2247
2160
|
const handleModalClose = () => {
|
|
2248
2161
|
setIsModalOpen(false);
|
|
2249
2162
|
setEditingRate(null);
|
|
2250
2163
|
fetchRates();
|
|
2251
2164
|
};
|
|
2252
|
-
const
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
var _a;
|
|
2258
|
-
return ((_a = SERVICE_CODES.find((s) => s.value === code)) == null ? void 0 : _a.label) || code;
|
|
2259
|
-
};
|
|
2260
|
-
const formatETA = (rate) => {
|
|
2261
|
-
if (rate.eta_hours_min !== null && rate.eta_hours_min !== void 0 && rate.eta_hours_max !== null && rate.eta_hours_max !== void 0) {
|
|
2262
|
-
return `${rate.eta_hours_min}-${rate.eta_hours_max} ชม.`;
|
|
2263
|
-
}
|
|
2264
|
-
if (rate.eta_days_min !== null && rate.eta_days_min !== void 0 && rate.eta_days_max !== null && rate.eta_days_max !== void 0) {
|
|
2265
|
-
return `${rate.eta_days_min}-${rate.eta_days_max} วัน`;
|
|
2165
|
+
const formatDistanceRange = (rate) => {
|
|
2166
|
+
const min = rate.min_distance_km ?? 0;
|
|
2167
|
+
const max = rate.max_distance_km;
|
|
2168
|
+
if (max === void 0 || max === null) {
|
|
2169
|
+
return `${min}+ กม.`;
|
|
2266
2170
|
}
|
|
2267
|
-
return
|
|
2171
|
+
return `${min}-${max} กม.`;
|
|
2268
2172
|
};
|
|
2269
2173
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2270
|
-
/* @__PURE__ */ jsxs("div", { className: "flex
|
|
2271
|
-
/* @__PURE__ */ jsxs("div", {
|
|
2272
|
-
/* @__PURE__ */
|
|
2273
|
-
|
|
2274
|
-
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "ประเภทขนส่ง" }),
|
|
2275
|
-
/* @__PURE__ */ jsxs(Select, { value: carrierFilter, onValueChange: setCarrierFilter, children: [
|
|
2276
|
-
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "ทั้งหมด" }) }),
|
|
2277
|
-
/* @__PURE__ */ jsx(Select.Content, { children: CARRIER_TYPES.map((type) => /* @__PURE__ */ jsx(Select.Item, { value: type.value, children: type.label }, type.value)) })
|
|
2278
|
-
] })
|
|
2279
|
-
] }),
|
|
2280
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
2281
|
-
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-ui-fg-subtle", children: "บริการจัดส่ง" }),
|
|
2282
|
-
/* @__PURE__ */ jsxs(Select, { value: serviceFilter, onValueChange: setServiceFilter, children: [
|
|
2283
|
-
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "ทั้งหมด" }) }),
|
|
2284
|
-
/* @__PURE__ */ jsx(Select.Content, { children: SERVICE_CODES.map((service) => /* @__PURE__ */ jsx(Select.Item, { value: service.value, children: service.label }, service.value)) })
|
|
2285
|
-
] })
|
|
2286
|
-
] })
|
|
2287
|
-
] }),
|
|
2288
|
-
/* @__PURE__ */ jsxs(Button, { onClick: handleCreateNew, children: [
|
|
2289
|
-
/* @__PURE__ */ jsx(Plus, {}),
|
|
2290
|
-
"สร้างอัตราใหม่"
|
|
2291
|
-
] })
|
|
2174
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
2175
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2176
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: "ฐานราคาตามระยะทาง (รถบริษัท - Same Day)" }),
|
|
2177
|
+
/* @__PURE__ */ jsx("p", { className: "text-ui-fg-subtle text-sm", children: "กำหนดราคาเริ่มต้นตามระยะทาง เช่น 0-10, 10-30, 30-50, 50+ กม." })
|
|
2292
2178
|
] }),
|
|
2293
|
-
/* @__PURE__ */ jsxs(
|
|
2294
|
-
/* @__PURE__ */ jsx(
|
|
2295
|
-
|
|
2296
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "บริการ" }),
|
|
2297
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "น้ำหนัก ≤ (kg)" }),
|
|
2298
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
2299
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ETA" }),
|
|
2300
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
2301
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
2302
|
-
] }) }),
|
|
2303
|
-
/* @__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: [
|
|
2304
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: getCarrierLabel(rate.carrier_type) }),
|
|
2305
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: getServiceLabel(rate.service_code) }),
|
|
2306
|
-
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
2307
|
-
"≤ ",
|
|
2308
|
-
rate.max_weight_kg,
|
|
2309
|
-
" kg"
|
|
2310
|
-
] }),
|
|
2311
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: rate.price.toFixed(2) }),
|
|
2312
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: "blue", children: formatETA(rate) }) }),
|
|
2313
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
2314
|
-
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
2315
|
-
/* @__PURE__ */ jsx(
|
|
2316
|
-
Button,
|
|
2317
|
-
{
|
|
2318
|
-
variant: "transparent",
|
|
2319
|
-
size: "small",
|
|
2320
|
-
onClick: () => handleEdit(rate),
|
|
2321
|
-
children: /* @__PURE__ */ jsx(Edit, {})
|
|
2322
|
-
}
|
|
2323
|
-
),
|
|
2324
|
-
/* @__PURE__ */ jsx(
|
|
2325
|
-
Button,
|
|
2326
|
-
{
|
|
2327
|
-
variant: "transparent",
|
|
2328
|
-
size: "small",
|
|
2329
|
-
onClick: () => setDeleteRateId(rate.id),
|
|
2330
|
-
children: /* @__PURE__ */ jsx(Trash, {})
|
|
2331
|
-
}
|
|
2332
|
-
)
|
|
2333
|
-
] }) })
|
|
2334
|
-
] }, rate.id)) })
|
|
2179
|
+
/* @__PURE__ */ jsxs(Button, { onClick: () => setIsModalOpen(true), children: [
|
|
2180
|
+
/* @__PURE__ */ jsx(Plus, {}),
|
|
2181
|
+
"เพิ่มช่วงราคา"
|
|
2335
2182
|
] })
|
|
2336
2183
|
] }),
|
|
2337
|
-
|
|
2184
|
+
/* @__PURE__ */ jsxs(Table, { className: "mt-4", children: [
|
|
2185
|
+
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
2186
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ช่วงระยะทาง" }),
|
|
2187
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
2188
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ลำดับ" }),
|
|
2189
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
2190
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
2191
|
+
] }) }),
|
|
2192
|
+
/* @__PURE__ */ jsx(Table.Body, { children: isLoading ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { colSpan: 5, className: "text-center", children: "กำลังโหลด..." }) }) : orderedRates.length === 0 ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { colSpan: 5, className: "text-center", children: "ยังไม่ได้ตั้งค่าฐานราคาตามระยะทาง (รถบริษัทจะใช้ราคาตามน้ำหนัก)" }) }) : orderedRates.map((rate) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
2193
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: formatDistanceRange(rate) }),
|
|
2194
|
+
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
2195
|
+
"฿",
|
|
2196
|
+
rate.price.toFixed(2)
|
|
2197
|
+
] }),
|
|
2198
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: rate.priority ?? 0 }),
|
|
2199
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
2200
|
+
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
2201
|
+
/* @__PURE__ */ jsx(
|
|
2202
|
+
Button,
|
|
2203
|
+
{
|
|
2204
|
+
variant: "transparent",
|
|
2205
|
+
size: "small",
|
|
2206
|
+
onClick: () => {
|
|
2207
|
+
setEditingRate(rate);
|
|
2208
|
+
setIsModalOpen(true);
|
|
2209
|
+
},
|
|
2210
|
+
children: /* @__PURE__ */ jsx(Edit, {})
|
|
2211
|
+
}
|
|
2212
|
+
),
|
|
2213
|
+
/* @__PURE__ */ jsx(
|
|
2214
|
+
Button,
|
|
2215
|
+
{
|
|
2216
|
+
variant: "transparent",
|
|
2217
|
+
size: "small",
|
|
2218
|
+
onClick: () => setDeleteId(rate.id),
|
|
2219
|
+
children: /* @__PURE__ */ jsx(Trash, {})
|
|
2220
|
+
}
|
|
2221
|
+
)
|
|
2222
|
+
] }) })
|
|
2223
|
+
] }, rate.id)) })
|
|
2224
|
+
] }),
|
|
2225
|
+
isModalOpen && /* @__PURE__ */ jsx(CompanyTruckBaseRateModal, { rate: editingRate, onClose: handleModalClose }),
|
|
2338
2226
|
/* @__PURE__ */ jsx(
|
|
2339
2227
|
Prompt,
|
|
2340
2228
|
{
|
|
2341
2229
|
variant: "confirmation",
|
|
2342
|
-
open: !!
|
|
2343
|
-
onOpenChange: () =>
|
|
2230
|
+
open: !!deleteId,
|
|
2231
|
+
onOpenChange: () => setDeleteId(null),
|
|
2344
2232
|
children: /* @__PURE__ */ jsxs(Prompt.Content, { children: [
|
|
2345
2233
|
/* @__PURE__ */ jsxs(Prompt.Header, { children: [
|
|
2346
|
-
/* @__PURE__ */ jsx(Prompt.Title, { children: "
|
|
2347
|
-
/* @__PURE__ */ jsx(Prompt.Description, { children: "
|
|
2234
|
+
/* @__PURE__ */ jsx(Prompt.Title, { children: "ลบฐานราคา" }),
|
|
2235
|
+
/* @__PURE__ */ jsx(Prompt.Description, { children: "คุณต้องการลบช่วงราคานี้ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้" })
|
|
2348
2236
|
] }),
|
|
2349
2237
|
/* @__PURE__ */ jsxs(Prompt.Footer, { children: [
|
|
2350
2238
|
/* @__PURE__ */ jsx(Prompt.Cancel, { children: "ยกเลิก" }),
|
|
@@ -2355,49 +2243,71 @@ const ShippingRatesTable = () => {
|
|
|
2355
2243
|
)
|
|
2356
2244
|
] });
|
|
2357
2245
|
};
|
|
2358
|
-
const
|
|
2359
|
-
|
|
2246
|
+
const RatesPage = () => {
|
|
2247
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-8", children: [
|
|
2248
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "อัตราค่าขนส่ง" }) }),
|
|
2249
|
+
/* @__PURE__ */ jsx(ShippingRatesTable, {}),
|
|
2250
|
+
/* @__PURE__ */ jsxs("div", { className: "border-t pt-4", children: [
|
|
2251
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", className: "mb-2 text-lg", children: "ฐานราคาตามระยะทาง (Messenger 3H)" }),
|
|
2252
|
+
/* @__PURE__ */ jsx(MessengerBasePricingTable, {})
|
|
2253
|
+
] }),
|
|
2254
|
+
/* @__PURE__ */ jsxs("div", { className: "border-t pt-4", children: [
|
|
2255
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", className: "mb-2 text-lg", children: "ฐานราคาตามระยะทาง (รถบริษัท - Same Day)" }),
|
|
2256
|
+
/* @__PURE__ */ jsx(CompanyTruckBasePricingTable, {})
|
|
2257
|
+
] })
|
|
2258
|
+
] }) });
|
|
2259
|
+
};
|
|
2260
|
+
const config$1 = defineRouteConfig({
|
|
2261
|
+
icon: HandTruck,
|
|
2262
|
+
label: "อัตราค่าขนส่ง"
|
|
2263
|
+
});
|
|
2264
|
+
const CATEGORIES$1 = [
|
|
2265
|
+
{ value: "PACKAGING", label: "วัสดุหีบห่อ" },
|
|
2266
|
+
{ value: "PROTECTION", label: "วัสดุป้องกัน" },
|
|
2267
|
+
{ value: "FILLING", label: "วัสดุเติมช่องว่าง" },
|
|
2268
|
+
{ value: "TAPE", label: "เทป/กาว" },
|
|
2269
|
+
{ value: "LABEL", label: "ฉลาก/สติกเกอร์" },
|
|
2270
|
+
{ value: "OTHER", label: "อื่นๆ" }
|
|
2271
|
+
];
|
|
2272
|
+
const MaterialCostModal = ({
|
|
2273
|
+
materialCost,
|
|
2360
2274
|
onClose
|
|
2361
2275
|
}) => {
|
|
2362
2276
|
const [formData, setFormData] = useState({
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2277
|
+
name: "",
|
|
2278
|
+
unit: "",
|
|
2279
|
+
cost_per_unit: "",
|
|
2280
|
+
currency: "THB",
|
|
2281
|
+
category: "",
|
|
2282
|
+
description: "",
|
|
2367
2283
|
active: true
|
|
2368
2284
|
});
|
|
2369
2285
|
const [isSaving, setIsSaving] = useState(false);
|
|
2370
2286
|
useEffect(() => {
|
|
2371
|
-
|
|
2372
|
-
if (rate) {
|
|
2287
|
+
if (materialCost) {
|
|
2373
2288
|
setFormData({
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2289
|
+
name: materialCost.name,
|
|
2290
|
+
unit: materialCost.unit,
|
|
2291
|
+
cost_per_unit: materialCost.cost_per_unit.toString(),
|
|
2292
|
+
currency: materialCost.currency,
|
|
2293
|
+
category: materialCost.category || "",
|
|
2294
|
+
description: materialCost.description || "",
|
|
2295
|
+
active: materialCost.active
|
|
2379
2296
|
});
|
|
2380
2297
|
}
|
|
2381
|
-
}, [
|
|
2298
|
+
}, [materialCost]);
|
|
2382
2299
|
const handleSubmit = async (e) => {
|
|
2383
2300
|
e.preventDefault();
|
|
2384
2301
|
const errors = [];
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
const maxDistance = hasMaxDistance ? parseFloat(formData.max_distance_km) : void 0;
|
|
2388
|
-
const price = parseFloat(formData.price);
|
|
2389
|
-
const priority = parseInt(formData.priority || "0", 10);
|
|
2390
|
-
if (isNaN(minDistance) || minDistance < 0) {
|
|
2391
|
-
errors.push("ระยะทางเริ่มต้นต้องไม่ติดลบ");
|
|
2392
|
-
}
|
|
2393
|
-
if (hasMaxDistance && (maxDistance === void 0 || isNaN(maxDistance) || (maxDistance ?? 0) < minDistance)) {
|
|
2394
|
-
errors.push("ระยะทางสูงสุดต้องมากกว่าหรือเท่ากับระยะทางเริ่มต้น");
|
|
2302
|
+
if (!formData.name.trim()) {
|
|
2303
|
+
errors.push("กรุณากรอกชื่อวัสดุ");
|
|
2395
2304
|
}
|
|
2396
|
-
if (
|
|
2397
|
-
errors.push("
|
|
2305
|
+
if (!formData.unit.trim()) {
|
|
2306
|
+
errors.push("กรุณากรอกหน่วย");
|
|
2398
2307
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2308
|
+
const costPerUnit = parseFloat(formData.cost_per_unit);
|
|
2309
|
+
if (isNaN(costPerUnit) || costPerUnit < 0) {
|
|
2310
|
+
errors.push("ต้นทุนต่อหน่วยต้องเป็นตัวเลขไม่ติดลบ");
|
|
2401
2311
|
}
|
|
2402
2312
|
if (errors.length > 0) {
|
|
2403
2313
|
toast.error("ข้อมูลไม่ถูกต้อง", {
|
|
@@ -2408,32 +2318,31 @@ const CompanyTruckBaseRateModal = ({
|
|
|
2408
2318
|
setIsSaving(true);
|
|
2409
2319
|
try {
|
|
2410
2320
|
const payload = {
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
currency: "THB",
|
|
2418
|
-
priority,
|
|
2321
|
+
name: formData.name.trim(),
|
|
2322
|
+
unit: formData.unit.trim(),
|
|
2323
|
+
cost_per_unit: costPerUnit,
|
|
2324
|
+
currency: formData.currency,
|
|
2325
|
+
category: formData.category || void 0,
|
|
2326
|
+
description: formData.description.trim() || void 0,
|
|
2419
2327
|
active: formData.active
|
|
2420
2328
|
};
|
|
2421
|
-
const url =
|
|
2422
|
-
const method =
|
|
2329
|
+
const url = materialCost ? `/admin/material-costs/${materialCost.id}` : "/admin/material-costs";
|
|
2330
|
+
const method = materialCost ? "PUT" : "POST";
|
|
2423
2331
|
const response = await fetch(url, {
|
|
2424
2332
|
method,
|
|
2425
2333
|
headers: { "Content-Type": "application/json" },
|
|
2426
2334
|
credentials: "include",
|
|
2427
2335
|
body: JSON.stringify(payload)
|
|
2428
2336
|
});
|
|
2429
|
-
if (
|
|
2430
|
-
|
|
2431
|
-
|
|
2337
|
+
if (response.ok) {
|
|
2338
|
+
toast.success("สำเร็จ", {
|
|
2339
|
+
description: materialCost ? "แก้ไขรายการต้นทุนวัสดุแล้ว" : "สร้างรายการต้นทุนวัสดุใหม่แล้ว"
|
|
2340
|
+
});
|
|
2341
|
+
onClose();
|
|
2342
|
+
} else {
|
|
2343
|
+
const error = await response.json();
|
|
2344
|
+
throw new Error(error.message || "Failed to save");
|
|
2432
2345
|
}
|
|
2433
|
-
toast.success("สำเร็จ", {
|
|
2434
|
-
description: rate ? "แก้ไขฐานราคาตามระยะทางแล้ว" : "สร้างฐานราคาตามระยะทางใหม่แล้ว"
|
|
2435
|
-
});
|
|
2436
|
-
onClose();
|
|
2437
2346
|
} catch (error) {
|
|
2438
2347
|
toast.error("ข้อผิดพลาด", {
|
|
2439
2348
|
description: error instanceof Error ? error.message : "ไม่สามารถบันทึกข้อมูลได้"
|
|
@@ -2443,69 +2352,89 @@ const CompanyTruckBaseRateModal = ({
|
|
|
2443
2352
|
}
|
|
2444
2353
|
};
|
|
2445
2354
|
return /* @__PURE__ */ jsx(Drawer, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(Drawer.Content, { children: [
|
|
2446
|
-
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children:
|
|
2355
|
+
/* @__PURE__ */ jsx(Drawer.Header, { children: /* @__PURE__ */ jsx(Drawer.Title, { children: materialCost ? "แก้ไขรายการต้นทุนวัสดุ" : "สร้างรายการต้นทุนวัสดุใหม่" }) }),
|
|
2447
2356
|
/* @__PURE__ */ jsx(Drawer.Body, { children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col gap-y-4", children: [
|
|
2357
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2358
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "name", children: "ชื่อวัสดุ *" }),
|
|
2359
|
+
/* @__PURE__ */ jsx(
|
|
2360
|
+
Input,
|
|
2361
|
+
{
|
|
2362
|
+
id: "name",
|
|
2363
|
+
value: formData.name,
|
|
2364
|
+
onChange: (e) => setFormData({ ...formData, name: e.target.value }),
|
|
2365
|
+
placeholder: "เช่น พลาสติกกันกระแทก, กล่องกระดาษ",
|
|
2366
|
+
required: true
|
|
2367
|
+
}
|
|
2368
|
+
)
|
|
2369
|
+
] }),
|
|
2370
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2371
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "category", children: "หมวดหมู่ (ไม่บังคับ)" }),
|
|
2372
|
+
/* @__PURE__ */ jsxs(
|
|
2373
|
+
Select,
|
|
2374
|
+
{
|
|
2375
|
+
value: formData.category || void 0,
|
|
2376
|
+
onValueChange: (value) => setFormData({ ...formData, category: value }),
|
|
2377
|
+
children: [
|
|
2378
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "เลือกหมวดหมู่ (ไม่บังคับ)" }) }),
|
|
2379
|
+
/* @__PURE__ */ jsx(Select.Content, { children: CATEGORIES$1.map((category) => /* @__PURE__ */ jsx(Select.Item, { value: category.value, children: category.label }, category.value)) })
|
|
2380
|
+
]
|
|
2381
|
+
}
|
|
2382
|
+
)
|
|
2383
|
+
] }),
|
|
2448
2384
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [
|
|
2449
2385
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2450
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
2386
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "unit", children: "หน่วย *" }),
|
|
2451
2387
|
/* @__PURE__ */ jsx(
|
|
2452
2388
|
Input,
|
|
2453
2389
|
{
|
|
2454
|
-
id: "
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
value: formData.min_distance_km,
|
|
2459
|
-
onChange: (e) => setFormData({ ...formData, min_distance_km: e.target.value }),
|
|
2390
|
+
id: "unit",
|
|
2391
|
+
value: formData.unit,
|
|
2392
|
+
onChange: (e) => setFormData({ ...formData, unit: e.target.value }),
|
|
2393
|
+
placeholder: "เช่น ชิ้น, เมตร, กิโลกรัม",
|
|
2460
2394
|
required: true
|
|
2461
2395
|
}
|
|
2462
2396
|
)
|
|
2463
2397
|
] }),
|
|
2464
2398
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2465
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
2399
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "costPerUnit", children: "ต้นทุนต่อหน่วย *" }),
|
|
2466
2400
|
/* @__PURE__ */ jsx(
|
|
2467
2401
|
Input,
|
|
2468
2402
|
{
|
|
2469
|
-
id: "
|
|
2403
|
+
id: "costPerUnit",
|
|
2470
2404
|
type: "number",
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2405
|
+
step: "0.01",
|
|
2406
|
+
value: formData.cost_per_unit,
|
|
2407
|
+
onChange: (e) => setFormData({ ...formData, cost_per_unit: e.target.value }),
|
|
2408
|
+
placeholder: "0.00",
|
|
2409
|
+
required: true
|
|
2476
2410
|
}
|
|
2477
|
-
)
|
|
2478
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "เว้นว่างเพื่อกำหนดช่วงราคาแบบ 30+ กม." })
|
|
2411
|
+
)
|
|
2479
2412
|
] })
|
|
2480
2413
|
] }),
|
|
2481
2414
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2482
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
2415
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "currency", children: "สกุลเงิน" }),
|
|
2483
2416
|
/* @__PURE__ */ jsx(
|
|
2484
2417
|
Input,
|
|
2485
2418
|
{
|
|
2486
|
-
id: "
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
value: formData.price,
|
|
2491
|
-
onChange: (e) => setFormData({ ...formData, price: e.target.value }),
|
|
2492
|
-
required: true
|
|
2419
|
+
id: "currency",
|
|
2420
|
+
value: formData.currency,
|
|
2421
|
+
onChange: (e) => setFormData({ ...formData, currency: e.target.value }),
|
|
2422
|
+
placeholder: "THB"
|
|
2493
2423
|
}
|
|
2494
2424
|
)
|
|
2495
2425
|
] }),
|
|
2496
2426
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2497
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "
|
|
2427
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "description", children: "คำอธิบาย" }),
|
|
2498
2428
|
/* @__PURE__ */ jsx(
|
|
2499
|
-
|
|
2429
|
+
Textarea,
|
|
2500
2430
|
{
|
|
2501
|
-
id: "
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2431
|
+
id: "description",
|
|
2432
|
+
value: formData.description,
|
|
2433
|
+
onChange: (e) => setFormData({ ...formData, description: e.target.value }),
|
|
2434
|
+
placeholder: "คำอธิบายเพิ่มเติมเกี่ยวกับวัสดุนี้...",
|
|
2435
|
+
rows: 3
|
|
2506
2436
|
}
|
|
2507
|
-
)
|
|
2508
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-ui-fg-subtle mt-1", children: "ใช้สำหรับจัดลำดับเมื่อมีช่วงราคาทับกัน (ค่าเริ่มต้น 0)" })
|
|
2437
|
+
)
|
|
2509
2438
|
] }),
|
|
2510
2439
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
2511
2440
|
/* @__PURE__ */ jsx(
|
|
@@ -2534,148 +2463,227 @@ const CompanyTruckBaseRateModal = ({
|
|
|
2534
2463
|
] }) })
|
|
2535
2464
|
] }) });
|
|
2536
2465
|
};
|
|
2537
|
-
const
|
|
2538
|
-
|
|
2539
|
-
|
|
2466
|
+
const CATEGORIES = [
|
|
2467
|
+
{ value: "ALL", label: "ทั้งหมด" },
|
|
2468
|
+
{ value: "PACKAGING", label: "วัสดุหีบห่อ" },
|
|
2469
|
+
{ value: "PROTECTION", label: "วัสดุป้องกัน" },
|
|
2470
|
+
{ value: "FILLING", label: "วัสดุเติมช่องว่าง" },
|
|
2471
|
+
{ value: "TAPE", label: "เทป/กาว" },
|
|
2472
|
+
{ value: "LABEL", label: "ฉลาก/สติกเกอร์" },
|
|
2473
|
+
{ value: "OTHER", label: "อื่นๆ" }
|
|
2474
|
+
];
|
|
2475
|
+
const MaterialCostsTable = () => {
|
|
2476
|
+
const [materialCosts, setMaterialCosts] = useState([]);
|
|
2477
|
+
const [filteredMaterialCosts, setFilteredMaterialCosts] = useState([]);
|
|
2478
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
2479
|
+
const [categoryFilter, setCategoryFilter] = useState("ALL");
|
|
2540
2480
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
2541
|
-
const [
|
|
2542
|
-
const [
|
|
2481
|
+
const [editingMaterialCost, setEditingMaterialCost] = useState(null);
|
|
2482
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
2483
|
+
const [deleteMaterialCostId, setDeleteMaterialCostId] = useState(null);
|
|
2484
|
+
const [deleteMaterialCostName, setDeleteMaterialCostName] = useState("");
|
|
2543
2485
|
useEffect(() => {
|
|
2544
|
-
|
|
2486
|
+
fetchMaterialCosts();
|
|
2545
2487
|
}, []);
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2488
|
+
useEffect(() => {
|
|
2489
|
+
let filtered = materialCosts;
|
|
2490
|
+
if (categoryFilter !== "ALL") {
|
|
2491
|
+
filtered = filtered.filter((mc) => mc.category === categoryFilter);
|
|
2492
|
+
}
|
|
2493
|
+
if (searchTerm) {
|
|
2494
|
+
filtered = filtered.filter(
|
|
2495
|
+
(mc) => mc.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
2496
|
+
);
|
|
2497
|
+
}
|
|
2498
|
+
setFilteredMaterialCosts(filtered);
|
|
2499
|
+
}, [searchTerm, categoryFilter, materialCosts]);
|
|
2500
|
+
const fetchMaterialCosts = async () => {
|
|
2557
2501
|
setIsLoading(true);
|
|
2558
2502
|
try {
|
|
2559
|
-
const response = await fetch(
|
|
2560
|
-
"
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2503
|
+
const response = await fetch("/admin/material-costs", {
|
|
2504
|
+
credentials: "include"
|
|
2505
|
+
});
|
|
2506
|
+
const data = await response.json();
|
|
2507
|
+
setMaterialCosts(data.material_costs || []);
|
|
2508
|
+
} catch (error) {
|
|
2509
|
+
toast.error("ข้อผิดพลาด", {
|
|
2510
|
+
description: "ไม่สามารถโหลดข้อมูลต้นทุนวัสดุได้"
|
|
2511
|
+
});
|
|
2512
|
+
} finally {
|
|
2513
|
+
setIsLoading(false);
|
|
2514
|
+
}
|
|
2515
|
+
};
|
|
2516
|
+
const handleToggleActive = async (materialCost) => {
|
|
2517
|
+
try {
|
|
2518
|
+
const response = await fetch(`/admin/material-costs/${materialCost.id}`, {
|
|
2519
|
+
method: "PUT",
|
|
2520
|
+
headers: { "Content-Type": "application/json" },
|
|
2521
|
+
credentials: "include",
|
|
2522
|
+
body: JSON.stringify({ ...materialCost, active: !materialCost.active })
|
|
2523
|
+
});
|
|
2524
|
+
if (response.ok) {
|
|
2525
|
+
toast.success("สำเร็จ", {
|
|
2526
|
+
description: `${materialCost.active ? "ปิดใช้งาน" : "เปิดใช้งาน"}วัสดุ ${materialCost.name} แล้ว`
|
|
2527
|
+
});
|
|
2528
|
+
fetchMaterialCosts();
|
|
2529
|
+
} else {
|
|
2530
|
+
throw new Error("Failed to update");
|
|
2567
2531
|
}
|
|
2568
|
-
const data = await response.json();
|
|
2569
|
-
setRates(data.base_rates || []);
|
|
2570
2532
|
} catch (error) {
|
|
2571
2533
|
toast.error("ข้อผิดพลาด", {
|
|
2572
|
-
description: "
|
|
2534
|
+
description: "ไม่สามารถอัพเดทสถานะได้"
|
|
2573
2535
|
});
|
|
2574
|
-
} finally {
|
|
2575
|
-
setIsLoading(false);
|
|
2576
2536
|
}
|
|
2577
2537
|
};
|
|
2578
2538
|
const handleDelete = async () => {
|
|
2579
|
-
if (!
|
|
2539
|
+
if (!deleteMaterialCostId) return;
|
|
2580
2540
|
try {
|
|
2581
2541
|
const response = await fetch(
|
|
2582
|
-
`/admin/
|
|
2542
|
+
`/admin/material-costs/${deleteMaterialCostId}`,
|
|
2583
2543
|
{
|
|
2584
2544
|
method: "DELETE",
|
|
2585
2545
|
credentials: "include"
|
|
2586
2546
|
}
|
|
2587
2547
|
);
|
|
2588
|
-
if (
|
|
2589
|
-
|
|
2548
|
+
if (response.ok) {
|
|
2549
|
+
toast.success("สำเร็จ", {
|
|
2550
|
+
description: `ลบวัสดุ ${deleteMaterialCostName} แล้ว`
|
|
2551
|
+
});
|
|
2552
|
+
fetchMaterialCosts();
|
|
2553
|
+
} else {
|
|
2554
|
+
throw new Error("Failed to delete");
|
|
2590
2555
|
}
|
|
2591
|
-
toast.success("สำเร็จ", {
|
|
2592
|
-
description: "ลบฐานราคาสำเร็จ"
|
|
2593
|
-
});
|
|
2594
|
-
fetchRates();
|
|
2595
2556
|
} catch (error) {
|
|
2596
2557
|
toast.error("ข้อผิดพลาด", {
|
|
2597
|
-
description: "
|
|
2558
|
+
description: "ไม่สามารถลบวัสดุได้"
|
|
2598
2559
|
});
|
|
2599
2560
|
} finally {
|
|
2600
|
-
|
|
2561
|
+
setDeleteMaterialCostId(null);
|
|
2562
|
+
setDeleteMaterialCostName("");
|
|
2601
2563
|
}
|
|
2602
2564
|
};
|
|
2565
|
+
const handleEdit = (materialCost) => {
|
|
2566
|
+
setEditingMaterialCost(materialCost);
|
|
2567
|
+
setIsModalOpen(true);
|
|
2568
|
+
};
|
|
2569
|
+
const handleCreateNew = () => {
|
|
2570
|
+
setEditingMaterialCost(null);
|
|
2571
|
+
setIsModalOpen(true);
|
|
2572
|
+
};
|
|
2603
2573
|
const handleModalClose = () => {
|
|
2604
2574
|
setIsModalOpen(false);
|
|
2605
|
-
|
|
2606
|
-
|
|
2575
|
+
setEditingMaterialCost(null);
|
|
2576
|
+
fetchMaterialCosts();
|
|
2607
2577
|
};
|
|
2608
|
-
const
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
return
|
|
2578
|
+
const openDeletePrompt = (id, name) => {
|
|
2579
|
+
setDeleteMaterialCostId(id);
|
|
2580
|
+
setDeleteMaterialCostName(name);
|
|
2581
|
+
};
|
|
2582
|
+
const getCategoryLabel = (category) => {
|
|
2583
|
+
var _a;
|
|
2584
|
+
if (!category) return "-";
|
|
2585
|
+
return ((_a = CATEGORIES.find((c) => c.value === category)) == null ? void 0 : _a.label) || category;
|
|
2615
2586
|
};
|
|
2616
2587
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2617
|
-
/* @__PURE__ */ jsxs("div", { className: "flex
|
|
2618
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
2619
|
-
/* @__PURE__ */
|
|
2620
|
-
/* @__PURE__ */ jsx("p", { className: "text-ui-fg-subtle text-sm", children: "กำหนดราคาเริ่มต้นตามระยะทาง เช่น 0-10, 10-30, 30-50, 50+ กม." })
|
|
2621
|
-
] }),
|
|
2622
|
-
/* @__PURE__ */ jsxs(Button, { onClick: () => setIsModalOpen(true), children: [
|
|
2623
|
-
/* @__PURE__ */ jsx(Plus, {}),
|
|
2624
|
-
"เพิ่มช่วงราคา"
|
|
2625
|
-
] })
|
|
2626
|
-
] }),
|
|
2627
|
-
/* @__PURE__ */ jsxs(Table, { className: "mt-4", children: [
|
|
2628
|
-
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
2629
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ช่วงระยะทาง" }),
|
|
2630
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ราคา (THB)" }),
|
|
2631
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ลำดับ" }),
|
|
2632
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
2633
|
-
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
2634
|
-
] }) }),
|
|
2635
|
-
/* @__PURE__ */ jsx(Table.Body, { children: isLoading ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { colSpan: 5, className: "text-center", children: "กำลังโหลด..." }) }) : orderedRates.length === 0 ? /* @__PURE__ */ jsx(Table.Row, { children: /* @__PURE__ */ jsx(Table.Cell, { colSpan: 5, className: "text-center", children: "ยังไม่ได้ตั้งค่าฐานราคาตามระยะทาง (รถบริษัทจะใช้ราคาตามน้ำหนัก)" }) }) : orderedRates.map((rate) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
2636
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: formatDistanceRange(rate) }),
|
|
2637
|
-
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
2638
|
-
"฿",
|
|
2639
|
-
rate.price.toFixed(2)
|
|
2640
|
-
] }),
|
|
2641
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: rate.priority ?? 0 }),
|
|
2642
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: rate.active ? "green" : "grey", children: rate.active ? "เปิดใช้งาน" : "ปิดใช้งาน" }) }),
|
|
2643
|
-
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
2588
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
2589
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
2590
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-x-4 flex-1", children: [
|
|
2644
2591
|
/* @__PURE__ */ jsx(
|
|
2645
|
-
|
|
2592
|
+
Input,
|
|
2646
2593
|
{
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
},
|
|
2653
|
-
children: /* @__PURE__ */ jsx(Edit, {})
|
|
2594
|
+
type: "search",
|
|
2595
|
+
placeholder: "ค้นหาชื่อวัสดุ...",
|
|
2596
|
+
value: searchTerm,
|
|
2597
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
2598
|
+
className: "max-w-md"
|
|
2654
2599
|
}
|
|
2655
2600
|
),
|
|
2656
|
-
/* @__PURE__ */
|
|
2657
|
-
|
|
2601
|
+
/* @__PURE__ */ jsxs(Select, { value: categoryFilter, onValueChange: setCategoryFilter, children: [
|
|
2602
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "หมวดหมู่" }) }),
|
|
2603
|
+
/* @__PURE__ */ jsx(Select.Content, { children: CATEGORIES.map((category) => /* @__PURE__ */ jsx(Select.Item, { value: category.value, children: category.label }, category.value)) })
|
|
2604
|
+
] })
|
|
2605
|
+
] }),
|
|
2606
|
+
/* @__PURE__ */ jsxs(Button, { onClick: handleCreateNew, children: [
|
|
2607
|
+
/* @__PURE__ */ jsx(Plus, {}),
|
|
2608
|
+
"สร้างรายการใหม่"
|
|
2609
|
+
] })
|
|
2610
|
+
] }),
|
|
2611
|
+
/* @__PURE__ */ jsxs(Table, { children: [
|
|
2612
|
+
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
2613
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ชื่อ" }),
|
|
2614
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "หมวดหมู่" }),
|
|
2615
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "หน่วย" }),
|
|
2616
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "ต้นทุนต่อหน่วย" }),
|
|
2617
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "คำอธิบาย" }),
|
|
2618
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "สถานะ" }),
|
|
2619
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "การจัดการ" })
|
|
2620
|
+
] }) }),
|
|
2621
|
+
/* @__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: [
|
|
2622
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: materialCost.name }),
|
|
2623
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Badge, { color: "blue", children: getCategoryLabel(materialCost.category) }) }),
|
|
2624
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: materialCost.unit }),
|
|
2625
|
+
/* @__PURE__ */ jsxs(Table.Cell, { children: [
|
|
2626
|
+
materialCost.cost_per_unit.toFixed(2),
|
|
2627
|
+
" ",
|
|
2628
|
+
materialCost.currency
|
|
2629
|
+
] }),
|
|
2630
|
+
/* @__PURE__ */ jsx(Table.Cell, { className: "max-w-xs truncate", children: materialCost.description || "-" }),
|
|
2631
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
|
|
2632
|
+
Badge,
|
|
2658
2633
|
{
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
onClick: () =>
|
|
2662
|
-
children:
|
|
2634
|
+
color: materialCost.active ? "green" : "grey",
|
|
2635
|
+
className: "cursor-pointer",
|
|
2636
|
+
onClick: () => handleToggleActive(materialCost),
|
|
2637
|
+
children: materialCost.active ? "เปิดใช้งาน" : "ปิดใช้งาน"
|
|
2663
2638
|
}
|
|
2664
|
-
)
|
|
2665
|
-
|
|
2666
|
-
|
|
2639
|
+
) }),
|
|
2640
|
+
/* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-x-2", children: [
|
|
2641
|
+
/* @__PURE__ */ jsx(
|
|
2642
|
+
Button,
|
|
2643
|
+
{
|
|
2644
|
+
variant: "transparent",
|
|
2645
|
+
size: "small",
|
|
2646
|
+
onClick: () => handleEdit(materialCost),
|
|
2647
|
+
children: /* @__PURE__ */ jsx(Edit, {})
|
|
2648
|
+
}
|
|
2649
|
+
),
|
|
2650
|
+
/* @__PURE__ */ jsx(
|
|
2651
|
+
Button,
|
|
2652
|
+
{
|
|
2653
|
+
variant: "transparent",
|
|
2654
|
+
size: "small",
|
|
2655
|
+
onClick: () => openDeletePrompt(materialCost.id, materialCost.name),
|
|
2656
|
+
children: /* @__PURE__ */ jsx(Trash, {})
|
|
2657
|
+
}
|
|
2658
|
+
)
|
|
2659
|
+
] }) })
|
|
2660
|
+
] }, materialCost.id)) })
|
|
2661
|
+
] })
|
|
2667
2662
|
] }),
|
|
2668
|
-
isModalOpen && /* @__PURE__ */ jsx(
|
|
2663
|
+
isModalOpen && /* @__PURE__ */ jsx(
|
|
2664
|
+
MaterialCostModal,
|
|
2665
|
+
{
|
|
2666
|
+
materialCost: editingMaterialCost,
|
|
2667
|
+
onClose: handleModalClose
|
|
2668
|
+
}
|
|
2669
|
+
),
|
|
2669
2670
|
/* @__PURE__ */ jsx(
|
|
2670
2671
|
Prompt,
|
|
2671
2672
|
{
|
|
2672
2673
|
variant: "confirmation",
|
|
2673
|
-
open: !!
|
|
2674
|
-
onOpenChange: () =>
|
|
2674
|
+
open: !!deleteMaterialCostId,
|
|
2675
|
+
onOpenChange: () => {
|
|
2676
|
+
setDeleteMaterialCostId(null);
|
|
2677
|
+
setDeleteMaterialCostName("");
|
|
2678
|
+
},
|
|
2675
2679
|
children: /* @__PURE__ */ jsxs(Prompt.Content, { children: [
|
|
2676
2680
|
/* @__PURE__ */ jsxs(Prompt.Header, { children: [
|
|
2677
|
-
/* @__PURE__ */ jsx(Prompt.Title, { children: "
|
|
2678
|
-
/* @__PURE__ */
|
|
2681
|
+
/* @__PURE__ */ jsx(Prompt.Title, { children: "ลบรายการต้นทุนวัสดุ" }),
|
|
2682
|
+
/* @__PURE__ */ jsxs(Prompt.Description, { children: [
|
|
2683
|
+
'คุณต้องการลบวัสดุ "',
|
|
2684
|
+
deleteMaterialCostName,
|
|
2685
|
+
'" ใช่หรือไม่? การดำเนินการนี้ไม่สามารถย้อนกลับได้'
|
|
2686
|
+
] })
|
|
2679
2687
|
] }),
|
|
2680
2688
|
/* @__PURE__ */ jsxs(Prompt.Footer, { children: [
|
|
2681
2689
|
/* @__PURE__ */ jsx(Prompt.Cancel, { children: "ยกเลิก" }),
|
|
@@ -2686,23 +2694,15 @@ const CompanyTruckBasePricingTable = () => {
|
|
|
2686
2694
|
)
|
|
2687
2695
|
] });
|
|
2688
2696
|
};
|
|
2689
|
-
const
|
|
2690
|
-
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-
|
|
2691
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "
|
|
2692
|
-
/* @__PURE__ */ jsx(
|
|
2693
|
-
/* @__PURE__ */ jsxs("div", { className: "border-t pt-4", children: [
|
|
2694
|
-
/* @__PURE__ */ jsx(Heading, { level: "h2", className: "mb-2 text-lg", children: "ฐานราคาตามระยะทาง (Messenger 3H)" }),
|
|
2695
|
-
/* @__PURE__ */ jsx(MessengerBasePricingTable, {})
|
|
2696
|
-
] }),
|
|
2697
|
-
/* @__PURE__ */ jsxs("div", { className: "border-t pt-4", children: [
|
|
2698
|
-
/* @__PURE__ */ jsx(Heading, { level: "h2", className: "mb-2 text-lg", children: "ฐานราคาตามระยะทาง (รถบริษัท - Same Day)" }),
|
|
2699
|
-
/* @__PURE__ */ jsx(CompanyTruckBasePricingTable, {})
|
|
2700
|
-
] })
|
|
2697
|
+
const MaterialCostsPage = () => {
|
|
2698
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
2699
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "ต้นทุนวัสดุ" }) }),
|
|
2700
|
+
/* @__PURE__ */ jsx(MaterialCostsTable, {})
|
|
2701
2701
|
] }) });
|
|
2702
2702
|
};
|
|
2703
2703
|
const config = defineRouteConfig({
|
|
2704
|
-
icon:
|
|
2705
|
-
label: "
|
|
2704
|
+
icon: CurrencyDollarSolid,
|
|
2705
|
+
label: "ต้นทุนวัสดุ"
|
|
2706
2706
|
});
|
|
2707
2707
|
const widgetModule = { widgets: [
|
|
2708
2708
|
{
|
|
@@ -2712,6 +2712,10 @@ const widgetModule = { widgets: [
|
|
|
2712
2712
|
] };
|
|
2713
2713
|
const routeModule = {
|
|
2714
2714
|
routes: [
|
|
2715
|
+
{
|
|
2716
|
+
Component: AreasPage,
|
|
2717
|
+
path: "/areas"
|
|
2718
|
+
},
|
|
2715
2719
|
{
|
|
2716
2720
|
Component: BasePricingPage,
|
|
2717
2721
|
path: "/base-pricing"
|
|
@@ -2721,56 +2725,52 @@ const routeModule = {
|
|
|
2721
2725
|
path: "/boxes"
|
|
2722
2726
|
},
|
|
2723
2727
|
{
|
|
2724
|
-
Component:
|
|
2725
|
-
path: "/
|
|
2728
|
+
Component: RatesPage,
|
|
2729
|
+
path: "/rates"
|
|
2726
2730
|
},
|
|
2727
2731
|
{
|
|
2728
2732
|
Component: MaterialCostsPage,
|
|
2729
2733
|
path: "/material-costs"
|
|
2730
|
-
},
|
|
2731
|
-
{
|
|
2732
|
-
Component: RatesPage,
|
|
2733
|
-
path: "/rates"
|
|
2734
2734
|
}
|
|
2735
2735
|
]
|
|
2736
2736
|
};
|
|
2737
2737
|
const menuItemModule = {
|
|
2738
2738
|
menuItems: [
|
|
2739
2739
|
{
|
|
2740
|
-
label: config$
|
|
2741
|
-
icon: config$
|
|
2740
|
+
label: config$4.label,
|
|
2741
|
+
icon: config$4.icon,
|
|
2742
2742
|
path: "/areas",
|
|
2743
2743
|
nested: void 0,
|
|
2744
2744
|
rank: void 0,
|
|
2745
2745
|
translationNs: void 0
|
|
2746
2746
|
},
|
|
2747
2747
|
{
|
|
2748
|
-
label: config$
|
|
2749
|
-
icon: config$
|
|
2748
|
+
label: config$3.label,
|
|
2749
|
+
icon: config$3.icon,
|
|
2750
2750
|
path: "/base-pricing",
|
|
2751
2751
|
nested: void 0,
|
|
2752
2752
|
rank: void 0,
|
|
2753
2753
|
translationNs: void 0
|
|
2754
2754
|
},
|
|
2755
2755
|
{
|
|
2756
|
-
label: config$
|
|
2757
|
-
icon: config$
|
|
2756
|
+
label: config$2.label,
|
|
2757
|
+
icon: config$2.icon,
|
|
2758
2758
|
path: "/boxes",
|
|
2759
2759
|
nested: void 0,
|
|
2760
2760
|
rank: void 0,
|
|
2761
2761
|
translationNs: void 0
|
|
2762
2762
|
},
|
|
2763
2763
|
{
|
|
2764
|
-
label: config
|
|
2765
|
-
icon: config
|
|
2764
|
+
label: config.label,
|
|
2765
|
+
icon: config.icon,
|
|
2766
2766
|
path: "/material-costs",
|
|
2767
2767
|
nested: void 0,
|
|
2768
2768
|
rank: void 0,
|
|
2769
2769
|
translationNs: void 0
|
|
2770
2770
|
},
|
|
2771
2771
|
{
|
|
2772
|
-
label: config.label,
|
|
2773
|
-
icon: config.icon,
|
|
2772
|
+
label: config$1.label,
|
|
2773
|
+
icon: config$1.icon,
|
|
2774
2774
|
path: "/rates",
|
|
2775
2775
|
nested: void 0,
|
|
2776
2776
|
rank: void 0,
|