From f6d7c3ac7cb4f924428c25a75e14a9622b86766e Mon Sep 17 00:00:00 2001 From: "vluk@2fi-solutions.com.hk" Date: Fri, 8 May 2026 15:57:42 +0800 Subject: [PATCH] no message --- src/app/(main)/m18Syn/page.tsx | 162 +++++++++++++++++++++++++-------- 1 file changed, 126 insertions(+), 36 deletions(-) diff --git a/src/app/(main)/m18Syn/page.tsx b/src/app/(main)/m18Syn/page.tsx index 3c82b3c..28ceae7 100644 --- a/src/app/(main)/m18Syn/page.tsx +++ b/src/app/(main)/m18Syn/page.tsx @@ -29,6 +29,62 @@ function TabPanel(props: TabPanelProps) { export default function M18SynPage() { const [tabValue, setTabValue] = useState(0); + type SyncResultDto = { + totalProcessed: number; + totalSuccess: number; + totalFail: number; + query?: string | null; + }; + + type ParsedSyncResult = + | { kind: "exists"; message: string } + | { kind: "not_found"; message: string } + | { kind: "success"; message: string; totalSuccess: number } + | { kind: "fail"; message: string; totalFail: number } + | { kind: "mixed"; message: string; totalSuccess: number; totalFail: number } + | { kind: "raw"; message: string }; + + const parseSyncResult = (docName: string, rawText: string): ParsedSyncResult => { + const text = rawText?.trim() ?? ""; + if (!text) return { kind: "raw", message: "未收到回應" }; + try { + const obj = JSON.parse(text) as Partial; + const totalSuccess = Number(obj.totalSuccess ?? 0); + const totalFail = Number(obj.totalFail ?? 0); + const totalProcessed = Number(obj.totalProcessed ?? 0); + const query = (obj.query ?? "").toString(); + + // newOnly skip message from backend + if (query.includes("skipped") && query.includes("already exists")) { + return { kind: "exists", message: `${docName}已存在系統` }; + } + + // "not found in M18" (sync-by-code returns processed=1, fail=1) + if ( + docName === "送貨訂單" && + totalProcessed === 1 && + totalSuccess === 0 && + totalFail === 1 && + query.includes("code=equal=") + ) { + return { kind: "not_found", message: `在M18找不到${docName}` }; + } + + if (totalSuccess > 0 && totalFail === 0) { + return { kind: "success", totalSuccess, message: `成功同步: ${totalSuccess}張${docName}` }; + } + if (totalSuccess === 0 && totalFail > 0) { + return { kind: "fail", totalFail, message: `失敗: 無法同步${docName}` }; + } + return { kind: "mixed", totalSuccess, totalFail, message: `完成: 成功 ${totalSuccess} / 失敗 ${totalFail} (${docName})` }; + } catch { + // Non-JSON error body or plain text + return { kind: "raw", message: text }; + } + }; + + const formatSyncResultText = (docName: string, rawText: string): string => parseSyncResult(docName, rawText).message; + const [m18PoCode, setM18PoCode] = useState(""); const [isSyncingM18Po, setIsSyncingM18Po] = useState(false); const [m18PoSyncResult, setM18PoSyncResult] = useState(""); @@ -65,7 +121,7 @@ export default function M18SynPage() { ); if (response.status === 401 || response.status === 403) return; const text = await response.text(); - setM18PoSyncResult(text); + setM18PoSyncResult(formatSyncResultText("採購單", text)); if (!response.ok) { alert(`Sync failed: ${response.status}`); } @@ -94,7 +150,7 @@ export default function M18SynPage() { ); if (response.status === 401 || response.status === 403) return; const text = await response.text(); - setM18DoSyncResult(text); + setM18DoSyncResult(formatSyncResultText("送貨訂單", text)); if (!response.ok) { alert(`Sync failed: ${response.status}`); } @@ -107,27 +163,60 @@ export default function M18SynPage() { } }; - /** DO(加單):M18 搜尋需含備註「(加單)」,本地 isEtra = true */ + /** DO(加單):手動按 code 同步,並寫入本地 isEtra=true(可輸入多個 code,用逗號或換行分隔) */ const handleSyncM18DoExtraByCode = async () => { if (m18DoExtraInFlightRef.current) return; - if (!m18DoExtraCode.trim()) { - alert("Please enter DO / shop PO code (加單)."); + const raw = m18DoExtraCode.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; } m18DoExtraInFlightRef.current = true; setIsSyncingM18DoExtra(true); setM18DoExtraSyncResult(""); try { - const response = await clientAuthFetch( - `${NEXT_PUBLIC_API_URL}/m18/test/do-by-code-extra?code=${encodeURIComponent(m18DoExtraCode.trim())}`, - { method: "GET" }, - ); - if (response.status === 401 || response.status === 403) return; - const text = await response.text(); - setM18DoExtraSyncResult(text); - if (!response.ok) { - alert(`Sync failed: ${response.status}`); + const outputs: string[] = []; + const okCodes: string[] = []; + const existsCodes: 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-extra?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 === "exists") existsCodes.push(code); + else if (parsed.kind === "not_found") notFoundCodes.push(code); + else otherFailCodes.push(code); + + const perCodeMsg = + parsed.kind === "success" ? "成功同步" : + parsed.kind === "exists" ? `失敗: ${parsed.message}` : + parsed.kind === "not_found" ? parsed.message : + parsed.message; + outputs.push(`${code}: ${perCodeMsg}${response.ok ? "" : ` (HTTP ${response.status})`}`); } + + // Summary + outputs.push(""); + outputs.push(`共${okCodes.length}張送貨訂單成功`); + if (notFoundCodes.length > 0) outputs.push(`共${notFoundCodes.length}張在M18找不到訂單 (${notFoundCodes.join(", ")})`); + if (existsCodes.length > 0) outputs.push(`共${existsCodes.length}張訂單已存在系統 (${existsCodes.join(", ")})`); + if (otherFailCodes.length > 0) outputs.push(`共${otherFailCodes.length}張同步失敗 (${otherFailCodes.join(", ")})`); + + setM18DoExtraSyncResult(outputs.join("\n")); } catch (e) { console.error("M18 DO (加單) Sync By Code Error:", e); alert("M18 DO (加單) sync failed. Check console/network."); @@ -153,7 +242,7 @@ export default function M18SynPage() { ); if (response.status === 401 || response.status === 403) return; const text = await response.text(); - setM18ProductSyncResult(text); + setM18ProductSyncResult(formatSyncResultText("產品", text)); if (!response.ok) { alert(`Sync failed: ${response.status}`); } @@ -185,14 +274,14 @@ export default function M18SynPage() { setTabValue(v)} aria-label="M18 sync by code" centered variant="fullWidth"> - - - + + + -
+
-
+
-
- - 與「2. DO」相同以單號從 M18 同步 shop PO/送貨單,但 M18 列表條件會限制備註含「(加單)」;同步寫入之 delivery_order.isEtra = true。 - - +
+ setM18DoExtraCode(e.target.value)} - placeholder="e.g. 與一般 DO 相同單號,但須為加單單據" - sx={{ minWidth: 320 }} + placeholder="可輸入多個 code,用逗號或換行分隔" + fullWidth + multiline + minRows={6} + maxRows={14} /> - + + + {m18DoExtraSyncResult ? (