| @@ -135,26 +135,55 @@ export default function M18SynPage() { | |||
| } | |||
| }; | |||
| /** 送貨訂單:手動按 code 同步(可輸入多個 code,用逗號或換行分隔) */ | |||
| const handleSyncM18DoByCode = async () => { | |||
| if (m18DoInFlightRef.current) return; | |||
| if (!m18DoCode.trim()) { | |||
| alert("Please enter DO / shop PO code."); | |||
| const raw = m18DoCode.trim(); | |||
| if (!raw) { | |||
| alert("Please enter DO / shop PO code(s)."); | |||
| return; | |||
| } | |||
| const codes = raw | |||
| .split(/[\n,]+/g) | |||
| .map((s) => s.trim()) | |||
| .filter(Boolean); | |||
| if (codes.length === 0) { | |||
| alert("Please enter at least one code."); | |||
| return; | |||
| } | |||
| m18DoInFlightRef.current = true; | |||
| setIsSyncingM18Do(true); | |||
| setM18DoSyncResult(""); | |||
| try { | |||
| const response = await clientAuthFetch( | |||
| `${NEXT_PUBLIC_API_URL}/m18/test/do-by-code?code=${encodeURIComponent(m18DoCode.trim())}`, | |||
| { method: "GET" }, | |||
| ); | |||
| if (response.status === 401 || response.status === 403) return; | |||
| const text = await response.text(); | |||
| setM18DoSyncResult(formatSyncResultText("送貨訂單", text)); | |||
| if (!response.ok) { | |||
| alert(`Sync failed: ${response.status}`); | |||
| const outputs: string[] = []; | |||
| const okCodes: string[] = []; | |||
| const notFoundCodes: string[] = []; | |||
| const otherFailCodes: string[] = []; | |||
| for (const code of codes) { | |||
| const response = await clientAuthFetch( | |||
| `${NEXT_PUBLIC_API_URL}/m18/test/do-by-code?code=${encodeURIComponent(code)}`, | |||
| { method: "GET" }, | |||
| ); | |||
| if (response.status === 401 || response.status === 403) return; | |||
| const text = await response.text(); | |||
| const parsed = parseSyncResult("送貨訂單", text || ""); | |||
| if (parsed.kind === "success") okCodes.push(code); | |||
| else if (parsed.kind === "not_found") notFoundCodes.push(code); | |||
| else otherFailCodes.push(code); | |||
| const perCodeMsg = | |||
| parsed.kind === "success" ? "成功同步" : | |||
| parsed.kind === "not_found" ? parsed.message : | |||
| parsed.message; | |||
| outputs.push(`${code}: ${perCodeMsg}${response.ok ? "" : ` (HTTP ${response.status})`}`); | |||
| } | |||
| outputs.push(""); | |||
| outputs.push(`共${okCodes.length}張送貨訂單成功`); | |||
| if (notFoundCodes.length > 0) outputs.push(`共${notFoundCodes.length}張在M18找不到訂單 (${notFoundCodes.join(", ")})`); | |||
| if (otherFailCodes.length > 0) outputs.push(`共${otherFailCodes.length}張同步失敗 (${otherFailCodes.join(", ")})`); | |||
| setM18DoSyncResult(outputs.join("\n")); | |||
| } catch (e) { | |||
| console.error("M18 DO Sync By Code Error:", e); | |||
| alert("M18 DO sync failed. Check console/network."); | |||
| @@ -312,18 +341,22 @@ export default function M18SynPage() { | |||
| <TabPanel value={tabValue} index={1}> | |||
| <Section title="M18 送貨訂單 — sync by code"> | |||
| <Stack direction="row" spacing={2} sx={{ mb: 2, alignItems: "center" }}> | |||
| <Stack spacing={2} sx={{ mb: 2 }}> | |||
| <TextField | |||
| size="small" | |||
| label="DO / Shop PO Code" | |||
| value={m18DoCode} | |||
| onChange={(e) => setM18DoCode(e.target.value)} | |||
| placeholder="e.g. same document code as M18 shop PO" | |||
| sx={{ minWidth: 320 }} | |||
| placeholder="可輸入多個 code,用逗號或換行分隔" | |||
| fullWidth | |||
| multiline | |||
| minRows={6} | |||
| maxRows={14} | |||
| /> | |||
| <Button variant="contained" color="primary" onClick={handleSyncM18DoByCode} disabled={isSyncingM18Do}> | |||
| {isSyncingM18Do ? "Syncing..." : "Sync DO from M18"} | |||
| </Button> | |||
| <Box sx={{ display: "flex", justifyContent: "flex-end" }}> | |||
| <Button variant="contained" color="primary" onClick={handleSyncM18DoByCode} disabled={isSyncingM18Do}> | |||
| {isSyncingM18Do ? "Syncing..." : "Sync DO from M18"} | |||
| </Button> | |||
| </Box> | |||
| </Stack> | |||
| {m18DoSyncResult ? ( | |||
| <TextField | |||