| @@ -730,3 +730,20 @@ export async function fetchDoReplenishmentList(params: { | |||||
| headers: { "Content-Type": "application/json" }, | headers: { "Content-Type": "application/json" }, | ||||
| }); | }); | ||||
| } | } | ||||
| export async function fetchDoReplenishmentForBatchRelease(params: { | |||||
| truckLaneCode?: string; | |||||
| shopName?: string; | |||||
| }): Promise<DoReplenishmentRecord[]> { | |||||
| const query = convertObjToURLSearchParams({ | |||||
| truckLaneCode: params.truckLaneCode?.trim() || undefined, | |||||
| shopName: params.shopName?.trim() || undefined, | |||||
| }); | |||||
| return serverFetchJson<DoReplenishmentRecord[]>( | |||||
| `${BASE_API_URL}/do/replenishment/for-batch-release?${query}`, | |||||
| { | |||||
| method: "GET", | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| }, | |||||
| ); | |||||
| } | |||||
| @@ -1,7 +1,17 @@ | |||||
| "use client"; | "use client"; | ||||
| import { DoResult } from "@/app/api/do"; | import { DoResult } from "@/app/api/do"; | ||||
| import { DoSearchAll, DoSearchLiteResponse, fetchDoSearch, fetchAllDoSearch, fetchDoSearchList, releaseDo ,startBatchReleaseAsync, getBatchReleaseProgress} from "@/app/api/do/actions"; | |||||
| import { | |||||
| DoSearchAll, | |||||
| DoSearchLiteResponse, | |||||
| fetchDoSearch, | |||||
| fetchAllDoSearch, | |||||
| fetchDoSearchList, | |||||
| fetchDoReplenishmentForBatchRelease, | |||||
| releaseDo, | |||||
| startBatchReleaseAsync, | |||||
| getBatchReleaseProgress, | |||||
| } from "@/app/api/do/actions"; | |||||
| import { | import { | ||||
| startWorkbenchBatchReleaseAsyncV2, | startWorkbenchBatchReleaseAsyncV2, | ||||
| getWorkbenchBatchReleaseProgress, | getWorkbenchBatchReleaseProgress, | ||||
| @@ -38,6 +48,14 @@ import { useSession } from "next-auth/react"; | |||||
| import { SessionWithTokens } from "@/config/authConfig"; | import { SessionWithTokens } from "@/config/authConfig"; | ||||
| import { useDoSearchRowSelection } from "./useDoSearchRowSelection"; | import { useDoSearchRowSelection } from "./useDoSearchRowSelection"; | ||||
| import DoReplenishmentTab from "./DoReplenishmentTab"; | import DoReplenishmentTab from "./DoReplenishmentTab"; | ||||
| import { | |||||
| applyBatchReleaseSwalLayout, | |||||
| applyMainContentAreaSwalOffset, | |||||
| BATCH_RELEASE_SWAL_WIDTH_WITH_REPLENISH, | |||||
| buildBatchReleaseReplenishmentHtml, | |||||
| deriveReplenishmentFetchParams, | |||||
| filterReplenishmentsForBatchDos, | |||||
| } from "./batchReleaseReplenishmentHtml"; | |||||
| type Props = { | type Props = { | ||||
| filterArgs?: Record<string, any>; | filterArgs?: Record<string, any>; | ||||
| @@ -533,12 +551,12 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||||
| allowOutsideClick: false, | allowOutsideClick: false, | ||||
| allowEscapeKey: false, | allowEscapeKey: false, | ||||
| showConfirmButton: false, | showConfirmButton: false, | ||||
| didOpen: () => { | |||||
| didOpen: (popup) => { | |||||
| applyMainContentAreaSwalOffset(popup); | |||||
| Swal.showLoading(); | Swal.showLoading(); | ||||
| } | |||||
| }, | |||||
| }); | }); | ||||
| // 获取所有匹配的记录 | |||||
| const allMatchingDos = await fetchAllDoSearch( | const allMatchingDos = await fetchAllDoSearch( | ||||
| currentSearchParams.code || "", | currentSearchParams.code || "", | ||||
| currentSearchParams.shopName || "", | currentSearchParams.shopName || "", | ||||
| @@ -548,7 +566,7 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||||
| tabFilter.floor, | tabFilter.floor, | ||||
| tabFilter.isExtra, | tabFilter.isExtra, | ||||
| ); | ); | ||||
| Swal.close(); | Swal.close(); | ||||
| if (allMatchingDos.length === 0) { | if (allMatchingDos.length === 0) { | ||||
| @@ -556,7 +574,8 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||||
| icon: "warning", | icon: "warning", | ||||
| title: t("No Records"), | title: t("No Records"), | ||||
| text: t("No matching records found for batch release."), | text: t("No matching records found for batch release."), | ||||
| confirmButtonText: t("OK") | |||||
| confirmButtonText: t("OK"), | |||||
| didOpen: (popup) => applyMainContentAreaSwalOffset(popup), | |||||
| }); | }); | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -571,19 +590,38 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||||
| title: t("No Records"), | title: t("No Records"), | ||||
| text: t("No delivery orders selected for batch release. Uncheck orders you want to exclude, or search again to reset selection."), | text: t("No delivery orders selected for batch release. Uncheck orders you want to exclude, or search again to reset selection."), | ||||
| confirmButtonText: t("OK"), | confirmButtonText: t("OK"), | ||||
| didOpen: (popup) => applyMainContentAreaSwalOffset(popup), | |||||
| }); | }); | ||||
| return; | return; | ||||
| } | } | ||||
| const dosForRelease = allMatchingDos.filter((row) => idsToRelease.includes(row.id)); | |||||
| const replenishmentFetchParams = deriveReplenishmentFetchParams( | |||||
| dosForRelease, | |||||
| currentSearchParams.shopName || "", | |||||
| effectiveTruckLanceCode, | |||||
| ); | |||||
| const pendingReplenishments = filterReplenishmentsForBatchDos( | |||||
| await fetchDoReplenishmentForBatchRelease(replenishmentFetchParams).catch(() => []), | |||||
| dosForRelease, | |||||
| ); | |||||
| const showMergeExtraOption = isWorkbench && activeTab === "ETRA"; | const showMergeExtraOption = isWorkbench && activeTab === "ETRA"; | ||||
| const replenishmentSectionHtml = buildBatchReleaseReplenishmentHtml( | |||||
| pendingReplenishments, | |||||
| t, | |||||
| ); | |||||
| const hasReplenishmentPreview = pendingReplenishments.length > 0; | |||||
| const result = await Swal.fire({ | const result = await Swal.fire({ | ||||
| icon: "question", | icon: "question", | ||||
| title: t("Batch Release"), | title: t("Batch Release"), | ||||
| width: hasReplenishmentPreview ? BATCH_RELEASE_SWAL_WIDTH_WITH_REPLENISH : undefined, | |||||
| html: ` | html: ` | ||||
| <div style="text-align: left;"> | |||||
| <p>${t("Selected Shop(s): ")}${idsToRelease.length}</p> | |||||
| <p style="font-size: 0.9em; color: #666; margin-top: 8px;"> | |||||
| <div class="do-batch-release-swal-body" style="text-align:left;width:100%;box-sizing:border-box;"> | |||||
| <p style="margin:0 0 4px;text-align:left;">${t("Selected Shop(s): ")}${idsToRelease.length}</p> | |||||
| <p style="font-size:0.9em;color:#666;margin:0;text-align:left;"> | |||||
| ${currentSearchParams.code ? `${t("Code")}: ${currentSearchParams.code} ` : ""} | ${currentSearchParams.code ? `${t("Code")}: ${currentSearchParams.code} ` : ""} | ||||
| ${currentSearchParams.shopName ? `${t("Shop Name")}: ${currentSearchParams.shopName} ` : ""} | ${currentSearchParams.shopName ? `${t("Shop Name")}: ${currentSearchParams.shopName} ` : ""} | ||||
| @@ -595,6 +633,7 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||||
| ? `<p style="font-size:0.95em;color:#666;margin-top:16px;">${t("Merge extra orders into lane batch ticket")}</p>` | ? `<p style="font-size:0.95em;color:#666;margin-top:16px;">${t("Merge extra orders into lane batch ticket")}</p>` | ||||
| : "" | : "" | ||||
| } | } | ||||
| ${replenishmentSectionHtml} | |||||
| </div> | </div> | ||||
| `, | `, | ||||
| showCancelButton: true, | showCancelButton: true, | ||||
| @@ -605,6 +644,9 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||||
| confirmButtonColor: "#8dba00", | confirmButtonColor: "#8dba00", | ||||
| denyButtonColor: "#6366f1", | denyButtonColor: "#6366f1", | ||||
| cancelButtonColor: "#F04438", | cancelButtonColor: "#F04438", | ||||
| didOpen: (popup) => { | |||||
| applyBatchReleaseSwalLayout(popup, { wide: hasReplenishmentPreview }); | |||||
| }, | |||||
| }); | }); | ||||
| if (result.isDismissed) return; | if (result.isDismissed) return; | ||||
| @@ -628,7 +670,12 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||||
| const jobId = startRes?.entity?.jobId; | const jobId = startRes?.entity?.jobId; | ||||
| if (!jobId) { | if (!jobId) { | ||||
| await Swal.fire({ icon: "error", title: t("Error"), text: t("Failed to start batch release") }); | |||||
| await Swal.fire({ | |||||
| icon: "error", | |||||
| title: t("Error"), | |||||
| text: t("Failed to start batch release"), | |||||
| didOpen: (popup) => applyMainContentAreaSwalOffset(popup), | |||||
| }); | |||||
| return; | return; | ||||
| } | } | ||||
| @@ -638,9 +685,10 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||||
| allowOutsideClick: false, | allowOutsideClick: false, | ||||
| allowEscapeKey: false, | allowEscapeKey: false, | ||||
| showConfirmButton: false, | showConfirmButton: false, | ||||
| didOpen: () => { | |||||
| didOpen: (popup) => { | |||||
| applyMainContentAreaSwalOffset(popup); | |||||
| Swal.showLoading(); | Swal.showLoading(); | ||||
| } | |||||
| }, | |||||
| }); | }); | ||||
| const timer = setInterval(async () => { | const timer = setInterval(async () => { | ||||
| @@ -669,7 +717,8 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||||
| title: t("Completed"), | title: t("Completed"), | ||||
| text: t("Batch release completed successfully."), | text: t("Batch release completed successfully."), | ||||
| confirmButtonText: t("Confirm"), | confirmButtonText: t("Confirm"), | ||||
| confirmButtonColor: "#8dba00" | |||||
| confirmButtonColor: "#8dba00", | |||||
| didOpen: (popup) => applyMainContentAreaSwalOffset(popup), | |||||
| }); | }); | ||||
| if (currentSearchParams && Object.keys(currentSearchParams).length > 0) { | if (currentSearchParams && Object.keys(currentSearchParams).length > 0) { | ||||
| @@ -686,7 +735,8 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||||
| icon: "error", | icon: "error", | ||||
| title: t("Error"), | title: t("Error"), | ||||
| text: t("An error occurred during batch release"), | text: t("An error occurred during batch release"), | ||||
| confirmButtonText: t("OK") | |||||
| confirmButtonText: t("OK"), | |||||
| didOpen: (popup) => applyMainContentAreaSwalOffset(popup), | |||||
| }); | }); | ||||
| } | } | ||||
| } catch (error) { | } catch (error) { | ||||
| @@ -695,7 +745,8 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||||
| icon: "error", | icon: "error", | ||||
| title: t("Error"), | title: t("Error"), | ||||
| text: t("Failed to fetch matching records"), | text: t("Failed to fetch matching records"), | ||||
| confirmButtonText: t("OK") | |||||
| confirmButtonText: t("OK"), | |||||
| didOpen: (popup) => applyMainContentAreaSwalOffset(popup), | |||||
| }); | }); | ||||
| } | } | ||||
| }, [t, currentUserId, currentSearchParams, handleSearch, resolveIdsForBatchRelease, activeTab, resolveTabFilter]); | }, [t, currentUserId, currentSearchParams, handleSearch, resolveIdsForBatchRelease, activeTab, resolveTabFilter]); | ||||
| @@ -0,0 +1,276 @@ | |||||
| import { DoReplenishmentRecord, DoSearchAll } from "@/app/api/do/actions"; | |||||
| import { NAVIGATION_CONTENT_WIDTH } from "@/config/uiConfig"; | |||||
| import { TFunction } from "i18next"; | |||||
| function normalizeText(value: string | null | undefined): string { | |||||
| return (value ?? "").trim().toLowerCase(); | |||||
| } | |||||
| function shopTokenFromDoRow(doRow: DoSearchAll): string { | |||||
| const raw = doRow.shopName?.trim() ?? ""; | |||||
| if (!raw) return ""; | |||||
| return normalizeText(raw.split(" - ")[0] || raw); | |||||
| } | |||||
| /** Replenishment must match the same shop + truck lane as a DO in this batch release. */ | |||||
| export function replenishmentMatchesDoRow( | |||||
| record: DoReplenishmentRecord, | |||||
| doRow: DoSearchAll, | |||||
| ): boolean { | |||||
| const doTruck = normalizeText(doRow.truckLanceCode); | |||||
| const recordTruck = normalizeText(record.truckLaneCode); | |||||
| if (doTruck) { | |||||
| if (!recordTruck || recordTruck !== doTruck) return false; | |||||
| } | |||||
| const doShopToken = shopTokenFromDoRow(doRow); | |||||
| if (!doShopToken) return false; | |||||
| const recordShopCode = normalizeText(record.shopCode); | |||||
| const recordShopName = normalizeText(record.shopName); | |||||
| return ( | |||||
| recordShopCode === doShopToken || | |||||
| recordShopName.startsWith(doShopToken) || | |||||
| (recordShopCode.length > 0 && doShopToken.startsWith(recordShopCode)) || | |||||
| (recordShopCode.length > 0 && recordShopCode.startsWith(doShopToken)) | |||||
| ); | |||||
| } | |||||
| export function filterReplenishmentsForBatchDos( | |||||
| records: DoReplenishmentRecord[], | |||||
| dosForRelease: DoSearchAll[], | |||||
| ): DoReplenishmentRecord[] { | |||||
| if (dosForRelease.length === 0) return []; | |||||
| return records.filter((record) => | |||||
| dosForRelease.some((doRow) => replenishmentMatchesDoRow(record, doRow)), | |||||
| ); | |||||
| } | |||||
| /** Narrow API query from selected DOs when search box shop/truck is empty. */ | |||||
| export function deriveReplenishmentFetchParams( | |||||
| dosForRelease: DoSearchAll[], | |||||
| searchShopName: string, | |||||
| searchTruckLaneCode: string, | |||||
| ): { shopName?: string; truckLaneCode?: string } { | |||||
| const shopFromSearch = searchShopName.trim(); | |||||
| const truckFromSearch = searchTruckLaneCode.trim(); | |||||
| if (shopFromSearch || truckFromSearch) { | |||||
| return { | |||||
| shopName: shopFromSearch || undefined, | |||||
| truckLaneCode: truckFromSearch || undefined, | |||||
| }; | |||||
| } | |||||
| const shopTokens = [ | |||||
| ...new Set(dosForRelease.map(shopTokenFromDoRow).filter(Boolean)), | |||||
| ]; | |||||
| const trucks = [ | |||||
| ...new Set( | |||||
| dosForRelease | |||||
| .map((row) => row.truckLanceCode?.trim()) | |||||
| .filter((value): value is string => Boolean(value)), | |||||
| ), | |||||
| ]; | |||||
| if (shopTokens.length === 1 && trucks.length === 1) { | |||||
| return { shopName: shopTokens[0], truckLaneCode: trucks[0] }; | |||||
| } | |||||
| if (shopTokens.length === 1) { | |||||
| return { shopName: shopTokens[0] }; | |||||
| } | |||||
| if (trucks.length === 1) { | |||||
| return { truckLaneCode: trucks[0] }; | |||||
| } | |||||
| return {}; | |||||
| } | |||||
| const BATCH_RELEASE_SWAL_SCROLL_CLASS = "do-batch-release-replenish-scroll"; | |||||
| /** Matches MUI `xl` — permanent nav drawer visible at this width and above. */ | |||||
| export const MUI_XL_BREAKPOINT_PX = 1440; | |||||
| /** Keep full-viewport backdrop; shift popup so it centers over `<main>`, not the sidebar. */ | |||||
| export function applyMainContentAreaSwalOffset(popup: HTMLElement): void { | |||||
| const container = popup.closest(".swal2-container"); | |||||
| if (container instanceof HTMLElement) { | |||||
| container.style.marginLeft = ""; | |||||
| container.style.width = ""; | |||||
| } | |||||
| if (!window.matchMedia(`(min-width: ${MUI_XL_BREAKPOINT_PX}px)`).matches) { | |||||
| popup.style.marginLeft = ""; | |||||
| return; | |||||
| } | |||||
| popup.style.marginLeft = `calc(${NAVIGATION_CONTENT_WIDTH} / 2)`; | |||||
| } | |||||
| function applyCompactSwalIcon(popup: HTMLElement): void { | |||||
| const icon = popup.querySelector(".swal2-icon"); | |||||
| if (icon instanceof HTMLElement) { | |||||
| icon.style.width = "2.5em"; | |||||
| icon.style.height = "2.5em"; | |||||
| icon.style.margin = "0.35em auto 0.25em"; | |||||
| icon.style.fontSize = "0.75em"; | |||||
| } | |||||
| } | |||||
| /** ~110% of prior 920px so 100% browser zoom matches previous balance. */ | |||||
| export const BATCH_RELEASE_SWAL_WIDTH_WITH_REPLENISH = 1024; | |||||
| function escapeHtml(text: string): string { | |||||
| return text | |||||
| .replace(/&/g, "&") | |||||
| .replace(/</g, "<") | |||||
| .replace(/>/g, ">") | |||||
| .replace(/"/g, """); | |||||
| } | |||||
| function shopKey(record: DoReplenishmentRecord): string { | |||||
| return (record.shopCode ?? record.shopName ?? "").trim().toLowerCase(); | |||||
| } | |||||
| function shouldShowShopColumn(records: DoReplenishmentRecord[]): boolean { | |||||
| const keys = new Set(records.map(shopKey).filter(Boolean)); | |||||
| return keys.size > 1; | |||||
| } | |||||
| export function buildBatchReleaseReplenishmentHtml( | |||||
| records: DoReplenishmentRecord[], | |||||
| t: TFunction, | |||||
| ): string { | |||||
| if (records.length === 0) { | |||||
| return `<p style="font-size:0.9em;color:#666;margin-top:16px;">${escapeHtml( | |||||
| t("Batch release no pending replenishment"), | |||||
| )}</p>`; | |||||
| } | |||||
| const showShopColumn = shouldShowShopColumn(records); | |||||
| const thStyle = | |||||
| "padding:7px 10px;text-align:left;font-weight:600;border-bottom:1px solid #ddd;white-space:nowrap;font-size:13px;"; | |||||
| const tdBase = | |||||
| "padding:7px 10px;text-align:left;vertical-align:top;border-top:1px solid #eee;font-size:13px;line-height:1.35;"; | |||||
| const headers: string[] = [t("Replenishment Code")]; | |||||
| if (showShopColumn) headers.push(t("Shop Name")); | |||||
| headers.push(t("Item No."), t("Item Name"), t("Replenish Qty"), t("Source DO")); | |||||
| const headerCells = headers | |||||
| .map((label) => `<th style="${thStyle}">${escapeHtml(label)}</th>`) | |||||
| .join(""); | |||||
| const colgroup = showShopColumn | |||||
| ? `<colgroup> | |||||
| <col style="width:128px" /> | |||||
| <col style="width:148px" /> | |||||
| <col style="width:76px" /> | |||||
| <col /> | |||||
| <col style="width:72px" /> | |||||
| <col style="width:148px" /> | |||||
| </colgroup>` | |||||
| : `<colgroup> | |||||
| <col style="width:132px" /> | |||||
| <col style="width:80px" /> | |||||
| <col /> | |||||
| <col style="width:76px" /> | |||||
| <col style="width:152px" /> | |||||
| </colgroup>`; | |||||
| const bodyRows = records | |||||
| .map((row) => { | |||||
| const qtyLabel = row.shortUom | |||||
| ? `${row.replenishQty} ${row.shortUom}` | |||||
| : String(row.replenishQty); | |||||
| const shopLabel = row.shopName ?? row.shopCode ?? "—"; | |||||
| const itemName = row.itemName ?? "—"; | |||||
| const shopCell = showShopColumn | |||||
| ? `<td style="${tdBase}word-break:break-word;" title="${escapeHtml(shopLabel)}">${escapeHtml(shopLabel)}</td>` | |||||
| : ""; | |||||
| return `<tr> | |||||
| <td style="${tdBase}white-space:nowrap;">${escapeHtml(row.code)}</td> | |||||
| ${shopCell} | |||||
| <td style="${tdBase}white-space:nowrap;">${escapeHtml(row.itemNo ?? "—")}</td> | |||||
| <td style="${tdBase}word-break:break-word;" title="${escapeHtml(itemName)}">${escapeHtml(itemName)}</td> | |||||
| <td style="${tdBase}white-space:nowrap;">${escapeHtml(qtyLabel)}</td> | |||||
| <td style="${tdBase}white-space:nowrap;">${escapeHtml(row.sourceDoCode ?? "—")}</td> | |||||
| </tr>`; | |||||
| }) | |||||
| .join(""); | |||||
| const scrollMaxHeight = records.length <= 4 ? "none" : "200px"; | |||||
| return ` | |||||
| <div class="do-batch-release-replenish-section" style="margin-top:12px;text-align:left;width:100%;box-sizing:border-box;"> | |||||
| <p style="font-weight:600;margin:0 0 6px;text-align:left;font-size:14px;">${escapeHtml( | |||||
| t("Batch release pending replenishment", { count: records.length }), | |||||
| )}</p> | |||||
| <p style="font-size:13px;color:#666;margin:0 0 8px;text-align:left;">${escapeHtml( | |||||
| t("Batch release replenishment info only"), | |||||
| )}</p> | |||||
| <div class="${BATCH_RELEASE_SWAL_SCROLL_CLASS}" style="max-height:${scrollMaxHeight};overflow:${scrollMaxHeight === "none" ? "visible" : "auto"};border:1px solid #ddd;border-radius:4px;width:100%;box-sizing:border-box;"> | |||||
| <table style="width:100%;table-layout:fixed;border-collapse:collapse;"> | |||||
| ${colgroup} | |||||
| <thead> | |||||
| <tr style="background:#f5f5f5;">${headerCells}</tr> | |||||
| </thead> | |||||
| <tbody>${bodyRows}</tbody> | |||||
| </table> | |||||
| </div> | |||||
| </div> | |||||
| `; | |||||
| } | |||||
| /** SweetAlert2 centers html-container by default; fix layout for left-aligned table. */ | |||||
| export function applyBatchReleaseSwalLayout( | |||||
| popup: HTMLElement, | |||||
| options?: { wide?: boolean }, | |||||
| ): void { | |||||
| applyMainContentAreaSwalOffset(popup); | |||||
| applyCompactSwalIcon(popup); | |||||
| const wide = options?.wide ?? false; | |||||
| if (!wide) { | |||||
| return; | |||||
| } | |||||
| popup.style.width = `${BATCH_RELEASE_SWAL_WIDTH_WITH_REPLENISH}px`; | |||||
| popup.style.maxWidth = `calc(100vw - ${NAVIGATION_CONTENT_WIDTH} - 48px)`; | |||||
| popup.style.boxSizing = "border-box"; | |||||
| popup.style.padding = "1.1em 1.35em 1.25em"; | |||||
| const title = popup.querySelector(".swal2-title"); | |||||
| if (title instanceof HTMLElement) { | |||||
| title.style.padding = "0.4em 0 0"; | |||||
| title.style.fontSize = "1.35em"; | |||||
| } | |||||
| const htmlContainer = popup.querySelector(".swal2-html-container"); | |||||
| if (htmlContainer instanceof HTMLElement) { | |||||
| htmlContainer.style.textAlign = "left"; | |||||
| htmlContainer.style.overflow = "visible"; | |||||
| htmlContainer.style.maxHeight = "none"; | |||||
| htmlContainer.style.margin = "0.25em 0 0"; | |||||
| htmlContainer.style.padding = "0"; | |||||
| htmlContainer.style.width = "100%"; | |||||
| htmlContainer.style.boxSizing = "border-box"; | |||||
| } | |||||
| popup.querySelectorAll(".do-batch-release-swal-body, .do-batch-release-replenish-section").forEach((el) => { | |||||
| if (el instanceof HTMLElement) { | |||||
| el.style.width = "100%"; | |||||
| el.style.boxSizing = "border-box"; | |||||
| } | |||||
| }); | |||||
| const scrollBox = popup.querySelector(`.${BATCH_RELEASE_SWAL_SCROLL_CLASS}`); | |||||
| if (scrollBox instanceof HTMLElement) { | |||||
| scrollBox.style.display = "block"; | |||||
| scrollBox.style.width = "100%"; | |||||
| scrollBox.style.boxSizing = "border-box"; | |||||
| } | |||||
| const actions = popup.querySelector(".swal2-actions"); | |||||
| if (actions instanceof HTMLElement) { | |||||
| actions.style.marginTop = "0.85em"; | |||||
| } | |||||
| } | |||||
| @@ -96,6 +96,9 @@ | |||||
| "Failed to submit replenishment": "Failed to submit replenishment", | "Failed to submit replenishment": "Failed to submit replenishment", | ||||
| "Replenishment API not ready": "Replenishment API not ready", | "Replenishment API not ready": "Replenishment API not ready", | ||||
| "Replenishment submitted successfully": "Replenishment submitted successfully", | "Replenishment submitted successfully": "Replenishment submitted successfully", | ||||
| "Batch release pending replenishment": "Pending replenishment ({{count}})", | |||||
| "Batch release no pending replenishment": "No pending replenishment for this search.", | |||||
| "Batch release replenishment info only": "Pending replenishments matching this search. For information only.", | |||||
| "Replenishment Entry": "Replenishment Entry", | "Replenishment Entry": "Replenishment Entry", | ||||
| "Replenishment item code": "Item Code", | "Replenishment item code": "Item Code", | ||||
| "Replenishment Tracking": "Replenishment Tracking", | "Replenishment Tracking": "Replenishment Tracking", | ||||
| @@ -43,6 +43,9 @@ | |||||
| "Failed to submit replenishment": "提交補貨失敗", | "Failed to submit replenishment": "提交補貨失敗", | ||||
| "Replenishment API not ready": "補貨 API 尚未就緒", | "Replenishment API not ready": "補貨 API 尚未就緒", | ||||
| "Replenishment submitted successfully": "補貨已提交", | "Replenishment submitted successfully": "補貨已提交", | ||||
| "Batch release pending replenishment": "待放單補貨({{count}} 筆)", | |||||
| "Batch release no pending replenishment": "此搜尋條件下沒有待放單補貨。", | |||||
| "Batch release replenishment info only": "以下為符合搜尋條件的待放單補貨,僅供查閱。", | |||||
| "Replenishment Entry": "補貨填表", | "Replenishment Entry": "補貨填表", | ||||
| "Replenishment item code": "貨品編號", | "Replenishment item code": "貨品編號", | ||||
| "Replenishment Tracking": "補貨進度追蹤", | "Replenishment Tracking": "補貨進度追蹤", | ||||