Ver a proveniência

dosearch not allow input withour date.

if 4/F truck is not match that day count as truck X
truck x tab have warn mesage
doTruckUpdate
CANCERYS\kw093 há 4 semanas
ascendente
cometimento
f700045a91
5 ficheiros alterados com 299 adições e 47 eliminações
  1. +5
    -0
      src/app/api/do/actions.tsx
  2. +243
    -47
      src/components/DoSearch/DoSearch.tsx
  3. +9
    -0
      src/components/SearchBox/SearchBox.tsx
  4. +21
    -0
      src/i18n/en/do.json
  5. +21
    -0
      src/i18n/zh/do.json

+ 5
- 0
src/app/api/do/actions.tsx Ver ficheiro

@@ -398,6 +398,8 @@ export async function fetchDoSearch(
floor?: string | null,
/** null:不篩;true/false:只顯示加單或非加單 DO */
isExtra?: boolean | null,
/** true:2/F、4/F 分頁排除車線-X */
excludeUnassignedTruck?: boolean,
): Promise<DoSearchLiteResponse> {
// 构建请求体
const requestBody: any = {
@@ -410,6 +412,7 @@ export async function fetchDoSearch(
pageSize: pageSize || 10,
floor: floor && floor !== "All" ? floor : null,
...(isExtra !== undefined && isExtra !== null ? { isExtra } : {}),
...(excludeUnassignedTruck ? { excludeUnassignedTruck: true } : {}),
};

// 如果日期不为空,转换为 LocalDateTime 格式
@@ -654,6 +657,7 @@ export async function fetchAllDoSearch(
truckLanceCode?: string,
floor?: string | null,
isExtra?: boolean | null,
excludeUnassignedTruck?: boolean,
): Promise<DoSearchAll[]> {
// 使用一个很大的 pageSize 来获取所有匹配的记录
const requestBody: any = {
@@ -666,6 +670,7 @@ export async function fetchAllDoSearch(
pageSize: 10000, // 使用一个很大的值来获取所有记录
floor: floor && floor !== "All" ? floor : null,
...(isExtra !== undefined && isExtra !== null ? { isExtra } : {}),
...(excludeUnassignedTruck ? { excludeUnassignedTruck: true } : {}),
};

if (estArrStartDate) {


+ 243
- 47
src/components/DoSearch/DoSearch.tsx Ver ficheiro

@@ -11,6 +11,8 @@ import {
releaseDo,
startBatchReleaseAsync,
getBatchReleaseProgress,
check4FTrucksBatch,
type ProblemDoDto,
} from "@/app/api/do/actions";
import {
startWorkbenchBatchReleaseAsyncV2,
@@ -18,7 +20,7 @@ import {
} from "@/app/api/doworkbench/actions";

import { useRouter } from "next/navigation";
import React, { ForwardedRef, useCallback, useEffect, useMemo, useState } from "react";
import React, { ForwardedRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Criterion } from "../SearchBox";
import { isEmpty, sortBy, uniqBy } from "lodash";
@@ -41,7 +43,7 @@ import {
SubmitHandler,
useForm,
} from "react-hook-form";
import { Box, Button, Paper, Stack, Tab, Tabs, TablePagination, Typography } from "@mui/material";
import { Alert, Box, Button, Paper, Stack, Tab, Tabs, TablePagination, Typography } from "@mui/material";
import StyledDataGrid from "../StyledDataGrid";
import Swal from "sweetalert2";
import { useSession } from "next-auth/react";
@@ -62,7 +64,7 @@ type Props = {
searchQuery?: Record<string, any>;
onDeliveryOrderSearch?: () => void;
};
type SearchBoxInputs = Record<"code" | "status" | "estimatedArrivalDate" | "orderDate" | "supplierName" | "shopName" | "deliveryOrderLines" | "truckLanceCode" | "codeTo" | "statusTo" | "estimatedArrivalDateTo" | "orderDateTo" | "supplierNameTo" | "shopNameTo" | "deliveryOrderLinesTo" | "truckLanceCodeTo", string>;
type SearchBoxInputs = Record<"code" | "status" | "estimatedArrivalDate" | "orderDate" | "supplierName" | "shopName" | "deliveryOrderLines" | "truckLanceCode" | "floor" | "codeTo" | "statusTo" | "estimatedArrivalDateTo" | "orderDateTo" | "supplierNameTo" | "shopNameTo" | "deliveryOrderLinesTo" | "truckLanceCodeTo" | "floorTo", string>;
type SearchParamNames = keyof SearchBoxInputs;
type DoSearchTab = "2F" | "4F" | "TRUCK_X" | "ETRA" | "REPLENISH";
type TabFilter = { floor: "2F" | "4F" | null; isExtra: boolean; forceTruckKeyword?: string };
@@ -76,9 +78,141 @@ type EntryError =
| undefined;
type DoRow = TableRow<Partial<DoResult>, EntryError>;

/** 已填車線但未選預計送貨日:後端會掃全量再篩,需擋下。 */
function isTruckLaneSearchMissingEta(truckLanceCode: string, estimatedArrivalDate: string): boolean {
return truckLanceCode.trim() !== "" && estimatedArrivalDate.trim() === "";
/** 2/F、4/F、車線-X 必須有預計送貨日,否則後端無法判斷當天車線。 */
function deliveryDateRequired(tab: DoSearchTab): boolean {
return tab === "2F" || tab === "4F" || tab === "TRUCK_X";
}

function escapeHtml(value: string): string {
return value
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}

function formatCheckDate(value: unknown): string {
if (Array.isArray(value) && value.length >= 3) {
const year = Number(value[0]);
const month = Number(value[1]);
const day = Number(value[2]);
if (!year || !month || !day) return "";
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
}
if (typeof value === "string" && value.length >= 10) return value.slice(0, 10);
return "";
}

const WEEKDAY_LABEL_KEY = ["Weekday Sun", "Weekday Mon", "Weekday Tue", "Weekday Wed", "Weekday Thu", "Weekday Fri", "Weekday Sat"];

function weekdayLabelKey(isoDate: string): string {
const parsed = new Date(`${isoDate}T00:00:00`);
if (Number.isNaN(parsed.getTime())) return "";
return WEEKDAY_LABEL_KEY[parsed.getDay()] ?? "";
}

const DAY_TRUCK_WARN_LIMIT = 12;

const LANE_DAY_KEYS: Array<[string, string]> = [
["sat", "Weekday Sat"],
["sun", "Weekday Sun"],
["mon", "Weekday Mon"],
["tue", "Weekday Tue"],
["wed", "Weekday Wed"],
["thu", "Weekday Thu"],
["fri", "Weekday Fri"],
];

function shortShopLabel(shopCode: string, shopName: string, fallback: string): string {
const branch = shopName.includes(" - ")
? shopName.split(" - ").slice(1).join(" - ").trim()
: shopName.trim();
const label = [shopCode, branch].filter(Boolean).join(" ");
return label || fallback;
}

function weekdayNamesInLanes(
lanes: string[],
t: (key: string, options?: Record<string, unknown>) => string,
): string[] {
const names: string[] = [];
const blob = lanes.join(" ").toLowerCase();
for (const [token, key] of LANE_DAY_KEYS) {
if (blob.includes(token)) names.push(t(key));
}
return names;
}

function buildDayTruckWarnHtml(
problems: ProblemDoDto[],
skipCount: number,
releaseCount: number,
t: (key: string, options?: Record<string, unknown>) => string,
): { html: string; title: string } {
const groups = new Map<string, { shop: string; date: string; weekday: string; lanes: string[] }>();
for (const problem of problems) {
const date = formatCheckDate(problem.targetDate);
const firstTruck = problem.availableTrucks?.[0];
const shopCode = firstTruck?.shopCode?.trim() || "";
const shopName = firstTruck?.shopName?.trim() || "";
const shop = shortShopLabel(
shopCode,
shopName,
problem.deliveryOrderCode || String(problem.deliveryOrderId),
);
const key = `${shopCode || problem.deliveryOrderCode}|${date}`;
const current = groups.get(key) ?? {
shop,
date,
weekday: weekdayLabelKey(date) ? t(weekdayLabelKey(date)) : "",
lanes: [],
};
for (const truck of problem.availableTrucks ?? []) {
const lane = truck.truckLanceCode?.trim();
if (lane && !current.lanes.includes(lane)) current.lanes.push(lane);
}
groups.set(key, current);
}

const rows = Array.from(groups.values());
const visible = rows.slice(0, DAY_TRUCK_WARN_LIMIT);
const hiddenCount = rows.length - visible.length;
const allSkipped = releaseCount === 0;
const sameDate = visible.every((row) => row.date && row.date === visible[0]?.date);
const sameWeekday = visible.every((row) => row.weekday && row.weekday === visible[0]?.weekday);
const missingDay = sameWeekday && visible[0]?.weekday
? visible[0].weekday
: t("4F day truck warn day fallback");
const reason = sameDate && sameWeekday && visible[0]?.date
? t("4F day truck warn reason", { date: visible[0].date, weekday: visible[0].weekday })
: t("4F day truck warn reason mixed");
const items = visible
.map((row) => {
const dayNames = weekdayNamesInLanes(row.lanes, t);
const prefix = sameDate ? "" : `${row.date} ${row.weekday} `;
const line = dayNames.length > 0
? t("4F day truck warn line", { shop: `${prefix}${row.shop}`, lanes: dayNames.join("、") })
: t("4F day truck warn line none", { shop: `${prefix}${row.shop}` });
return `<li style="margin:0 0 4px;">${escapeHtml(line)}</li>`;
})
.join("");
const more = hiddenCount > 0
? `<p style="margin:6px 0 0;color:#666;">${escapeHtml(t("4F day truck warn more", { count: hiddenCount }))}</p>`
: "";
const action = t(allSkipped ? "4F day truck warn action none" : "4F day truck warn action some", { day: missingDay });
const title = t(allSkipped ? "4F day truck warn title none" : "4F day truck warn title some", {
count: skipCount,
skip: skipCount,
});
return {
title,
html: `
<p style="margin:0;">${escapeHtml(reason)}</p>
<ul style="margin:8px 0 0;padding-left:18px;">${items}</ul>
${more}
<p style="margin:10px 0 0;font-weight:700;">${escapeHtml(action)}</p>
`,
};
}

/** FP-MTMS Version Checklist | Functions Ref. No. 77 | v1.0.0 | 2026-09-08 */
@@ -96,6 +230,8 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
const router = useRouter();
const { data: session } = useSession() as { data: SessionWithTokens | null };
const currentUserId = session?.id ? parseInt(session.id) : undefined;
const batchReleaseLockRef = useRef(false);
const [isBatchReleasing, setIsBatchReleasing] = useState(false);
//console.log("🔍 DoSearch - session:", session);
//console.log("🔍 DoSearch - currentUserId:", currentUserId);
const [searchTimeout, setSearchTimeout] = useState<NodeJS.Timeout | null>(null);
@@ -119,6 +255,7 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
shopName: "",
deliveryOrderLines: "",
truckLanceCode: "",
floor: "All",
codeTo: "",
statusTo: "",
estimatedArrivalDateTo: "",
@@ -127,6 +264,7 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
shopNameTo: "",
deliveryOrderLinesTo: "",
truckLanceCodeTo: "",
floorTo: "",
});
const createClearedSearchParams = useCallback(
(source: SearchBoxInputs): SearchBoxInputs => ({
@@ -138,6 +276,7 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
shopName: "",
deliveryOrderLines: "",
truckLanceCode: "",
floor: "All",
codeTo: "",
statusTo: "",
estimatedArrivalDateTo: source.estimatedArrivalDateTo || "",
@@ -146,12 +285,14 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
shopNameTo: "",
deliveryOrderLinesTo: "",
truckLanceCodeTo: "",
floorTo: "",
}),
[],
);

const [hasSearched, setHasSearched] = useState(false);
const [hasResults, setHasResults] = useState(false);
const [dateFieldError, setDateFieldError] = useState(false);

const {
rowSelectionModel,
@@ -172,6 +313,7 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
currentSearchParams.status,
currentSearchParams.estimatedArrivalDate,
currentSearchParams.truckLanceCode,
currentSearchParams.floor,
]);


@@ -180,11 +322,23 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
{ label: t("Code"), paramName: "code", type: "text" },
{ label: t("Shop Name"), paramName: "shopName", type: "text" },
{ label: t("Truck Lance Code"), paramName: "truckLanceCode", type: "text" },
...(activeTab === "TRUCK_X"
? [{
label: t("Floor"),
paramName: "floor" as const,
type: "select-labelled" as const,
options: [
{ label: "2/F", value: "2F" },
{ label: "4/F", value: "4F" },
],
}]
: []),
{
label: t("Estimated Arrival"),
paramName: "estimatedArrivalDate",
type: "date",
preFilledValue: currentSearchParams.estimatedArrivalDate || "",
errorText: dateFieldError ? t("Estimated arrival required") : undefined,
},
{
label: t("Status"),
@@ -197,7 +351,7 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
]
}
],
[t, currentSearchParams.estimatedArrivalDate],
[t, activeTab, currentSearchParams.estimatedArrivalDate, dateFieldError],
);

const onReset = useCallback(async () => {
@@ -206,6 +360,7 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
setTotalCount(0);
setHasSearched(false);
setHasResults(false);
setDateFieldError(false);
resetSelection();
setPagingController({ pageNum: 1, pageSize: 10 });
}
@@ -351,20 +506,19 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
const tabFilter = resolveTabFilter(effectiveTab);
const tabTruckKeyword = tabFilter.forceTruckKeyword ?? "";
const effectiveTruckLanceCode = tabTruckKeyword || query.truckLanceCode || "";
const shouldValidateTruckLane = effectiveTab !== "TRUCK_X";
const searchFloor =
effectiveTab === "TRUCK_X"
? query.floor === "2F" || query.floor === "4F"
? query.floor
: null
: tabFilter.floor;
const excludeUnassigned = effectiveTab === "2F" || effectiveTab === "4F";

if (
shouldValidateTruckLane &&
isTruckLaneSearchMissingEta(effectiveTruckLanceCode, query.estimatedArrivalDate ?? "")
) {
await Swal.fire({
icon: "warning",
title: t("Truck lane search requires date title"),
text: t("Truck lane search requires date message"),
confirmButtonText: t("Confirm"),
});
if (deliveryDateRequired(effectiveTab) && !(query.estimatedArrivalDate ?? "").trim()) {
setDateFieldError(true);
return false;
}
setDateFieldError(false);

let estArrStartDate = query.estimatedArrivalDate;
const time = "T00:00:00";
@@ -385,8 +539,9 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
pageNum,
pageSize,
effectiveTruckLanceCode,
tabFilter.floor,
searchFloor,
tabFilter.isExtra,
excludeUnassigned,
);

setSearchAllDos(response.records);
@@ -509,6 +664,10 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
);

const handleBatchRelease = useCallback(async (isWorkbench: boolean) => {
if (batchReleaseLockRef.current) return;
batchReleaseLockRef.current = true;
setIsBatchReleasing(true);
try {
try {
if (!currentUserId) {
await Swal.fire({
@@ -523,22 +682,18 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
const tabFilter = resolveTabFilter(activeTab);
const tabTruckKeyword = tabFilter.forceTruckKeyword ?? "";
const effectiveTruckLanceCode = tabTruckKeyword || currentSearchParams.truckLanceCode || "";
const shouldValidateTruckLane = activeTab !== "TRUCK_X";
if (
shouldValidateTruckLane &&
isTruckLaneSearchMissingEta(
effectiveTruckLanceCode,
currentSearchParams.estimatedArrivalDate ?? "",
)
) {
await Swal.fire({
icon: "warning",
title: t("Truck lane search requires date title"),
text: t("Truck lane search requires date message"),
confirmButtonText: t("Confirm"),
});
const searchFloor =
activeTab === "TRUCK_X"
? currentSearchParams.floor === "2F" || currentSearchParams.floor === "4F"
? currentSearchParams.floor
: null
: tabFilter.floor;
const excludeUnassigned = activeTab === "2F" || activeTab === "4F";
if (deliveryDateRequired(activeTab) && !(currentSearchParams.estimatedArrivalDate ?? "").trim()) {
setDateFieldError(true);
return;
}
setDateFieldError(false);
// 根据当前搜索条件获取所有匹配的记录(不分页)
let estArrStartDate = currentSearchParams.estimatedArrivalDate;
const time = "T00:00:00";
@@ -574,8 +729,9 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
status,
estArrStartDate,
effectiveTruckLanceCode,
tabFilter.floor,
searchFloor,
tabFilter.isExtra,
excludeUnassigned,
);

Swal.close();
@@ -606,7 +762,23 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
return;
}

const dosForRelease = allMatchingDos.filter((row) => idsToRelease.includes(row.id));
let dayTruckProblems: ProblemDoDto[] = [];
if (activeTab === "2F" || activeTab === "4F") {
try {
const truckCheck = await check4FTrucksBatch(idsToRelease);
dayTruckProblems = truckCheck?.problems ?? [];
} catch (checkError) {
console.error("4F day truck check failed:", checkError);
}
}
const skipIdSet = new Set(dayTruckProblems.map((problem) => problem.deliveryOrderId));
const releaseIds = idsToRelease.filter((id) => !skipIdSet.has(id));
const allDayTrucksSkipped = dayTruckProblems.length > 0 && releaseIds.length === 0;
const dayTruckWarn = dayTruckProblems.length > 0
? buildDayTruckWarnHtml(dayTruckProblems, skipIdSet.size, releaseIds.length, t)
: null;

const dosForRelease = allMatchingDos.filter((row) => releaseIds.includes(row.id));
const replenishmentFetchParams = deriveReplenishmentFetchParams(
dosForRelease,
currentSearchParams.shopName || "",
@@ -624,21 +796,32 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
);

const hasReplenishmentPreview = pendingReplenishments.length > 0;
const widenReleaseDialog = hasReplenishmentPreview || dayTruckWarn != null;
const confirmText = allDayTrucksSkipped
? t("4F day truck warn ack")
: dayTruckWarn != null && !showMergeExtraOption
? t("4F day truck warn release rest")
: showMergeExtraOption
? t("Confirm merge release")
: t("Confirm");

const result = await Swal.fire({
icon: "question",
title: t("Batch Release"),
width: hasReplenishmentPreview ? BATCH_RELEASE_SWAL_WIDTH_WITH_REPLENISH : undefined,
icon: dayTruckWarn != null ? "warning" : "question",
title: dayTruckWarn?.title ?? t("Batch Release"),
width: widenReleaseDialog ? BATCH_RELEASE_SWAL_WIDTH_WITH_REPLENISH : undefined,
html: `
<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>
${
dayTruckWarn == null
? `<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.shopName ? `${t("Shop Name")}: ${currentSearchParams.shopName} ` : ""}
${currentSearchParams.estimatedArrivalDate ? `${t("Estimated Arrival")}: ${currentSearchParams.estimatedArrivalDate} ` : ""}
${status ? `${t("Status")}: ${t(status)} ` : ""}
</p>
</p>`
: dayTruckWarn.html
}
${
showMergeExtraOption
? `<p style="font-size:0.95em;color:#666;margin-top:16px;">${t("Merge extra orders into lane batch ticket")}</p>`
@@ -647,21 +830,22 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
${replenishmentSectionHtml}
</div>
`,
showCancelButton: true,
showDenyButton: showMergeExtraOption,
confirmButtonText: showMergeExtraOption ? t("Confirm merge release") : t("Confirm"),
showCancelButton: !allDayTrucksSkipped,
showDenyButton: showMergeExtraOption && !allDayTrucksSkipped,
confirmButtonText: confirmText,
denyButtonText: t("Confirm release without merge"),
cancelButtonText: t("Cancel"),
confirmButtonColor: "#8dba00",
denyButtonColor: "#6366f1",
cancelButtonColor: "#F04438",
didOpen: (popup) => {
applyBatchReleaseSwalLayout(popup, { wide: hasReplenishmentPreview });
applyBatchReleaseSwalLayout(popup, { wide: widenReleaseDialog });
},
});

if (result.isDismissed) return;
if (!result.isConfirmed && !result.isDenied) return;
if (releaseIds.length === 0) return;

const mergeExtraIntoLaneTicket = showMergeExtraOption ? result.isConfirmed : true;

@@ -669,13 +853,13 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
let startRes ;
if(isWorkbench){
startRes = await startWorkbenchBatchReleaseAsyncV2({
ids: idsToRelease,
ids: releaseIds,
userId: currentUserId,
mergeExtraIntoLaneTicket,
});
}
else{
startRes = await startBatchReleaseAsync({ ids: idsToRelease, userId: currentUserId });
startRes = await startBatchReleaseAsync({ ids: releaseIds, userId: currentUserId });
}
//await startBatchReleaseAsync({ ids: idsToRelease, userId: currentUserId ?? 1 });
const jobId = startRes?.entity?.jobId;
@@ -760,6 +944,10 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
didOpen: (popup) => applyMainContentAreaSwalOffset(popup),
});
}
} finally {
batchReleaseLockRef.current = false;
setIsBatchReleasing(false);
}
}, [t, currentUserId, currentSearchParams, handleSearch, resolveIdsForBatchRelease, activeTab, resolveTabFilter]);

