@lodashventure/medusa-parcel-shipping 0.4.3 → 0.4.5
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.
|
@@ -1,11 +1,232 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const jsxRuntime = require("react/jsx-runtime");
|
|
3
|
-
const adminSdk = require("@medusajs/admin-sdk");
|
|
4
|
-
const ui = require("@medusajs/ui");
|
|
5
3
|
const react = require("react");
|
|
6
|
-
const
|
|
4
|
+
const ui = require("@medusajs/ui");
|
|
5
|
+
const adminSdk = require("@medusajs/admin-sdk");
|
|
7
6
|
const icons = require("@medusajs/icons");
|
|
7
|
+
const lucideReact = require("lucide-react");
|
|
8
8
|
require("@medusajs/admin-shared");
|
|
9
|
+
const OrderPackingSlipWidget = ({ data }) => {
|
|
10
|
+
const [packingSlip, setPackingSlip] = react.useState(null);
|
|
11
|
+
const [loading, setLoading] = react.useState(true);
|
|
12
|
+
const [generating, setGenerating] = react.useState(false);
|
|
13
|
+
const [error, setError] = react.useState(null);
|
|
14
|
+
const order = data;
|
|
15
|
+
react.useEffect(() => {
|
|
16
|
+
fetchPackingSlip();
|
|
17
|
+
}, [order.id]);
|
|
18
|
+
const fetchPackingSlip = async () => {
|
|
19
|
+
setLoading(true);
|
|
20
|
+
setError(null);
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch(
|
|
23
|
+
`/admin/documents/packing-slip?orderId=${order.id}`,
|
|
24
|
+
{
|
|
25
|
+
credentials: "include"
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
throw new Error("Failed to fetch packing slip");
|
|
30
|
+
}
|
|
31
|
+
const data2 = await response.json();
|
|
32
|
+
setPackingSlip(data2.packingSlip || null);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.error("Error fetching packing slip:", err);
|
|
35
|
+
setError(err.message);
|
|
36
|
+
} finally {
|
|
37
|
+
setLoading(false);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const generatePackingSlip = async () => {
|
|
41
|
+
setGenerating(true);
|
|
42
|
+
setError(null);
|
|
43
|
+
try {
|
|
44
|
+
const response = await fetch("/admin/documents/packing-slip", {
|
|
45
|
+
method: "POST",
|
|
46
|
+
headers: {
|
|
47
|
+
"Content-Type": "application/json"
|
|
48
|
+
},
|
|
49
|
+
credentials: "include",
|
|
50
|
+
body: JSON.stringify({
|
|
51
|
+
order_id: order.id
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
const errorData = await response.json().catch(() => ({
|
|
56
|
+
message: `HTTP ${response.status}: ${response.statusText}`
|
|
57
|
+
}));
|
|
58
|
+
throw new Error(errorData.message || "Failed to generate packing slip");
|
|
59
|
+
}
|
|
60
|
+
const data2 = await response.json();
|
|
61
|
+
setPackingSlip(data2.packingSlip);
|
|
62
|
+
ui.toast.success("สร้างใบแพ็คสินค้าสำเร็จ", {
|
|
63
|
+
description: `หมายเลขเอกสาร: ${data2.packingSlip.number}`
|
|
64
|
+
});
|
|
65
|
+
} catch (err) {
|
|
66
|
+
setError(err.message);
|
|
67
|
+
ui.toast.error("เกิดข้อผิดพลาด", {
|
|
68
|
+
description: err.message
|
|
69
|
+
});
|
|
70
|
+
} finally {
|
|
71
|
+
setGenerating(false);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const downloadPackingSlip = async () => {
|
|
75
|
+
var _a;
|
|
76
|
+
try {
|
|
77
|
+
const response = await fetch(
|
|
78
|
+
`/admin/documents/packing-slip?orderId=${order.id}&includeBuffer=true`,
|
|
79
|
+
{
|
|
80
|
+
credentials: "include"
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
throw new Error("Failed to fetch packing slip");
|
|
85
|
+
}
|
|
86
|
+
const data2 = await response.json();
|
|
87
|
+
if ((_a = data2.packingSlip) == null ? void 0 : _a.pdfBase64) {
|
|
88
|
+
const byteCharacters = atob(data2.packingSlip.pdfBase64);
|
|
89
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
90
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
91
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
92
|
+
}
|
|
93
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
94
|
+
const blob = new Blob([byteArray], { type: "application/pdf" });
|
|
95
|
+
const url = window.URL.createObjectURL(blob);
|
|
96
|
+
const link = document.createElement("a");
|
|
97
|
+
link.href = url;
|
|
98
|
+
link.download = `packing-slip-${data2.packingSlip.number}.pdf`;
|
|
99
|
+
document.body.appendChild(link);
|
|
100
|
+
link.click();
|
|
101
|
+
document.body.removeChild(link);
|
|
102
|
+
window.URL.revokeObjectURL(url);
|
|
103
|
+
ui.toast.success("ดาวน์โหลดสำเร็จ");
|
|
104
|
+
} else {
|
|
105
|
+
throw new Error("PDF data not available");
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
ui.toast.error("เกิดข้อผิดพลาดในการดาวน์โหลด", {
|
|
109
|
+
description: err.message
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const formatDateTime = (dateString) => {
|
|
114
|
+
const date = new Date(dateString);
|
|
115
|
+
return new Intl.DateTimeFormat("th-TH", {
|
|
116
|
+
year: "numeric",
|
|
117
|
+
month: "short",
|
|
118
|
+
day: "numeric",
|
|
119
|
+
hour: "2-digit",
|
|
120
|
+
minute: "2-digit"
|
|
121
|
+
}).format(date);
|
|
122
|
+
};
|
|
123
|
+
if (loading) {
|
|
124
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
125
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
126
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.DocumentText, { className: "text-ui-fg-subtle" }),
|
|
127
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "ใบแพ็คสินค้า" })
|
|
128
|
+
] }) }),
|
|
129
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "กำลังโหลด..." }) })
|
|
130
|
+
] });
|
|
131
|
+
}
|
|
132
|
+
if (error && !packingSlip) {
|
|
133
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
134
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
135
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.DocumentText, { className: "text-ui-fg-subtle" }),
|
|
136
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "ใบแพ็คสินค้า" })
|
|
137
|
+
] }) }),
|
|
138
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-red-500", children: error }) })
|
|
139
|
+
] });
|
|
140
|
+
}
|
|
141
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
142
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {}),
|
|
143
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
144
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
145
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
146
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.DocumentText, { className: "text-ui-fg-subtle" }),
|
|
147
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "ใบแพ็คสินค้า" }),
|
|
148
|
+
packingSlip && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "green", size: "small", children: "สร้างแล้ว" })
|
|
149
|
+
] }),
|
|
150
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
151
|
+
packingSlip && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
152
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
153
|
+
ui.Button,
|
|
154
|
+
{
|
|
155
|
+
size: "small",
|
|
156
|
+
variant: "transparent",
|
|
157
|
+
onClick: fetchPackingSlip,
|
|
158
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: 16 })
|
|
159
|
+
}
|
|
160
|
+
),
|
|
161
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
162
|
+
ui.Button,
|
|
163
|
+
{
|
|
164
|
+
size: "small",
|
|
165
|
+
variant: "secondary",
|
|
166
|
+
onClick: downloadPackingSlip,
|
|
167
|
+
children: [
|
|
168
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { size: 16 }),
|
|
169
|
+
"ดาวน์โหลด"
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
)
|
|
173
|
+
] }),
|
|
174
|
+
!packingSlip && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
175
|
+
ui.Button,
|
|
176
|
+
{
|
|
177
|
+
size: "small",
|
|
178
|
+
onClick: generatePackingSlip,
|
|
179
|
+
isLoading: generating,
|
|
180
|
+
children: [
|
|
181
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: 16 }),
|
|
182
|
+
"สร้างใบแพ็คสินค้า"
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
)
|
|
186
|
+
] })
|
|
187
|
+
] }),
|
|
188
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: packingSlip ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
189
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
190
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
191
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: "หมายเลขเอกสาร" }),
|
|
192
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "plus", className: "font-mono", children: packingSlip.number })
|
|
193
|
+
] }),
|
|
194
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
195
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: "สร้างเมื่อ" }),
|
|
196
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: formatDateTime(packingSlip.created_at) })
|
|
197
|
+
] })
|
|
198
|
+
] }),
|
|
199
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-4 border-t", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", className: "text-ui-fg-muted", children: [
|
|
200
|
+
"Document ID: ",
|
|
201
|
+
packingSlip.id
|
|
202
|
+
] }) }),
|
|
203
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pt-4 border-t", children: [
|
|
204
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
205
|
+
ui.Button,
|
|
206
|
+
{
|
|
207
|
+
size: "small",
|
|
208
|
+
variant: "secondary",
|
|
209
|
+
onClick: generatePackingSlip,
|
|
210
|
+
isLoading: generating,
|
|
211
|
+
children: [
|
|
212
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: 16 }),
|
|
213
|
+
"สร้างใหม่"
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
),
|
|
217
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-muted mt-2", children: "การสร้างใหม่จะแทนที่ใบแพ็คสินค้าเดิม" })
|
|
218
|
+
] })
|
|
219
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
|
|
220
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.DocumentText, { className: "text-ui-fg-muted mb-4" }),
|
|
221
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle mb-2", children: "ยังไม่มีใบแพ็คสินค้าสำหรับคำสั่งซื้อนี้" }),
|
|
222
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-muted mb-4", children: "คลิกปุ่มด้านบนเพื่อสร้างใบแพ็คสินค้า" })
|
|
223
|
+
] }) })
|
|
224
|
+
] })
|
|
225
|
+
] });
|
|
226
|
+
};
|
|
227
|
+
adminSdk.defineWidgetConfig({
|
|
228
|
+
zone: "order.details.after"
|
|
229
|
+
});
|
|
9
230
|
const OrderShippingQuoteWidget = ({ data }) => {
|
|
10
231
|
const [quote, setQuote] = react.useState(null);
|
|
11
232
|
const [loading, setLoading] = react.useState(false);
|
|
@@ -44,16 +265,18 @@ const OrderShippingQuoteWidget = ({ data }) => {
|
|
|
44
265
|
},
|
|
45
266
|
allow_split_boxes: true
|
|
46
267
|
};
|
|
47
|
-
const response = await fetch("/
|
|
268
|
+
const response = await fetch("/admin/quote", {
|
|
48
269
|
method: "POST",
|
|
49
270
|
headers: {
|
|
50
|
-
"Content-Type": "application/json"
|
|
51
|
-
"x-publishable-api-key": "pk_99d0d47048eaf52697f42d149a7f58c1661652292b9e1ea1445f519cd16dc071"
|
|
271
|
+
"Content-Type": "application/json"
|
|
52
272
|
},
|
|
273
|
+
credentials: "include",
|
|
53
274
|
body: JSON.stringify(payload)
|
|
54
275
|
});
|
|
55
276
|
if (!response.ok) {
|
|
56
|
-
const errorData = await response.json()
|
|
277
|
+
const errorData = await response.json().catch(() => ({
|
|
278
|
+
message: `HTTP ${response.status}: ${response.statusText}`
|
|
279
|
+
}));
|
|
57
280
|
throw new Error(errorData.message || "Failed to fetch quote");
|
|
58
281
|
}
|
|
59
282
|
const data2 = await response.json();
|
|
@@ -651,7 +874,7 @@ const AreasPage = () => {
|
|
|
651
874
|
/* @__PURE__ */ jsxRuntime.jsx(ServiceAreasTable, {})
|
|
652
875
|
] }) });
|
|
653
876
|
};
|
|
654
|
-
const config$
|
|
877
|
+
const config$4 = adminSdk.defineRouteConfig({
|
|
655
878
|
icon: icons.MapPin,
|
|
656
879
|
label: "พื้นที่บริการ"
|
|
657
880
|
});
|
|
@@ -1059,7 +1282,7 @@ const BoxesPage = () => {
|
|
|
1059
1282
|
/* @__PURE__ */ jsxRuntime.jsx(ParcelBoxesTable, {})
|
|
1060
1283
|
] }) });
|
|
1061
1284
|
};
|
|
1062
|
-
const config$
|
|
1285
|
+
const config$3 = adminSdk.defineRouteConfig({
|
|
1063
1286
|
icon: icons.ArchiveBox,
|
|
1064
1287
|
label: "กล่องพัสดุ"
|
|
1065
1288
|
});
|
|
@@ -1502,10 +1725,225 @@ const MaterialCostsPage = () => {
|
|
|
1502
1725
|
/* @__PURE__ */ jsxRuntime.jsx(MaterialCostsTable, {})
|
|
1503
1726
|
] }) });
|
|
1504
1727
|
};
|
|
1505
|
-
const config$
|
|
1728
|
+
const config$2 = adminSdk.defineRouteConfig({
|
|
1506
1729
|
icon: icons.CurrencyDollarSolid,
|
|
1507
1730
|
label: "ต้นทุนวัสดุ"
|
|
1508
1731
|
});
|
|
1732
|
+
const PackingSlipSettingsPage = () => {
|
|
1733
|
+
const [settings, setSettings] = react.useState(null);
|
|
1734
|
+
const [loading, setLoading] = react.useState(true);
|
|
1735
|
+
const [saving, setSaving] = react.useState(false);
|
|
1736
|
+
const [formatNumber, setFormatNumber] = react.useState("");
|
|
1737
|
+
const [forcedNumber, setForcedNumber] = react.useState("");
|
|
1738
|
+
const [template, setTemplate] = react.useState("BASIC");
|
|
1739
|
+
react.useEffect(() => {
|
|
1740
|
+
fetchSettings();
|
|
1741
|
+
}, []);
|
|
1742
|
+
const fetchSettings = async () => {
|
|
1743
|
+
setLoading(true);
|
|
1744
|
+
try {
|
|
1745
|
+
const response = await fetch(
|
|
1746
|
+
"/admin/documents/document-packing-slip-settings",
|
|
1747
|
+
{
|
|
1748
|
+
credentials: "include"
|
|
1749
|
+
}
|
|
1750
|
+
);
|
|
1751
|
+
if (!response.ok) {
|
|
1752
|
+
throw new Error("Failed to fetch settings");
|
|
1753
|
+
}
|
|
1754
|
+
const data = await response.json();
|
|
1755
|
+
if (data.settings) {
|
|
1756
|
+
setSettings(data.settings);
|
|
1757
|
+
setFormatNumber(data.settings.formatNumber || "");
|
|
1758
|
+
setForcedNumber(data.settings.forcedNumber || "");
|
|
1759
|
+
setTemplate(data.settings.template || "BASIC");
|
|
1760
|
+
}
|
|
1761
|
+
} catch (err) {
|
|
1762
|
+
ui.toast.error("เกิดข้อผิดพลาด", {
|
|
1763
|
+
description: err.message
|
|
1764
|
+
});
|
|
1765
|
+
} finally {
|
|
1766
|
+
setLoading(false);
|
|
1767
|
+
}
|
|
1768
|
+
};
|
|
1769
|
+
const handleSave = async () => {
|
|
1770
|
+
setSaving(true);
|
|
1771
|
+
try {
|
|
1772
|
+
const response = await fetch(
|
|
1773
|
+
"/admin/documents/document-packing-slip-settings",
|
|
1774
|
+
{
|
|
1775
|
+
method: "POST",
|
|
1776
|
+
headers: {
|
|
1777
|
+
"Content-Type": "application/json"
|
|
1778
|
+
},
|
|
1779
|
+
credentials: "include",
|
|
1780
|
+
body: JSON.stringify({
|
|
1781
|
+
formatNumber: formatNumber || void 0,
|
|
1782
|
+
forcedNumber: forcedNumber || void 0,
|
|
1783
|
+
template
|
|
1784
|
+
})
|
|
1785
|
+
}
|
|
1786
|
+
);
|
|
1787
|
+
if (!response.ok) {
|
|
1788
|
+
const errorData = await response.json().catch(() => ({
|
|
1789
|
+
message: `HTTP ${response.status}: ${response.statusText}`
|
|
1790
|
+
}));
|
|
1791
|
+
throw new Error(errorData.message || "Failed to save settings");
|
|
1792
|
+
}
|
|
1793
|
+
const data = await response.json();
|
|
1794
|
+
setSettings(data.settings);
|
|
1795
|
+
ui.toast.success("บันทึกสำเร็จ", {
|
|
1796
|
+
description: "ตั้งค่าใบแพ็คสินค้าได้รับการบันทึกแล้ว"
|
|
1797
|
+
});
|
|
1798
|
+
} catch (err) {
|
|
1799
|
+
ui.toast.error("เกิดข้อผิดพลาดในการบันทึก", {
|
|
1800
|
+
description: err.message
|
|
1801
|
+
});
|
|
1802
|
+
} finally {
|
|
1803
|
+
setSaving(false);
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
const handlePreview = async () => {
|
|
1807
|
+
var _a;
|
|
1808
|
+
try {
|
|
1809
|
+
const response = await fetch(
|
|
1810
|
+
`/admin/documents/packing-slip/preview?template=${template}`,
|
|
1811
|
+
{
|
|
1812
|
+
credentials: "include"
|
|
1813
|
+
}
|
|
1814
|
+
);
|
|
1815
|
+
if (!response.ok) {
|
|
1816
|
+
const errorData = await response.json().catch(() => ({
|
|
1817
|
+
message: `HTTP ${response.status}: ${response.statusText}`
|
|
1818
|
+
}));
|
|
1819
|
+
throw new Error(errorData.message || "Failed to generate preview");
|
|
1820
|
+
}
|
|
1821
|
+
const data = await response.json();
|
|
1822
|
+
if ((_a = data.packingSlip) == null ? void 0 : _a.pdfBase64) {
|
|
1823
|
+
const byteCharacters = atob(data.packingSlip.pdfBase64);
|
|
1824
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
1825
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
1826
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
1827
|
+
}
|
|
1828
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
1829
|
+
const blob = new Blob([byteArray], { type: "application/pdf" });
|
|
1830
|
+
const url = window.URL.createObjectURL(blob);
|
|
1831
|
+
window.open(url, "_blank");
|
|
1832
|
+
ui.toast.success("เปิดตัวอย่างในแท็บใหม่");
|
|
1833
|
+
} else {
|
|
1834
|
+
throw new Error("PDF data not available");
|
|
1835
|
+
}
|
|
1836
|
+
} catch (err) {
|
|
1837
|
+
ui.toast.error("เกิดข้อผิดพลาดในการแสดงตัวอย่าง", {
|
|
1838
|
+
description: err.message
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1842
|
+
if (loading) {
|
|
1843
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1844
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "ตั้งค่าใบแพ็คสินค้า" }),
|
|
1845
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "กำลังโหลด..." })
|
|
1846
|
+
] }) });
|
|
1847
|
+
}
|
|
1848
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1849
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {}),
|
|
1850
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Container, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-6", children: [
|
|
1851
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1852
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "ตั้งค่าใบแพ็คสินค้า" }),
|
|
1853
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle mt-1", children: "กำหนดรูปแบบและเทมเพลตสำหรับใบแพ็คสินค้า" })
|
|
1854
|
+
] }) }),
|
|
1855
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-6", children: [
|
|
1856
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
1857
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "template", weight: "plus", children: "เทมเพลต" }),
|
|
1858
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: template, onValueChange: setTemplate, children: [
|
|
1859
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { id: "template", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, {}) }),
|
|
1860
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
|
|
1861
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "BASIC", children: "Basic - ใบแพ็คมาตรฐาน" }),
|
|
1862
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "BASIC_SMALL", children: "Basic Small - ใบแพ็คขนาดเล็ก" })
|
|
1863
|
+
] })
|
|
1864
|
+
] }),
|
|
1865
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "เลือกรูปแบบเทมเพลตสำหรับใบแพ็คสินค้า" }),
|
|
1866
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1867
|
+
ui.Button,
|
|
1868
|
+
{
|
|
1869
|
+
size: "small",
|
|
1870
|
+
variant: "secondary",
|
|
1871
|
+
onClick: handlePreview,
|
|
1872
|
+
className: "mt-2 w-fit",
|
|
1873
|
+
children: [
|
|
1874
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { size: 16 }),
|
|
1875
|
+
"ดูตัวอย่างเทมเพลต"
|
|
1876
|
+
]
|
|
1877
|
+
}
|
|
1878
|
+
)
|
|
1879
|
+
] }),
|
|
1880
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
1881
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "formatNumber", weight: "plus", children: "รูปแบบหมายเลขเอกสาร" }),
|
|
1882
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1883
|
+
ui.Input,
|
|
1884
|
+
{
|
|
1885
|
+
id: "formatNumber",
|
|
1886
|
+
placeholder: "เช่น PS-{YYYY}-{0000}",
|
|
1887
|
+
value: formatNumber,
|
|
1888
|
+
onChange: (e) => setFormatNumber(e.target.value)
|
|
1889
|
+
}
|
|
1890
|
+
),
|
|
1891
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
|
|
1892
|
+
"กำหนดรูปแบบหมายเลขใบแพ็คสินค้า ใช้ ",
|
|
1893
|
+
"{YYYY}",
|
|
1894
|
+
" สำหรับปี และ",
|
|
1895
|
+
" ",
|
|
1896
|
+
"{0000}",
|
|
1897
|
+
" สำหรับเลขที่เอกสาร"
|
|
1898
|
+
] })
|
|
1899
|
+
] }),
|
|
1900
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
1901
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "forcedNumber", weight: "plus", children: "กำหนดหมายเลขเริ่มต้น" }),
|
|
1902
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1903
|
+
ui.Input,
|
|
1904
|
+
{
|
|
1905
|
+
id: "forcedNumber",
|
|
1906
|
+
type: "number",
|
|
1907
|
+
placeholder: "เช่น 1",
|
|
1908
|
+
value: forcedNumber,
|
|
1909
|
+
onChange: (e) => setForcedNumber(e.target.value)
|
|
1910
|
+
}
|
|
1911
|
+
),
|
|
1912
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "กำหนดหมายเลขเริ่มต้นสำหรับเอกสารใหม่ (ถ้าไม่ระบุจะนับต่อจากเอกสารล่าสุด)" })
|
|
1913
|
+
] }),
|
|
1914
|
+
settings && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-4 bg-ui-bg-subtle", children: [
|
|
1915
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "plus", size: "small", className: "mb-2", children: "ข้อมูลการตั้งค่าปัจจุบัน" }),
|
|
1916
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2 text-sm text-ui-fg-subtle", children: [
|
|
1917
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: "อัปเดตล่าสุด:" }),
|
|
1918
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: new Date(settings.updated_at).toLocaleString("th-TH") }),
|
|
1919
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: "สร้างเมื่อ:" }),
|
|
1920
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: new Date(settings.created_at).toLocaleString("th-TH") })
|
|
1921
|
+
] })
|
|
1922
|
+
] }),
|
|
1923
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border p-4", children: [
|
|
1924
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "plus", size: "small", className: "mb-2", children: "ตัวอย่างหมายเลขเอกสาร" }),
|
|
1925
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-lg", children: formatNumber ? formatNumber.replace("{YYYY}", (/* @__PURE__ */ new Date()).getFullYear().toString()).replace("{0000}", (forcedNumber || "1").padStart(4, "0")) : "ยังไม่ได้กำหนดรูปแบบ" })
|
|
1926
|
+
] }),
|
|
1927
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 justify-end", children: [
|
|
1928
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1929
|
+
ui.Button,
|
|
1930
|
+
{
|
|
1931
|
+
variant: "secondary",
|
|
1932
|
+
onClick: fetchSettings,
|
|
1933
|
+
disabled: saving,
|
|
1934
|
+
children: "รีเซ็ต"
|
|
1935
|
+
}
|
|
1936
|
+
),
|
|
1937
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: handleSave, isLoading: saving, children: "บันทึกการตั้งค่า" })
|
|
1938
|
+
] })
|
|
1939
|
+
] })
|
|
1940
|
+
] }) })
|
|
1941
|
+
] });
|
|
1942
|
+
};
|
|
1943
|
+
const config$1 = adminSdk.defineRouteConfig({
|
|
1944
|
+
icon: icons.DocumentText,
|
|
1945
|
+
label: "ตั้งค่าใบแพ็คสินค้า"
|
|
1946
|
+
});
|
|
1509
1947
|
const CARRIER_TYPES$1 = [
|
|
1510
1948
|
{ value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
|
|
1511
1949
|
{ value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
|
|
@@ -1994,6 +2432,10 @@ const config = adminSdk.defineRouteConfig({
|
|
|
1994
2432
|
label: "อัตราค่าขนส่ง"
|
|
1995
2433
|
});
|
|
1996
2434
|
const widgetModule = { widgets: [
|
|
2435
|
+
{
|
|
2436
|
+
Component: OrderPackingSlipWidget,
|
|
2437
|
+
zone: ["order.details.after"]
|
|
2438
|
+
},
|
|
1997
2439
|
{
|
|
1998
2440
|
Component: OrderShippingQuoteWidget,
|
|
1999
2441
|
zone: ["order.details.after"]
|
|
@@ -2013,6 +2455,10 @@ const routeModule = {
|
|
|
2013
2455
|
Component: MaterialCostsPage,
|
|
2014
2456
|
path: "/material-costs"
|
|
2015
2457
|
},
|
|
2458
|
+
{
|
|
2459
|
+
Component: PackingSlipSettingsPage,
|
|
2460
|
+
path: "/packing-slip-settings"
|
|
2461
|
+
},
|
|
2016
2462
|
{
|
|
2017
2463
|
Component: RatesPage,
|
|
2018
2464
|
path: "/rates"
|
|
@@ -2021,22 +2467,28 @@ const routeModule = {
|
|
|
2021
2467
|
};
|
|
2022
2468
|
const menuItemModule = {
|
|
2023
2469
|
menuItems: [
|
|
2470
|
+
{
|
|
2471
|
+
label: config$4.label,
|
|
2472
|
+
icon: config$4.icon,
|
|
2473
|
+
path: "/areas",
|
|
2474
|
+
nested: void 0
|
|
2475
|
+
},
|
|
2024
2476
|
{
|
|
2025
2477
|
label: config$3.label,
|
|
2026
2478
|
icon: config$3.icon,
|
|
2027
|
-
path: "/
|
|
2479
|
+
path: "/boxes",
|
|
2028
2480
|
nested: void 0
|
|
2029
2481
|
},
|
|
2030
2482
|
{
|
|
2031
2483
|
label: config$2.label,
|
|
2032
2484
|
icon: config$2.icon,
|
|
2033
|
-
path: "/
|
|
2485
|
+
path: "/material-costs",
|
|
2034
2486
|
nested: void 0
|
|
2035
2487
|
},
|
|
2036
2488
|
{
|
|
2037
2489
|
label: config$1.label,
|
|
2038
2490
|
icon: config$1.icon,
|
|
2039
|
-
path: "/
|
|
2491
|
+
path: "/packing-slip-settings",
|
|
2040
2492
|
nested: void 0
|
|
2041
2493
|
},
|
|
2042
2494
|
{
|
|
@@ -1,10 +1,231 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
|
|
3
|
-
import { Container, Heading, Button, Text, Badge, Drawer, Label, Select, Input, Switch, toast, Table, Prompt, Textarea } from "@medusajs/ui";
|
|
4
2
|
import { useState, useEffect } from "react";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { Container, Heading, Text, Toaster, Badge, Button, toast, Drawer, Label, Select, Input, Switch, Table, Prompt, Textarea } from "@medusajs/ui";
|
|
4
|
+
import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
|
|
5
|
+
import { DocumentText, MapPin, ArchiveBox, CurrencyDollarSolid, HandTruck } from "@medusajs/icons";
|
|
6
|
+
import { RefreshCw, Download, Plus, Package, AlertCircle, Truck, Clock, Edit, Trash, Eye } from "lucide-react";
|
|
7
7
|
import "@medusajs/admin-shared";
|
|
8
|
+
const OrderPackingSlipWidget = ({ data }) => {
|
|
9
|
+
const [packingSlip, setPackingSlip] = useState(null);
|
|
10
|
+
const [loading, setLoading] = useState(true);
|
|
11
|
+
const [generating, setGenerating] = useState(false);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
const order = data;
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
fetchPackingSlip();
|
|
16
|
+
}, [order.id]);
|
|
17
|
+
const fetchPackingSlip = async () => {
|
|
18
|
+
setLoading(true);
|
|
19
|
+
setError(null);
|
|
20
|
+
try {
|
|
21
|
+
const response = await fetch(
|
|
22
|
+
`/admin/documents/packing-slip?orderId=${order.id}`,
|
|
23
|
+
{
|
|
24
|
+
credentials: "include"
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new Error("Failed to fetch packing slip");
|
|
29
|
+
}
|
|
30
|
+
const data2 = await response.json();
|
|
31
|
+
setPackingSlip(data2.packingSlip || null);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
console.error("Error fetching packing slip:", err);
|
|
34
|
+
setError(err.message);
|
|
35
|
+
} finally {
|
|
36
|
+
setLoading(false);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const generatePackingSlip = async () => {
|
|
40
|
+
setGenerating(true);
|
|
41
|
+
setError(null);
|
|
42
|
+
try {
|
|
43
|
+
const response = await fetch("/admin/documents/packing-slip", {
|
|
44
|
+
method: "POST",
|
|
45
|
+
headers: {
|
|
46
|
+
"Content-Type": "application/json"
|
|
47
|
+
},
|
|
48
|
+
credentials: "include",
|
|
49
|
+
body: JSON.stringify({
|
|
50
|
+
order_id: order.id
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const errorData = await response.json().catch(() => ({
|
|
55
|
+
message: `HTTP ${response.status}: ${response.statusText}`
|
|
56
|
+
}));
|
|
57
|
+
throw new Error(errorData.message || "Failed to generate packing slip");
|
|
58
|
+
}
|
|
59
|
+
const data2 = await response.json();
|
|
60
|
+
setPackingSlip(data2.packingSlip);
|
|
61
|
+
toast.success("สร้างใบแพ็คสินค้าสำเร็จ", {
|
|
62
|
+
description: `หมายเลขเอกสาร: ${data2.packingSlip.number}`
|
|
63
|
+
});
|
|
64
|
+
} catch (err) {
|
|
65
|
+
setError(err.message);
|
|
66
|
+
toast.error("เกิดข้อผิดพลาด", {
|
|
67
|
+
description: err.message
|
|
68
|
+
});
|
|
69
|
+
} finally {
|
|
70
|
+
setGenerating(false);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const downloadPackingSlip = async () => {
|
|
74
|
+
var _a;
|
|
75
|
+
try {
|
|
76
|
+
const response = await fetch(
|
|
77
|
+
`/admin/documents/packing-slip?orderId=${order.id}&includeBuffer=true`,
|
|
78
|
+
{
|
|
79
|
+
credentials: "include"
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new Error("Failed to fetch packing slip");
|
|
84
|
+
}
|
|
85
|
+
const data2 = await response.json();
|
|
86
|
+
if ((_a = data2.packingSlip) == null ? void 0 : _a.pdfBase64) {
|
|
87
|
+
const byteCharacters = atob(data2.packingSlip.pdfBase64);
|
|
88
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
89
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
90
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
91
|
+
}
|
|
92
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
93
|
+
const blob = new Blob([byteArray], { type: "application/pdf" });
|
|
94
|
+
const url = window.URL.createObjectURL(blob);
|
|
95
|
+
const link = document.createElement("a");
|
|
96
|
+
link.href = url;
|
|
97
|
+
link.download = `packing-slip-${data2.packingSlip.number}.pdf`;
|
|
98
|
+
document.body.appendChild(link);
|
|
99
|
+
link.click();
|
|
100
|
+
document.body.removeChild(link);
|
|
101
|
+
window.URL.revokeObjectURL(url);
|
|
102
|
+
toast.success("ดาวน์โหลดสำเร็จ");
|
|
103
|
+
} else {
|
|
104
|
+
throw new Error("PDF data not available");
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
toast.error("เกิดข้อผิดพลาดในการดาวน์โหลด", {
|
|
108
|
+
description: err.message
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const formatDateTime = (dateString) => {
|
|
113
|
+
const date = new Date(dateString);
|
|
114
|
+
return new Intl.DateTimeFormat("th-TH", {
|
|
115
|
+
year: "numeric",
|
|
116
|
+
month: "short",
|
|
117
|
+
day: "numeric",
|
|
118
|
+
hour: "2-digit",
|
|
119
|
+
minute: "2-digit"
|
|
120
|
+
}).format(date);
|
|
121
|
+
};
|
|
122
|
+
if (loading) {
|
|
123
|
+
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
124
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
125
|
+
/* @__PURE__ */ jsx(DocumentText, { className: "text-ui-fg-subtle" }),
|
|
126
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "ใบแพ็คสินค้า" })
|
|
127
|
+
] }) }),
|
|
128
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "กำลังโหลด..." }) })
|
|
129
|
+
] });
|
|
130
|
+
}
|
|
131
|
+
if (error && !packingSlip) {
|
|
132
|
+
return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
133
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
134
|
+
/* @__PURE__ */ jsx(DocumentText, { className: "text-ui-fg-subtle" }),
|
|
135
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "ใบแพ็คสินค้า" })
|
|
136
|
+
] }) }),
|
|
137
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Text, { className: "text-red-500", children: error }) })
|
|
138
|
+
] });
|
|
139
|
+
}
|
|
140
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
141
|
+
/* @__PURE__ */ jsx(Toaster, {}),
|
|
142
|
+
/* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
|
|
143
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
144
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
145
|
+
/* @__PURE__ */ jsx(DocumentText, { className: "text-ui-fg-subtle" }),
|
|
146
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "ใบแพ็คสินค้า" }),
|
|
147
|
+
packingSlip && /* @__PURE__ */ jsx(Badge, { color: "green", size: "small", children: "สร้างแล้ว" })
|
|
148
|
+
] }),
|
|
149
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
150
|
+
packingSlip && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
151
|
+
/* @__PURE__ */ jsx(
|
|
152
|
+
Button,
|
|
153
|
+
{
|
|
154
|
+
size: "small",
|
|
155
|
+
variant: "transparent",
|
|
156
|
+
onClick: fetchPackingSlip,
|
|
157
|
+
children: /* @__PURE__ */ jsx(RefreshCw, { size: 16 })
|
|
158
|
+
}
|
|
159
|
+
),
|
|
160
|
+
/* @__PURE__ */ jsxs(
|
|
161
|
+
Button,
|
|
162
|
+
{
|
|
163
|
+
size: "small",
|
|
164
|
+
variant: "secondary",
|
|
165
|
+
onClick: downloadPackingSlip,
|
|
166
|
+
children: [
|
|
167
|
+
/* @__PURE__ */ jsx(Download, { size: 16 }),
|
|
168
|
+
"ดาวน์โหลด"
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
] }),
|
|
173
|
+
!packingSlip && /* @__PURE__ */ jsxs(
|
|
174
|
+
Button,
|
|
175
|
+
{
|
|
176
|
+
size: "small",
|
|
177
|
+
onClick: generatePackingSlip,
|
|
178
|
+
isLoading: generating,
|
|
179
|
+
children: [
|
|
180
|
+
/* @__PURE__ */ jsx(Plus, { size: 16 }),
|
|
181
|
+
"สร้างใบแพ็คสินค้า"
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
] })
|
|
186
|
+
] }),
|
|
187
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: packingSlip ? /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
188
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
189
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
190
|
+
/* @__PURE__ */ jsx(Text, { size: "xsmall", className: "text-ui-fg-subtle", children: "หมายเลขเอกสาร" }),
|
|
191
|
+
/* @__PURE__ */ jsx(Text, { weight: "plus", className: "font-mono", children: packingSlip.number })
|
|
192
|
+
] }),
|
|
193
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
194
|
+
/* @__PURE__ */ jsx(Text, { size: "xsmall", className: "text-ui-fg-subtle", children: "สร้างเมื่อ" }),
|
|
195
|
+
/* @__PURE__ */ jsx(Text, { size: "small", children: formatDateTime(packingSlip.created_at) })
|
|
196
|
+
] })
|
|
197
|
+
] }),
|
|
198
|
+
/* @__PURE__ */ jsx("div", { className: "pt-4 border-t", children: /* @__PURE__ */ jsxs(Text, { size: "xsmall", className: "text-ui-fg-muted", children: [
|
|
199
|
+
"Document ID: ",
|
|
200
|
+
packingSlip.id
|
|
201
|
+
] }) }),
|
|
202
|
+
/* @__PURE__ */ jsxs("div", { className: "pt-4 border-t", children: [
|
|
203
|
+
/* @__PURE__ */ jsxs(
|
|
204
|
+
Button,
|
|
205
|
+
{
|
|
206
|
+
size: "small",
|
|
207
|
+
variant: "secondary",
|
|
208
|
+
onClick: generatePackingSlip,
|
|
209
|
+
isLoading: generating,
|
|
210
|
+
children: [
|
|
211
|
+
/* @__PURE__ */ jsx(RefreshCw, { size: 16 }),
|
|
212
|
+
"สร้างใหม่"
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
),
|
|
216
|
+
/* @__PURE__ */ jsx(Text, { size: "xsmall", className: "text-ui-fg-muted mt-2", children: "การสร้างใหม่จะแทนที่ใบแพ็คสินค้าเดิม" })
|
|
217
|
+
] })
|
|
218
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
|
|
219
|
+
/* @__PURE__ */ jsx(DocumentText, { className: "text-ui-fg-muted mb-4" }),
|
|
220
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle mb-2", children: "ยังไม่มีใบแพ็คสินค้าสำหรับคำสั่งซื้อนี้" }),
|
|
221
|
+
/* @__PURE__ */ jsx(Text, { size: "xsmall", className: "text-ui-fg-muted mb-4", children: "คลิกปุ่มด้านบนเพื่อสร้างใบแพ็คสินค้า" })
|
|
222
|
+
] }) })
|
|
223
|
+
] })
|
|
224
|
+
] });
|
|
225
|
+
};
|
|
226
|
+
defineWidgetConfig({
|
|
227
|
+
zone: "order.details.after"
|
|
228
|
+
});
|
|
8
229
|
const OrderShippingQuoteWidget = ({ data }) => {
|
|
9
230
|
const [quote, setQuote] = useState(null);
|
|
10
231
|
const [loading, setLoading] = useState(false);
|
|
@@ -43,16 +264,18 @@ const OrderShippingQuoteWidget = ({ data }) => {
|
|
|
43
264
|
},
|
|
44
265
|
allow_split_boxes: true
|
|
45
266
|
};
|
|
46
|
-
const response = await fetch("/
|
|
267
|
+
const response = await fetch("/admin/quote", {
|
|
47
268
|
method: "POST",
|
|
48
269
|
headers: {
|
|
49
|
-
"Content-Type": "application/json"
|
|
50
|
-
"x-publishable-api-key": "pk_99d0d47048eaf52697f42d149a7f58c1661652292b9e1ea1445f519cd16dc071"
|
|
270
|
+
"Content-Type": "application/json"
|
|
51
271
|
},
|
|
272
|
+
credentials: "include",
|
|
52
273
|
body: JSON.stringify(payload)
|
|
53
274
|
});
|
|
54
275
|
if (!response.ok) {
|
|
55
|
-
const errorData = await response.json()
|
|
276
|
+
const errorData = await response.json().catch(() => ({
|
|
277
|
+
message: `HTTP ${response.status}: ${response.statusText}`
|
|
278
|
+
}));
|
|
56
279
|
throw new Error(errorData.message || "Failed to fetch quote");
|
|
57
280
|
}
|
|
58
281
|
const data2 = await response.json();
|
|
@@ -650,7 +873,7 @@ const AreasPage = () => {
|
|
|
650
873
|
/* @__PURE__ */ jsx(ServiceAreasTable, {})
|
|
651
874
|
] }) });
|
|
652
875
|
};
|
|
653
|
-
const config$
|
|
876
|
+
const config$4 = defineRouteConfig({
|
|
654
877
|
icon: MapPin,
|
|
655
878
|
label: "พื้นที่บริการ"
|
|
656
879
|
});
|
|
@@ -1058,7 +1281,7 @@ const BoxesPage = () => {
|
|
|
1058
1281
|
/* @__PURE__ */ jsx(ParcelBoxesTable, {})
|
|
1059
1282
|
] }) });
|
|
1060
1283
|
};
|
|
1061
|
-
const config$
|
|
1284
|
+
const config$3 = defineRouteConfig({
|
|
1062
1285
|
icon: ArchiveBox,
|
|
1063
1286
|
label: "กล่องพัสดุ"
|
|
1064
1287
|
});
|
|
@@ -1501,10 +1724,225 @@ const MaterialCostsPage = () => {
|
|
|
1501
1724
|
/* @__PURE__ */ jsx(MaterialCostsTable, {})
|
|
1502
1725
|
] }) });
|
|
1503
1726
|
};
|
|
1504
|
-
const config$
|
|
1727
|
+
const config$2 = defineRouteConfig({
|
|
1505
1728
|
icon: CurrencyDollarSolid,
|
|
1506
1729
|
label: "ต้นทุนวัสดุ"
|
|
1507
1730
|
});
|
|
1731
|
+
const PackingSlipSettingsPage = () => {
|
|
1732
|
+
const [settings, setSettings] = useState(null);
|
|
1733
|
+
const [loading, setLoading] = useState(true);
|
|
1734
|
+
const [saving, setSaving] = useState(false);
|
|
1735
|
+
const [formatNumber, setFormatNumber] = useState("");
|
|
1736
|
+
const [forcedNumber, setForcedNumber] = useState("");
|
|
1737
|
+
const [template, setTemplate] = useState("BASIC");
|
|
1738
|
+
useEffect(() => {
|
|
1739
|
+
fetchSettings();
|
|
1740
|
+
}, []);
|
|
1741
|
+
const fetchSettings = async () => {
|
|
1742
|
+
setLoading(true);
|
|
1743
|
+
try {
|
|
1744
|
+
const response = await fetch(
|
|
1745
|
+
"/admin/documents/document-packing-slip-settings",
|
|
1746
|
+
{
|
|
1747
|
+
credentials: "include"
|
|
1748
|
+
}
|
|
1749
|
+
);
|
|
1750
|
+
if (!response.ok) {
|
|
1751
|
+
throw new Error("Failed to fetch settings");
|
|
1752
|
+
}
|
|
1753
|
+
const data = await response.json();
|
|
1754
|
+
if (data.settings) {
|
|
1755
|
+
setSettings(data.settings);
|
|
1756
|
+
setFormatNumber(data.settings.formatNumber || "");
|
|
1757
|
+
setForcedNumber(data.settings.forcedNumber || "");
|
|
1758
|
+
setTemplate(data.settings.template || "BASIC");
|
|
1759
|
+
}
|
|
1760
|
+
} catch (err) {
|
|
1761
|
+
toast.error("เกิดข้อผิดพลาด", {
|
|
1762
|
+
description: err.message
|
|
1763
|
+
});
|
|
1764
|
+
} finally {
|
|
1765
|
+
setLoading(false);
|
|
1766
|
+
}
|
|
1767
|
+
};
|
|
1768
|
+
const handleSave = async () => {
|
|
1769
|
+
setSaving(true);
|
|
1770
|
+
try {
|
|
1771
|
+
const response = await fetch(
|
|
1772
|
+
"/admin/documents/document-packing-slip-settings",
|
|
1773
|
+
{
|
|
1774
|
+
method: "POST",
|
|
1775
|
+
headers: {
|
|
1776
|
+
"Content-Type": "application/json"
|
|
1777
|
+
},
|
|
1778
|
+
credentials: "include",
|
|
1779
|
+
body: JSON.stringify({
|
|
1780
|
+
formatNumber: formatNumber || void 0,
|
|
1781
|
+
forcedNumber: forcedNumber || void 0,
|
|
1782
|
+
template
|
|
1783
|
+
})
|
|
1784
|
+
}
|
|
1785
|
+
);
|
|
1786
|
+
if (!response.ok) {
|
|
1787
|
+
const errorData = await response.json().catch(() => ({
|
|
1788
|
+
message: `HTTP ${response.status}: ${response.statusText}`
|
|
1789
|
+
}));
|
|
1790
|
+
throw new Error(errorData.message || "Failed to save settings");
|
|
1791
|
+
}
|
|
1792
|
+
const data = await response.json();
|
|
1793
|
+
setSettings(data.settings);
|
|
1794
|
+
toast.success("บันทึกสำเร็จ", {
|
|
1795
|
+
description: "ตั้งค่าใบแพ็คสินค้าได้รับการบันทึกแล้ว"
|
|
1796
|
+
});
|
|
1797
|
+
} catch (err) {
|
|
1798
|
+
toast.error("เกิดข้อผิดพลาดในการบันทึก", {
|
|
1799
|
+
description: err.message
|
|
1800
|
+
});
|
|
1801
|
+
} finally {
|
|
1802
|
+
setSaving(false);
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
const handlePreview = async () => {
|
|
1806
|
+
var _a;
|
|
1807
|
+
try {
|
|
1808
|
+
const response = await fetch(
|
|
1809
|
+
`/admin/documents/packing-slip/preview?template=${template}`,
|
|
1810
|
+
{
|
|
1811
|
+
credentials: "include"
|
|
1812
|
+
}
|
|
1813
|
+
);
|
|
1814
|
+
if (!response.ok) {
|
|
1815
|
+
const errorData = await response.json().catch(() => ({
|
|
1816
|
+
message: `HTTP ${response.status}: ${response.statusText}`
|
|
1817
|
+
}));
|
|
1818
|
+
throw new Error(errorData.message || "Failed to generate preview");
|
|
1819
|
+
}
|
|
1820
|
+
const data = await response.json();
|
|
1821
|
+
if ((_a = data.packingSlip) == null ? void 0 : _a.pdfBase64) {
|
|
1822
|
+
const byteCharacters = atob(data.packingSlip.pdfBase64);
|
|
1823
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
1824
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
1825
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
1826
|
+
}
|
|
1827
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
1828
|
+
const blob = new Blob([byteArray], { type: "application/pdf" });
|
|
1829
|
+
const url = window.URL.createObjectURL(blob);
|
|
1830
|
+
window.open(url, "_blank");
|
|
1831
|
+
toast.success("เปิดตัวอย่างในแท็บใหม่");
|
|
1832
|
+
} else {
|
|
1833
|
+
throw new Error("PDF data not available");
|
|
1834
|
+
}
|
|
1835
|
+
} catch (err) {
|
|
1836
|
+
toast.error("เกิดข้อผิดพลาดในการแสดงตัวอย่าง", {
|
|
1837
|
+
description: err.message
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
if (loading) {
|
|
1842
|
+
return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4", children: [
|
|
1843
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "ตั้งค่าใบแพ็คสินค้า" }),
|
|
1844
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "กำลังโหลด..." })
|
|
1845
|
+
] }) });
|
|
1846
|
+
}
|
|
1847
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1848
|
+
/* @__PURE__ */ jsx(Toaster, {}),
|
|
1849
|
+
/* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-6", children: [
|
|
1850
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
1851
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "ตั้งค่าใบแพ็คสินค้า" }),
|
|
1852
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle mt-1", children: "กำหนดรูปแบบและเทมเพลตสำหรับใบแพ็คสินค้า" })
|
|
1853
|
+
] }) }),
|
|
1854
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-6", children: [
|
|
1855
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
1856
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "template", weight: "plus", children: "เทมเพลต" }),
|
|
1857
|
+
/* @__PURE__ */ jsxs(Select, { value: template, onValueChange: setTemplate, children: [
|
|
1858
|
+
/* @__PURE__ */ jsx(Select.Trigger, { id: "template", children: /* @__PURE__ */ jsx(Select.Value, {}) }),
|
|
1859
|
+
/* @__PURE__ */ jsxs(Select.Content, { children: [
|
|
1860
|
+
/* @__PURE__ */ jsx(Select.Item, { value: "BASIC", children: "Basic - ใบแพ็คมาตรฐาน" }),
|
|
1861
|
+
/* @__PURE__ */ jsx(Select.Item, { value: "BASIC_SMALL", children: "Basic Small - ใบแพ็คขนาดเล็ก" })
|
|
1862
|
+
] })
|
|
1863
|
+
] }),
|
|
1864
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "เลือกรูปแบบเทมเพลตสำหรับใบแพ็คสินค้า" }),
|
|
1865
|
+
/* @__PURE__ */ jsxs(
|
|
1866
|
+
Button,
|
|
1867
|
+
{
|
|
1868
|
+
size: "small",
|
|
1869
|
+
variant: "secondary",
|
|
1870
|
+
onClick: handlePreview,
|
|
1871
|
+
className: "mt-2 w-fit",
|
|
1872
|
+
children: [
|
|
1873
|
+
/* @__PURE__ */ jsx(Eye, { size: 16 }),
|
|
1874
|
+
"ดูตัวอย่างเทมเพลต"
|
|
1875
|
+
]
|
|
1876
|
+
}
|
|
1877
|
+
)
|
|
1878
|
+
] }),
|
|
1879
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
1880
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "formatNumber", weight: "plus", children: "รูปแบบหมายเลขเอกสาร" }),
|
|
1881
|
+
/* @__PURE__ */ jsx(
|
|
1882
|
+
Input,
|
|
1883
|
+
{
|
|
1884
|
+
id: "formatNumber",
|
|
1885
|
+
placeholder: "เช่น PS-{YYYY}-{0000}",
|
|
1886
|
+
value: formatNumber,
|
|
1887
|
+
onChange: (e) => setFormatNumber(e.target.value)
|
|
1888
|
+
}
|
|
1889
|
+
),
|
|
1890
|
+
/* @__PURE__ */ jsxs(Text, { size: "small", className: "text-ui-fg-subtle", children: [
|
|
1891
|
+
"กำหนดรูปแบบหมายเลขใบแพ็คสินค้า ใช้ ",
|
|
1892
|
+
"{YYYY}",
|
|
1893
|
+
" สำหรับปี และ",
|
|
1894
|
+
" ",
|
|
1895
|
+
"{0000}",
|
|
1896
|
+
" สำหรับเลขที่เอกสาร"
|
|
1897
|
+
] })
|
|
1898
|
+
] }),
|
|
1899
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
1900
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "forcedNumber", weight: "plus", children: "กำหนดหมายเลขเริ่มต้น" }),
|
|
1901
|
+
/* @__PURE__ */ jsx(
|
|
1902
|
+
Input,
|
|
1903
|
+
{
|
|
1904
|
+
id: "forcedNumber",
|
|
1905
|
+
type: "number",
|
|
1906
|
+
placeholder: "เช่น 1",
|
|
1907
|
+
value: forcedNumber,
|
|
1908
|
+
onChange: (e) => setForcedNumber(e.target.value)
|
|
1909
|
+
}
|
|
1910
|
+
),
|
|
1911
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "กำหนดหมายเลขเริ่มต้นสำหรับเอกสารใหม่ (ถ้าไม่ระบุจะนับต่อจากเอกสารล่าสุด)" })
|
|
1912
|
+
] }),
|
|
1913
|
+
settings && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border p-4 bg-ui-bg-subtle", children: [
|
|
1914
|
+
/* @__PURE__ */ jsx(Text, { weight: "plus", size: "small", className: "mb-2", children: "ข้อมูลการตั้งค่าปัจจุบัน" }),
|
|
1915
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 text-sm text-ui-fg-subtle", children: [
|
|
1916
|
+
/* @__PURE__ */ jsx("div", { children: "อัปเดตล่าสุด:" }),
|
|
1917
|
+
/* @__PURE__ */ jsx("div", { children: new Date(settings.updated_at).toLocaleString("th-TH") }),
|
|
1918
|
+
/* @__PURE__ */ jsx("div", { children: "สร้างเมื่อ:" }),
|
|
1919
|
+
/* @__PURE__ */ jsx("div", { children: new Date(settings.created_at).toLocaleString("th-TH") })
|
|
1920
|
+
] })
|
|
1921
|
+
] }),
|
|
1922
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-lg border p-4", children: [
|
|
1923
|
+
/* @__PURE__ */ jsx(Text, { weight: "plus", size: "small", className: "mb-2", children: "ตัวอย่างหมายเลขเอกสาร" }),
|
|
1924
|
+
/* @__PURE__ */ jsx("div", { className: "font-mono text-lg", children: formatNumber ? formatNumber.replace("{YYYY}", (/* @__PURE__ */ new Date()).getFullYear().toString()).replace("{0000}", (forcedNumber || "1").padStart(4, "0")) : "ยังไม่ได้กำหนดรูปแบบ" })
|
|
1925
|
+
] }),
|
|
1926
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 justify-end", children: [
|
|
1927
|
+
/* @__PURE__ */ jsx(
|
|
1928
|
+
Button,
|
|
1929
|
+
{
|
|
1930
|
+
variant: "secondary",
|
|
1931
|
+
onClick: fetchSettings,
|
|
1932
|
+
disabled: saving,
|
|
1933
|
+
children: "รีเซ็ต"
|
|
1934
|
+
}
|
|
1935
|
+
),
|
|
1936
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleSave, isLoading: saving, children: "บันทึกการตั้งค่า" })
|
|
1937
|
+
] })
|
|
1938
|
+
] })
|
|
1939
|
+
] }) })
|
|
1940
|
+
] });
|
|
1941
|
+
};
|
|
1942
|
+
const config$1 = defineRouteConfig({
|
|
1943
|
+
icon: DocumentText,
|
|
1944
|
+
label: "ตั้งค่าใบแพ็คสินค้า"
|
|
1945
|
+
});
|
|
1508
1946
|
const CARRIER_TYPES$1 = [
|
|
1509
1947
|
{ value: "COMPANY_FLEET", label: "บริษัทรถส่งของ (Messenger)" },
|
|
1510
1948
|
{ value: "COMPANY_TRUCK", label: "รถบริษัท (Same Day)" },
|
|
@@ -1993,6 +2431,10 @@ const config = defineRouteConfig({
|
|
|
1993
2431
|
label: "อัตราค่าขนส่ง"
|
|
1994
2432
|
});
|
|
1995
2433
|
const widgetModule = { widgets: [
|
|
2434
|
+
{
|
|
2435
|
+
Component: OrderPackingSlipWidget,
|
|
2436
|
+
zone: ["order.details.after"]
|
|
2437
|
+
},
|
|
1996
2438
|
{
|
|
1997
2439
|
Component: OrderShippingQuoteWidget,
|
|
1998
2440
|
zone: ["order.details.after"]
|
|
@@ -2012,6 +2454,10 @@ const routeModule = {
|
|
|
2012
2454
|
Component: MaterialCostsPage,
|
|
2013
2455
|
path: "/material-costs"
|
|
2014
2456
|
},
|
|
2457
|
+
{
|
|
2458
|
+
Component: PackingSlipSettingsPage,
|
|
2459
|
+
path: "/packing-slip-settings"
|
|
2460
|
+
},
|
|
2015
2461
|
{
|
|
2016
2462
|
Component: RatesPage,
|
|
2017
2463
|
path: "/rates"
|
|
@@ -2020,22 +2466,28 @@ const routeModule = {
|
|
|
2020
2466
|
};
|
|
2021
2467
|
const menuItemModule = {
|
|
2022
2468
|
menuItems: [
|
|
2469
|
+
{
|
|
2470
|
+
label: config$4.label,
|
|
2471
|
+
icon: config$4.icon,
|
|
2472
|
+
path: "/areas",
|
|
2473
|
+
nested: void 0
|
|
2474
|
+
},
|
|
2023
2475
|
{
|
|
2024
2476
|
label: config$3.label,
|
|
2025
2477
|
icon: config$3.icon,
|
|
2026
|
-
path: "/
|
|
2478
|
+
path: "/boxes",
|
|
2027
2479
|
nested: void 0
|
|
2028
2480
|
},
|
|
2029
2481
|
{
|
|
2030
2482
|
label: config$2.label,
|
|
2031
2483
|
icon: config$2.icon,
|
|
2032
|
-
path: "/
|
|
2484
|
+
path: "/material-costs",
|
|
2033
2485
|
nested: void 0
|
|
2034
2486
|
},
|
|
2035
2487
|
{
|
|
2036
2488
|
label: config$1.label,
|
|
2037
2489
|
icon: config$1.icon,
|
|
2038
|
-
path: "/
|
|
2490
|
+
path: "/packing-slip-settings",
|
|
2039
2491
|
nested: void 0
|
|
2040
2492
|
},
|
|
2041
2493
|
{
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.POST = POST;
|
|
4
|
+
const framework_1 = require("@medusajs/framework");
|
|
5
|
+
const parcel_shipping_1 = require("../../../modules/parcel-shipping");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const QuoteItemSchema = zod_1.z.object({
|
|
8
|
+
sku: zod_1.z.string(),
|
|
9
|
+
width: zod_1.z.number().positive(),
|
|
10
|
+
length: zod_1.z.number().positive(),
|
|
11
|
+
height: zod_1.z.number().positive(),
|
|
12
|
+
weight: zod_1.z.number().positive(),
|
|
13
|
+
quantity: zod_1.z.number().int().positive(),
|
|
14
|
+
attributes: zod_1.z
|
|
15
|
+
.object({
|
|
16
|
+
noStack: zod_1.z.boolean().optional(),
|
|
17
|
+
fragile: zod_1.z.boolean().optional(),
|
|
18
|
+
stackable: zod_1.z.number().optional(),
|
|
19
|
+
})
|
|
20
|
+
.optional(),
|
|
21
|
+
});
|
|
22
|
+
const ShippingAddressSchema = zod_1.z.object({
|
|
23
|
+
address_line_1: zod_1.z.string().optional(), // House number, building, floor
|
|
24
|
+
address_line_2: zod_1.z.string().optional(), // Additional address info
|
|
25
|
+
street: zod_1.z.string().optional(), // Street name (ถนน)
|
|
26
|
+
sub_district: zod_1.z.string().optional(), // Sub-district (ตำบล/แขวง)
|
|
27
|
+
district: zod_1.z.string().optional(), // District (อำเภอ/เขต)
|
|
28
|
+
city: zod_1.z.string().optional(), // City
|
|
29
|
+
province: zod_1.z.string().optional(), // Province (จังหวัด)
|
|
30
|
+
postcode: zod_1.z.string().optional(), // Postal code
|
|
31
|
+
country: zod_1.z.string().default("TH"),
|
|
32
|
+
});
|
|
33
|
+
const PreferredServiceSchema = zod_1.z.object({
|
|
34
|
+
carrier_type: zod_1.z.enum(["COMPANY_FLEET", "COMPANY_TRUCK", "PRIVATE_CARRIER"]),
|
|
35
|
+
service_code: zod_1.z.enum([
|
|
36
|
+
"MESSENGER_3H",
|
|
37
|
+
"SAME_DAY",
|
|
38
|
+
"STANDARD_3_5D",
|
|
39
|
+
"EXPRESS_1_2D",
|
|
40
|
+
]),
|
|
41
|
+
});
|
|
42
|
+
const QuoteRequestSchema = zod_1.z.object({
|
|
43
|
+
items: zod_1.z.array(QuoteItemSchema).min(1),
|
|
44
|
+
shipping_address: ShippingAddressSchema,
|
|
45
|
+
origin_address: ShippingAddressSchema.optional(),
|
|
46
|
+
preferred_service: PreferredServiceSchema.optional(),
|
|
47
|
+
allow_split_boxes: zod_1.z.boolean().optional().default(true),
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* POST /store/quote
|
|
51
|
+
* Get shipping quote with box selection and multiple service options
|
|
52
|
+
*
|
|
53
|
+
* Request Body:
|
|
54
|
+
* - items: Array of items to ship
|
|
55
|
+
* - shipping_address: Destination address
|
|
56
|
+
* - origin_address: Origin address (optional, for distance calculation)
|
|
57
|
+
* - preferred_service: Preferred carrier/service (optional)
|
|
58
|
+
* - allow_split_boxes: Allow splitting items across multiple boxes (default: true)
|
|
59
|
+
*
|
|
60
|
+
* Returns:
|
|
61
|
+
* - best_box: First box (for backward compatibility)
|
|
62
|
+
* - packing: First box packing details (for backward compatibility)
|
|
63
|
+
* - boxes: Array of all boxes with packing details
|
|
64
|
+
* - shipping_options: All available shipping options with cost breakdown
|
|
65
|
+
* - selected: Selected/preferred option
|
|
66
|
+
* - distance: Distance calculation result (if origin_address provided)
|
|
67
|
+
*/
|
|
68
|
+
async function POST(req, res) {
|
|
69
|
+
const parcelService = framework_1.container.resolve(parcel_shipping_1.PARCEL_SHIPPING_MODULE);
|
|
70
|
+
try {
|
|
71
|
+
const validated = QuoteRequestSchema.parse(req.body);
|
|
72
|
+
console.log("Quote request validated:", {
|
|
73
|
+
items_count: validated.items.length,
|
|
74
|
+
shipping_address: validated.shipping_address,
|
|
75
|
+
preferred_service: validated.preferred_service,
|
|
76
|
+
});
|
|
77
|
+
const result = await parcelService.quote(validated);
|
|
78
|
+
res.json({
|
|
79
|
+
best_box: result.best_box,
|
|
80
|
+
packing: result.packing,
|
|
81
|
+
boxes: result.boxes,
|
|
82
|
+
shipping_options: result.shipping_options,
|
|
83
|
+
selected: result.selected,
|
|
84
|
+
distance: result.distance,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (error.name === "ZodError" && error.errors) {
|
|
89
|
+
console.error("Zod validation errors:", JSON.stringify(error.errors, null, 2));
|
|
90
|
+
return res.status(400).json({
|
|
91
|
+
code: "invalid_request",
|
|
92
|
+
message: "Invalid request data",
|
|
93
|
+
errors: error.errors,
|
|
94
|
+
details: error.errors.map((e) => ({
|
|
95
|
+
path: e.path.join("."),
|
|
96
|
+
message: e.message,
|
|
97
|
+
received: e.received,
|
|
98
|
+
})),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
console.error("Quote API error:", {
|
|
102
|
+
name: error.name,
|
|
103
|
+
message: error.message,
|
|
104
|
+
stack: error.stack,
|
|
105
|
+
});
|
|
106
|
+
return res.status(400).json({
|
|
107
|
+
code: error.code || "quote_failed",
|
|
108
|
+
message: error.message || "Failed to generate quote",
|
|
109
|
+
error_details: {
|
|
110
|
+
name: error.name,
|
|
111
|
+
stack: error.stack?.split("\n").slice(0, 5).join("\n"),
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3F1b3RlL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBc0VBLG9CQXVEQztBQTVIRCxtREFBZ0Q7QUFDaEQsc0VBQTBFO0FBQzFFLDZCQUF3QjtBQUV4QixNQUFNLGVBQWUsR0FBRyxPQUFDLENBQUMsTUFBTSxDQUFDO0lBQy9CLEdBQUcsRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFO0lBQ2YsS0FBSyxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDNUIsTUFBTSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDN0IsTUFBTSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDN0IsTUFBTSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDN0IsUUFBUSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDckMsVUFBVSxFQUFFLE9BQUM7U0FDVixNQUFNLENBQUM7UUFDTixPQUFPLEVBQUUsT0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDLFFBQVEsRUFBRTtRQUMvQixPQUFPLEVBQUUsT0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDLFFBQVEsRUFBRTtRQUMvQixTQUFTLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsRUFBRTtLQUNqQyxDQUFDO1NBQ0QsUUFBUSxFQUFFO0NBQ2QsQ0FBQyxDQUFDO0FBRUgsTUFBTSxxQkFBcUIsR0FBRyxPQUFDLENBQUMsTUFBTSxDQUFDO0lBQ3JDLGNBQWMsRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLEVBQUUsZ0NBQWdDO0lBQ3ZFLGNBQWMsRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFF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
|