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