const handleTabChange = useCallback(
@@ -776,6 +964,7 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
setTotalCount(0);
setHasSearched(false);
setHasResults(false);
setDateFieldError(false);
},
[
activeTab,
@@ -813,6 +1002,7 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
<Button
name="batch_release"
variant="contained"
disabled={isBatchReleasing}
onClick={() => handleBatchRelease(true)}
>
{t("Workbench Batch Release")}
@@ -833,6 +1023,12 @@ const DoSearch: React.FC<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
onReset={onReset}
/>

{activeTab === "TRUCK_X" && hasSearched && totalCount > 0 && (
<Alert severity="warning">
{t("Truck X tab warn title")}
</Alert>
)}

<Paper variant="outlined" sx={{ overflow: "hidden" }}>
<StyledDataGrid
rows={searchAllDos}


+ 9
- 0
src/components/SearchBox/SearchBox.tsx Ver ficheiro

@@ -49,6 +49,8 @@ interface BaseCriterion<T extends string> {
handleSelectionChange?: (selectedOptions: T[]) => void;
/** Hidden until the user clicks more criteria. */
advanced?: boolean;
/** Shown under the field in error color when set. */
errorText?: string;
}

interface OptionWithLabel<T extends string> {
@@ -574,6 +576,13 @@ function SearchBox<T extends string>({
? dayjs(inputs[c.paramName])
: null
}
slotProps={{
textField: {
fullWidth: true,
error: Boolean(c.errorText),
helperText: c.errorText || undefined,
},
}}
/>
</FormControl>
</Box>


+ 21
- 0
src/i18n/en/do.json Ver ficheiro

@@ -7,6 +7,25 @@
"Available Trucks": "Available Trucks",
"Back": "Back",
"Batch Release": "Batch Release",
"4F day truck warn title none": "These {{count}} orders will not be released",
"4F day truck warn title some": "{{skip}} will be skipped. The rest will be released",
"4F day truck warn reason": "{{date}} ({{weekday}}) has no matching 4F truck.",
"4F day truck warn reason mixed": "No 4F truck for that delivery day.",
"4F day truck warn line": "{{shop}}: only {{lanes}} trucks now",
"4F day truck warn line none": "{{shop}}: no 4F truck",
"4F day truck warn action none": "Add the {{day}} truck in Lane Shop Management, then release again.",
"4F day truck warn action some": "Add the {{day}} truck in Lane Shop Management, then release these shops separately.",
"4F day truck warn day fallback": "matching weekday",
"4F day truck warn ack": "OK",
"4F day truck warn release rest": "Skip and release the rest",
"4F day truck warn more": "{{count}} more not shown",
"Weekday Sun": "Sunday",
"Weekday Mon": "Monday",
"Weekday Tue": "Tuesday",
"Weekday Wed": "Wednesday",
"Weekday Thu": "Thursday",
"Weekday Fri": "Friday",
"Weekday Sat": "Saturday",
"Batch release completed successfully.": "Batch release completed successfully.",
"Cancel": "Cancel",
"Code": "Code",
@@ -27,6 +46,7 @@
"Edit": "Edit",
"Edit Delivery Order Detail": "Edit Delivery Order Detail",
"Estimated Arrival": "Estimated Arrival",
"Estimated arrival required": "Please select an estimated delivery date",
"Estimated Arrival Date": "Estimated Arrival Date",
"Estimated Arrival From": "Estimated Arrival From",
"Estimated Arrival To": "Estimated Arrival To",
@@ -164,6 +184,7 @@
"Truck Lance Code": "Truck Lance Code",
"Truck Lane": "Truck Lane",
"Truck X": "Truck X",
"Truck X tab warn title": "Shop truck lanes are not set correctly. Please add them in Lane Shop Management.",
"Truck lane search requires date message": "Truck lane search requires date message",
"Truck lane search requires date title": "Truck lane search requires date title",
"User session not found": "User session not found. Please login again.",


+ 21
- 0
src/i18n/zh/do.json Ver ficheiro

@@ -7,6 +7,7 @@
"Delivery Order Status": "送貨訂單狀態",
"Order Date": "訂單日期",
"Estimated Arrival": "預計送貨日期",
"Estimated arrival required": "請選擇預計送貨日期",
"Estimated Arrival From": "預計送貨日期",
"Estimated Arrival To": "預計送貨日期至",
"Status": "來貨狀態",
@@ -95,6 +96,7 @@
"Rows per page": "每頁數量",
"OK": "確認",
"Truck X": "車線-X",
"Truck X tab warn title": "店舖車線未設定正確。請到「車線店鋪管理」補上車線。",
"Order Date From": "訂單日期",
"Workbench Batch Release": "批量放單",
"Delivery Order Code": "送貨訂單編號",
@@ -161,6 +163,25 @@
"Release": "放單",
"Back": "返回",
"Batch Release": "批量放單",
"4F day truck warn title none": "這 {{count}} 張單不會放",
"4F day truck warn title some": "{{skip}} 張會跳過,其餘正常放單",
"4F day truck warn reason": "{{date}}({{weekday}})沒有對應的 4F 車線的店鋪。",
"4F day truck warn reason mixed": "沒有對到當天的 4F 車。",
"4F day truck warn line": "{{shop}}:目前只有{{lanes}}車線",
"4F day truck warn line none": "{{shop}}:沒有 4F 車",
"4F day truck warn action none": "請到「車線店鋪管理」補上{{day}}車線,再重新放單。",
"4F day truck warn action some": "請到「車線店鋪管理」補上{{day}}車線,再重新放這幾張單。",
"4F day truck warn day fallback": "對應星期的",
"4F day truck warn ack": "知道了",
"4F day truck warn release rest": "跳過並放其餘",
"4F day truck warn more": "其餘 {{count}} 間未列出",
"Weekday Sun": "星期日",
"Weekday Mon": "星期一",
"Weekday Tue": "星期二",
"Weekday Wed": "星期三",
"Weekday Thu": "星期四",
"Weekday Fri": "星期五",
"Weekday Sat": "星期六",
"Batch release completed successfully.": "已完成批量放單",
"Edit Delivery Order Detail": "編輯送貨訂單詳情",
"DO released successfully! Pick orders created.": "送貨訂單放單成功!提料單已建立。",


Carregando…
Cancelar
Guardar