diff --git a/scripts/update-chart-i18n.js b/scripts/update-chart-i18n.js new file mode 100644 index 0000000..05fa7c0 --- /dev/null +++ b/scripts/update-chart-i18n.js @@ -0,0 +1,267 @@ +const fs = require('fs'); +const path = require('path'); + +const en = { + "pageTitle_delivery": "Delivery & Dispatch", + "pageTitle_jobOrder": "Job Orders", + "pageTitle_forecast": "Forecast & Planning", + "pageTitle_warehouse": "Inventory & Warehouse", + "pageTitle_equipmentBoard": "Equipment Usage Board", + "pageTitle_processBoard": "Process Real-time Board", + "pageTitle_jobOrderBoard": "Job Order Real-time Board", + "pageTitle_purchase": "Purchase", + + "all": "All", + "noData": "No data", + "exportExcel": "Export Excel", + "show": "Show", + "laneX": "Lane X", + "today": "Today", + "yesterday": "Yesterday", + "selectDate": "Select Date", + "refresh": "Refresh", + "otherBoards": "Other Boards", + "autoRefresh": "Auto Refresh", + "on": "On", + "off": "Off", + "intervalSeconds": "Interval (sec)", + "minutes": "minutes", + "minutesWithVal": "{{val}} min", + "minuteAbbr": "min", + + "delivery_staffPerfDateError": "Staff performance start date cannot be later than end date", + "delivery_ordersByDate": "Delivery Orders by Date", + "delivery_ordersByDate_export": "Delivery_Orders_By_Date", + "delivery_orderCount": "Order Count", + "delivery_topItemsByCount": "Top Items by Delivery Count", + "delivery_item": "Item", + "delivery_itemPlaceholder": "Leave empty for all", + "delivery_staffPerformanceTitle": "Staff Delivery Performance (Daily Pick Count & Duration)", + "delivery_startDate": "Start Date", + "delivery_endDate": "End Date", + "delivery_store": "Store", + "delivery_staff": "Staff", + "delivery_staffPlaceholder": "Leave empty for all", + "delivery_staffPerfCaption": "Per-person pick count & total duration for period", + "delivery_colStaff": "Staff", + "delivery_colPickCount": "Pick Count", + "delivery_colTotalMin": "Total Min", + "delivery_colAvgMin": "Avg Min/Order", + "delivery_dailyByStaff": "Daily by Staff", + "delivery_noDataDesc": "No delivery data available for the selected period.", + + "jo_byStatus": "Job Orders by Status", + "jo_byStatus_export": "Job_Orders_By_Status", + "jo_datePlanStart": "Date (Plan Start)", + "jo_createdVsCompleted": "Job Orders Created vs Completed by Date", + "jo_createdVsCompleted_export": "Job_Orders_Created_vs_Completed", + "jo_detailSection": "Job Order Material / Process / Equipment", + "jo_materialPendingPicked": "Material Pending / Picked (by Plan Date)", + "jo_materialPendingPicked_export": "Material_Pending_vs_Picked", + "jo_processPendingCompleted": "Process Pending / Completed (by Plan Date)", + "jo_processPendingCompleted_export": "Process_Pending_vs_Completed", + "jo_equipmentWorkingWorked": "Equipment In Use / Used (by Job Order)", + "jo_equipmentWorkingWorked_export": "Equipment_In_Use_vs_Used", + + "board_jobOrderLive": "Job Order Live Board", + "board_equipmentUsage": "Equipment Usage Board", + "board_processLive": "Process Live Board", + "board_jobOrderChart": "Job Order Chart", + + "forecast_plannedOutputByDate": "Planned Daily Output by Item (Forecast)", + "forecast_plannedOutputByDate_export": "Planned_Daily_Output_By_Item", + "forecast_itemCode": "Item Code", + "forecast_noScheduleData": "No scheduling data for this date range.", + "forecast_productionSchedule": "Production Schedule by Date (Estimated Output)", + "forecast_productionSchedule_export": "Production_Schedule_By_Date", + + "warehouse_stockTxnByDate": "Stock Transactions by Date (In / Out / Total)", + "warehouse_stockTxnByDate_export": "Stock_Transactions_By_Date", + "warehouse_stockInOutByDate": "Stock In vs Out by Date", + "warehouse_stockInOutByDate_export": "Stock_In_vs_Out", + "warehouse_balanceTrend": "Stock Balance Trend", + "warehouse_balanceTrend_export": "Stock_Balance_Trend", + "warehouse_consumptionTrend": "Monthly Consumption Trend (Outbound)", + "warehouse_consumptionTrend_export": "Monthly_Consumption_Trend", + "warehouse_qty": "Qty", + "warehouse_optional": "Optional", + "warehouse_sumAll": "Sum all if empty", + "warehouse_addItem": "Add item to split", + "warehouse_exportFail": "Master export failed", + + "equipment_status": "Status", + "equipment_equipment": "Equipment", + "equipment_jobOrder": "Job Order", + "equipment_process": "Process", + "equipment_planStart": "Plan Start", + "equipment_startTime": "Start Time", + "equipment_endTime": "End Time", + "equipment_operator": "Operator", + "equipment_boardTitle": "Equipment Usage Board", + "equipment_infoDescription1": "Shows equipment status in real-time: working, idle, or under maintenance.", + "equipment_infoDescription2": "Each equipment card displays the current job order and process.", + "equipment_infoDescription3": "Equipment with unclosed hours or missing time entries will be flagged.", + "equipment_searchAndList": "Search & List", + "equipment_notToday": "Not Today", + "equipment_unclosedHours": "Equipment hours not closed", + "equipment_missingHours": "Missing equipment hours", + "equipment_completed": "Completed", + + "process_notStarted": "Not Started", + "process_inProgress": "In Progress", + "process_completed": "Completed", + "process_nonToday": "Not Today", + + "dateRange_lastDays": "Last {{d}} days", + + "series_inbound": "Inbound", + "series_outbound": "Outbound", + "series_total": "Total", + "series_balance": "Balance", + "series_consumption": "Consumption", + "series_created": "Created", + "series_completed": "Completed", + "series_month": "Month", + + "requestFailed": "Request failed" +}; + +const zh = { + "pageTitle_delivery": "發貨與配送", + "pageTitle_jobOrder": "工單", + "pageTitle_forecast": "預測與計劃", + "pageTitle_warehouse": "庫存與倉儲", + "pageTitle_equipmentBoard": "設備使用看板", + "pageTitle_processBoard": "工序即時看板", + "pageTitle_jobOrderBoard": "工單即時看板", + "pageTitle_purchase": "採購", + + "all": "全部", + "noData": "無數據", + "exportExcel": "匯出 Excel", + "show": "顯示", + "laneX": "車線-X", + "today": "今日", + "yesterday": "昨日", + "selectDate": "選擇日期", + "refresh": "重新整理", + "otherBoards": "其他看板", + "autoRefresh": "自動重新整理", + "on": "開啟", + "off": "關閉", + "intervalSeconds": "間隔(秒)", + "minutes": "分鐘", + "minutesWithVal": "{{val}} 分鐘", + "minuteAbbr": "分", + + "delivery_staffPerfDateError": "員工發貨績效的起始日期不能晚於結束日期", + "delivery_ordersByDate": "按日期發貨單數量", + "delivery_ordersByDate_export": "發貨單數量_按日期", + "delivery_orderCount": "單數", + "delivery_topItemsByCount": "發貨數量排行(按物料)", + "delivery_item": "物料", + "delivery_itemPlaceholder": "不選則全部", + "delivery_staffPerformanceTitle": "員工發貨績效(每日揀貨數量與耗時)", + "delivery_startDate": "開始日期", + "delivery_endDate": "結束日期", + "delivery_store": "倉別", + "delivery_staff": "員工", + "delivery_staffPlaceholder": "不選則全部", + "delivery_staffPerfCaption": "週期內每人揀單數及總耗時(首揀至完成)", + "delivery_colStaff": "員工", + "delivery_colPickCount": "揀單數", + "delivery_colTotalMin": "總分鐘", + "delivery_colAvgMin": "平均分鐘/單", + "delivery_dailyByStaff": "每日按員工單數", + "delivery_noDataDesc": "所選期間內暫無發貨記錄,請調整日期範圍後再試。", + + "jo_byStatus": "工單按狀態", + "jo_byStatus_export": "工單_按狀態", + "jo_datePlanStart": "日期(計劃開始)", + "jo_createdVsCompleted": "工單創建與完成按日期", + "jo_createdVsCompleted_export": "工單_創建與完成_按日期", + "jo_detailSection": "工單物料/工序/設備", + "jo_materialPendingPicked": "物料待領/已揀(按工單計劃日)", + "jo_materialPendingPicked_export": "物料_待領_已揀", + "jo_processPendingCompleted": "工序待完成/已完成(按工單計劃日)", + "jo_processPendingCompleted_export": "工序_待完成_已完成", + "jo_equipmentWorkingWorked": "設備使用中/已使用(按工單)", + "jo_equipmentWorkingWorked_export": "設備_使用中_已使用", + + "board_jobOrderLive": "工單即時看板", + "board_equipmentUsage": "設備使用看板", + "board_processLive": "工序即時看板", + "board_jobOrderChart": "工單圖表", + + "forecast_plannedOutputByDate": "按物料計劃日產量(預測)", + "forecast_plannedOutputByDate_export": "計劃日產量_按物料", + "forecast_itemCode": "物料編碼", + "forecast_noScheduleData": "此日期範圍內尚無排程資料。", + "forecast_productionSchedule": "按日期生產排程(預估產量)", + "forecast_productionSchedule_export": "生產排程_按日期", + + "warehouse_stockTxnByDate": "按日期庫存流水(入/出/合計)", + "warehouse_stockTxnByDate_export": "庫存流水_按日期", + "warehouse_stockInOutByDate": "按日期入庫與出庫", + "warehouse_stockInOutByDate_export": "入庫_出庫_按日期", + "warehouse_balanceTrend": "庫存餘額趨勢", + "warehouse_balanceTrend_export": "庫存餘額趨勢", + "warehouse_consumptionTrend": "按月考勤消耗趨勢(出庫量)", + "warehouse_consumptionTrend_export": "月考勤消耗趨勢", + "warehouse_qty": "數量", + "warehouse_optional": "可選", + "warehouse_sumAll": "不選則全部合計", + "warehouse_addItem": "新增物料以分項顯示", + "warehouse_exportFail": "總表匯出失敗", + + "equipment_status": "狀態", + "equipment_equipment": "設備", + "equipment_jobOrder": "工單", + "equipment_process": "工序", + "equipment_planStart": "工單計劃開始", + "equipment_startTime": "開工時間", + "equipment_endTime": "完工時間", + "equipment_operator": "操作員", + "equipment_boardTitle": "設備使用看板", + "equipment_infoDescription1": "即時顯示設備狀態:使用中、閒置或維護中。", + "equipment_infoDescription2": "每張設備卡片顯示當前工單和工序。", + "equipment_infoDescription3": "設備工時未結案或未填寫將被標記。", + "equipment_searchAndList": "查詢與列表", + "equipment_notToday": "非今日", + "equipment_unclosedHours": "設備工時未結案", + "equipment_missingHours": "未填設備工時", + "equipment_completed": "已完工", + + "process_notStarted": "未開工", + "process_inProgress": "進行中", + "process_completed": "已完工", + "process_nonToday": "非今日", + + "dateRange_lastDays": "最近 {{d}} 天", + + "series_inbound": "入庫", + "series_outbound": "出庫", + "series_total": "合計", + "series_balance": "餘額", + "series_consumption": "消耗", + "series_created": "新建", + "series_completed": "完成", + "series_month": "月份", + + "requestFailed": "請求失敗" +}; + +const i18nDir = path.join(__dirname, '..', 'src', 'i18n'); +const enPath = path.join(i18nDir, 'en', 'chart.json'); +const zhPath = path.join(i18nDir, 'zh', 'chart.json'); + +// Sort keys alphabetically +const sortKeys = (obj) => { + const sorted = {}; + Object.keys(obj).sort().forEach(k => { sorted[k] = obj[k]; }); + return sorted; +}; + +fs.writeFileSync(enPath, JSON.stringify(sortKeys(en), null, 2) + '\n'); +fs.writeFileSync(zhPath, JSON.stringify(sortKeys(zh), null, 2) + '\n'); +console.log('Updated chart.json with', Object.keys(en).length, 'keys for each language'); diff --git a/src/app/(main)/bag/page.tsx b/src/app/(main)/bag/page.tsx index 8e859ef..5e8939f 100644 --- a/src/app/(main)/bag/page.tsx +++ b/src/app/(main)/bag/page.tsx @@ -9,7 +9,7 @@ export const metadata: Metadata = { } const bagPage: React.FC = async () => { - const { t } = await getServerI18n("jo"); + const { t } = await getServerI18n("bagUsage"); return ( <> @@ -23,7 +23,7 @@ const bagPage: React.FC = async () => { {t("Bag Usage")} - + }> diff --git a/src/app/(main)/bagPrint/page.tsx b/src/app/(main)/bagPrint/page.tsx index e935cee..0afeae7 100644 --- a/src/app/(main)/bagPrint/page.tsx +++ b/src/app/(main)/bagPrint/page.tsx @@ -1,23 +1,21 @@ +import { I18nProvider } from "@/i18n"; +import { Metadata } from "next"; import BagPrintSearch from "@/components/BagPrint/BagPrintSearch"; import { Stack, Typography } from "@mui/material"; -import { Metadata } from "next"; -import React from "react"; export const metadata: Metadata = { title: "打袋機", }; -const BagPrintPage: React.FC = () => { +export default async function BagPrintPage() { return ( - <> + 打袋機 - + ); -}; - -export default BagPrintPage; +} diff --git a/src/app/(main)/chart/layout.tsx b/src/app/(main)/chart/layout.tsx index 0c4a8ef..b2c183b 100644 --- a/src/app/(main)/chart/layout.tsx +++ b/src/app/(main)/chart/layout.tsx @@ -1,24 +1,13 @@ -import { Metadata } from "next"; -import { getServerSession } from "next-auth"; -import { redirect } from "next/navigation"; -import { authOptions } from "@/config/authConfig"; -import { AUTH } from "@/authorities"; +import { I18nProvider } from "@/i18n"; -export const metadata: Metadata = { - title: "圖表報告", -}; - -export default async function ChartLayout({ +export default function ChartLayout({ children, }: { children: React.ReactNode; }) { - const session = await getServerSession(authOptions); - const abilities = session?.user?.abilities ?? []; - const canViewCharts = - abilities.includes(AUTH.TESTING) || abilities.includes(AUTH.ADMIN); - if (!canViewCharts) { - redirect("/dashboard"); - } - return <>{children}; + return ( + + {children} + + ); } diff --git a/src/app/(main)/dashboard/page.tsx b/src/app/(main)/dashboard/page.tsx index d965477..796234b 100644 --- a/src/app/(main)/dashboard/page.tsx +++ b/src/app/(main)/dashboard/page.tsx @@ -18,7 +18,7 @@ const Dashboard: React.FC = async ({ searchParams }) => { fetchEscalationLogsByUser() return ( - + }> diff --git a/src/app/(main)/do copy 2/edit/page.tsx b/src/app/(main)/do copy 2/edit/page.tsx index a3200a5..b114acc 100644 --- a/src/app/(main)/do copy 2/edit/page.tsx +++ b/src/app/(main)/do copy 2/edit/page.tsx @@ -24,7 +24,7 @@ const DoEdit: React.FC = async ({ searchParams }) => { return ( <> - + }> diff --git a/src/app/(main)/do copy 2/page.tsx b/src/app/(main)/do copy 2/page.tsx index 5489385..235f4e0 100644 --- a/src/app/(main)/do copy 2/page.tsx +++ b/src/app/(main)/do copy 2/page.tsx @@ -23,7 +23,7 @@ const Page: React.FC = async () => { /doworkbench

- + }> diff --git a/src/app/(main)/do copy/edit/page.tsx b/src/app/(main)/do copy/edit/page.tsx index a3200a5..b114acc 100644 --- a/src/app/(main)/do copy/edit/page.tsx +++ b/src/app/(main)/do copy/edit/page.tsx @@ -24,7 +24,7 @@ const DoEdit: React.FC = async ({ searchParams }) => { return ( <> - + }> diff --git a/src/app/(main)/do copy/page.tsx b/src/app/(main)/do copy/page.tsx index e1ef75d..f2004ba 100644 --- a/src/app/(main)/do copy/page.tsx +++ b/src/app/(main)/do copy/page.tsx @@ -17,7 +17,7 @@ const DeliveryOrder: React.FC = async () => { return ( <> - + }> diff --git a/src/app/(main)/do/edit/page.tsx b/src/app/(main)/do/edit/page.tsx index a3200a5..b114acc 100644 --- a/src/app/(main)/do/edit/page.tsx +++ b/src/app/(main)/do/edit/page.tsx @@ -24,7 +24,7 @@ const DoEdit: React.FC = async ({ searchParams }) => { return ( <> - + }> diff --git a/src/app/(main)/do/page.tsx b/src/app/(main)/do/page.tsx index e1ef75d..f2004ba 100644 --- a/src/app/(main)/do/page.tsx +++ b/src/app/(main)/do/page.tsx @@ -17,7 +17,7 @@ const DeliveryOrder: React.FC = async () => { return ( <> - + }> diff --git a/src/app/(main)/doworkbench/edit/page.tsx b/src/app/(main)/doworkbench/edit/page.tsx index 308d185..3ac873e 100644 --- a/src/app/(main)/doworkbench/edit/page.tsx +++ b/src/app/(main)/doworkbench/edit/page.tsx @@ -15,7 +15,7 @@ export const metadata: Metadata = { type Props = SearchParams; const Page: React.FC = async ({ searchParams }) => { - const { t } = await getServerI18n("do"); + const { t } = await getServerI18n("doWorkbench"); const id = searchParams["id"]; if (!id || isArray(id) || !isFinite(parseInt(id))) { @@ -34,7 +34,7 @@ const Page: React.FC = async ({ searchParams }) => { {t("DO Workbench Search", { defaultValue: "DO Workbench Search" })}

- + }> diff --git a/src/app/(main)/doworkbench/page.tsx b/src/app/(main)/doworkbench/page.tsx index 6322a89..381f79f 100644 --- a/src/app/(main)/doworkbench/page.tsx +++ b/src/app/(main)/doworkbench/page.tsx @@ -9,13 +9,13 @@ export const metadata: Metadata = { }; const DoWorkbenchPage: React.FC = async () => { - const { t } = await getServerI18n("do"); + const { t } = await getServerI18n("doWorkbench"); const printerCombo = await fetchPrinterCombo(); return ( <> - + diff --git a/src/app/(main)/doworkbenchsearch/page.tsx b/src/app/(main)/doworkbenchsearch/page.tsx index 602cc26..ee44fcf 100644 --- a/src/app/(main)/doworkbenchsearch/page.tsx +++ b/src/app/(main)/doworkbenchsearch/page.tsx @@ -19,7 +19,7 @@ const DoWorkbenchSearchPage: React.FC = async () => { title={t("DO Workbench Search", { defaultValue: "DO Workbench Search" })} className="mb-4" /> - + }> diff --git a/src/app/(main)/finishedGood/detail/page.tsx b/src/app/(main)/finishedGood/detail/page.tsx index a456798..ec11371 100644 --- a/src/app/(main)/finishedGood/detail/page.tsx +++ b/src/app/(main)/finishedGood/detail/page.tsx @@ -12,13 +12,13 @@ export const metadata: Metadata = { type Props = {} & SearchParams; const PickOrder: React.FC = async ({ searchParams }) => { - const { t } = await getServerI18n("pickOrder"); + const { t } = await getServerI18n("finishedGood"); return ( <> - + }> diff --git a/src/app/(main)/finishedGood/management/page.tsx b/src/app/(main)/finishedGood/management/page.tsx index 1594c8f..e754248 100644 --- a/src/app/(main)/finishedGood/management/page.tsx +++ b/src/app/(main)/finishedGood/management/page.tsx @@ -18,7 +18,7 @@ const Page = async () => { redirect("/dashboard"); } return ( - + }> diff --git a/src/app/(main)/finishedGood/page.tsx b/src/app/(main)/finishedGood/page.tsx index 44ca00b..eb0cbd9 100644 --- a/src/app/(main)/finishedGood/page.tsx +++ b/src/app/(main)/finishedGood/page.tsx @@ -11,13 +11,13 @@ export const metadata: Metadata = { }; const PickOrder: React.FC = async () => { - const { t } = await getServerI18n("pickOrder"); + const { t } = await getServerI18n("finishedGood"); //PreloadPickOrder(); return ( <> - + }> diff --git a/src/app/(main)/inventory/page.tsx b/src/app/(main)/inventory/page.tsx index 3b202f0..651aed9 100644 --- a/src/app/(main)/inventory/page.tsx +++ b/src/app/(main)/inventory/page.tsx @@ -14,7 +14,7 @@ export const metadata: Metadata = { }; const Inventory: React.FC = async () => { - const { t } = await getServerI18n("inventory", "common"); + const { t } = await getServerI18n("inventory"); preloadInventory(); @@ -30,7 +30,7 @@ const Inventory: React.FC = async () => { {t("Inventory")} - + }> diff --git a/src/app/(main)/jo/edit/page.tsx b/src/app/(main)/jo/edit/page.tsx index 4a6b8ed..3c9f0c4 100644 --- a/src/app/(main)/jo/edit/page.tsx +++ b/src/app/(main)/jo/edit/page.tsx @@ -39,7 +39,7 @@ const JoEdit: React.FC = async ({ searchParams }) => { return ( <> - + }> diff --git a/src/app/(main)/jo/page.tsx b/src/app/(main)/jo/page.tsx index 7cd53e4..295b60b 100644 --- a/src/app/(main)/jo/page.tsx +++ b/src/app/(main)/jo/page.tsx @@ -32,7 +32,7 @@ const Jo: React.FC = async () => { return ( <> - + }> { return ( <> - + }> diff --git a/src/app/(main)/jodetail/edit/page.tsx b/src/app/(main)/jodetail/edit/page.tsx index 43a8027..82c1fe4 100644 --- a/src/app/(main)/jodetail/edit/page.tsx +++ b/src/app/(main)/jodetail/edit/page.tsx @@ -38,7 +38,7 @@ const JodetailEdit: React.FC = async ({ searchParams }) => { return ( <> - + }> diff --git a/src/app/(main)/jodetail/page.tsx b/src/app/(main)/jodetail/page.tsx index 7769148..cc6af2a 100644 --- a/src/app/(main)/jodetail/page.tsx +++ b/src/app/(main)/jodetail/page.tsx @@ -18,7 +18,7 @@ const Jodetail: React.FC = async () => { return ( <> - + }> diff --git a/src/app/(main)/laserPrint/page.tsx b/src/app/(main)/laserPrint/page.tsx index 081900e..aa01a1d 100644 --- a/src/app/(main)/laserPrint/page.tsx +++ b/src/app/(main)/laserPrint/page.tsx @@ -1,23 +1,21 @@ +import { I18nProvider } from "@/i18n"; +import { Metadata } from "next"; import LaserPrintSearch from "@/components/LaserPrint/LaserPrintSearch"; import { Stack, Typography } from "@mui/material"; -import { Metadata } from "next"; -import React from "react"; export const metadata: Metadata = { title: "檸檬機(激光機)", }; -const LaserPrintPage: React.FC = () => { +export default async function LaserPrintPage() { return ( - <> + 檸檬機(激光機) - + ); -}; - -export default LaserPrintPage; +} diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index cc020d8..3f2ddad 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -45,7 +45,7 @@ export default async function MainLayout({ /> } mainContent={ - + {children} } diff --git a/src/app/(main)/m18Syn/layout.tsx b/src/app/(main)/m18Syn/layout.tsx index ad756a7..ecbed38 100644 --- a/src/app/(main)/m18Syn/layout.tsx +++ b/src/app/(main)/m18Syn/layout.tsx @@ -1,22 +1,13 @@ -import { Metadata } from "next"; -import { getServerSession } from "next-auth"; -import { redirect } from "next/navigation"; -import { authOptions } from "@/config/authConfig"; -import { AUTH } from "@/authorities"; +import { I18nProvider } from "@/i18n"; -export const metadata: Metadata = { - title: "M18 Sync", -}; - -export default async function M18SynLayout({ +export default function M18SyncLayout({ children, }: { children: React.ReactNode; }) { - const session = await getServerSession(authOptions); - const abilities = session?.user?.abilities ?? []; - if (!abilities.includes(AUTH.ADMIN)) { - redirect("/dashboard"); - } - return <>{children}; + return ( + + {children} + + ); } diff --git a/src/app/(main)/pickOrder/detail/page.tsx b/src/app/(main)/pickOrder/detail/page.tsx index d40809a..7f69b70 100644 --- a/src/app/(main)/pickOrder/detail/page.tsx +++ b/src/app/(main)/pickOrder/detail/page.tsx @@ -18,7 +18,7 @@ const PickOrder: React.FC = async ({ searchParams }) => { return ( <> - + }> diff --git a/src/app/(main)/pickOrder/page.tsx b/src/app/(main)/pickOrder/page.tsx index 31219a2..6f367f9 100644 --- a/src/app/(main)/pickOrder/page.tsx +++ b/src/app/(main)/pickOrder/page.tsx @@ -17,7 +17,7 @@ const PickOrder: React.FC = async () => { return ( <> - + }> diff --git a/src/app/(main)/po/edit/page.tsx b/src/app/(main)/po/edit/page.tsx index d2fcbfd..e7f0667 100644 --- a/src/app/(main)/po/edit/page.tsx +++ b/src/app/(main)/po/edit/page.tsx @@ -31,7 +31,7 @@ const PoEdit: React.FC = async ({ searchParams }) => { return ( <> {/* {t("Create Material")} */} - + }> diff --git a/src/app/(main)/po/page.tsx b/src/app/(main)/po/page.tsx index 96263ab..e2fbdba 100644 --- a/src/app/(main)/po/page.tsx +++ b/src/app/(main)/po/page.tsx @@ -17,7 +17,7 @@ const PurchaseOrder: React.FC = async () => { // preloadClaims(); return ( <> - + - + diff --git a/src/app/(main)/production/page.tsx b/src/app/(main)/production/page.tsx index d8253ac..5708110 100644 --- a/src/app/(main)/production/page.tsx +++ b/src/app/(main)/production/page.tsx @@ -15,7 +15,7 @@ export const metadata: Metadata = { }; const production: React.FC = async () => { - const { t } = await getServerI18n("common"); + const { t } = await getServerI18n("production", "common"); const printerCombo = await fetchPrinterCombo(); return ( <> @@ -38,7 +38,7 @@ const production: React.FC = async () => { {t("Create Process")} */} - + {/* Use new component */} diff --git a/src/app/(main)/productionProcess/page.tsx b/src/app/(main)/productionProcess/page.tsx index 9d1ee22..94dbf7f 100644 --- a/src/app/(main)/productionProcess/page.tsx +++ b/src/app/(main)/productionProcess/page.tsx @@ -13,7 +13,7 @@ export const metadata: Metadata = { }; const productionProcess: React.FC = async () => { - const { t } = await getServerI18n("common"); + const { t } = await getServerI18n("productionProcess", "navigation", "common"); const printerCombo = await fetchPrinterCombo(); return ( <> @@ -36,7 +36,7 @@ const productionProcess: React.FC = async () => { {t("Create Process")} */} - + }> diff --git a/src/app/(main)/projects/create/page.tsx b/src/app/(main)/projects/create/page.tsx index 3af4630..9ceee68 100644 --- a/src/app/(main)/projects/create/page.tsx +++ b/src/app/(main)/projects/create/page.tsx @@ -11,12 +11,12 @@ export const metadata: Metadata = { }; const Projects: React.FC = async () => { - const { t } = await getServerI18n("projects"); + const { t } = await getServerI18n("project"); return ( <> {t("Create Project")} - + diff --git a/src/app/(main)/projects/page.tsx b/src/app/(main)/projects/page.tsx index a117fa2..0b9121a 100644 --- a/src/app/(main)/projects/page.tsx +++ b/src/app/(main)/projects/page.tsx @@ -20,7 +20,7 @@ export const metadata: Metadata = { }; const Projects: React.FC = async () => { - const { t } = await getServerI18n("projects"); + const { t } = await getServerI18n("project"); preloadProjects(); return ( @@ -43,7 +43,7 @@ const Projects: React.FC = async () => { {t("Create Project")} - + }> diff --git a/src/app/(main)/ps/layout.tsx b/src/app/(main)/ps/layout.tsx new file mode 100644 index 0000000..a68270b --- /dev/null +++ b/src/app/(main)/ps/layout.tsx @@ -0,0 +1,13 @@ +import { I18nProvider } from "@/i18n"; + +export default function PsLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/src/app/(main)/ps/page.tsx b/src/app/(main)/ps/page.tsx index a8f872d..fb4b5b0 100644 --- a/src/app/(main)/ps/page.tsx +++ b/src/app/(main)/ps/page.tsx @@ -619,7 +619,7 @@ export default function ProductionSchedulePage() { return (
*/} - + }> diff --git a/src/app/(main)/scheduling/rough/page.tsx b/src/app/(main)/scheduling/rough/page.tsx index cc4e9f8..23dfbf0 100644 --- a/src/app/(main)/scheduling/rough/page.tsx +++ b/src/app/(main)/scheduling/rough/page.tsx @@ -47,7 +47,7 @@ const roughScheduling: React.FC = async () => { {t("Test Rough Scheduling")} */} - + }> diff --git a/src/app/(main)/settings/bomWeighting/page.tsx b/src/app/(main)/settings/bomWeighting/page.tsx index 551dcd2..3aa3d39 100644 --- a/src/app/(main)/settings/bomWeighting/page.tsx +++ b/src/app/(main)/settings/bomWeighting/page.tsx @@ -9,13 +9,13 @@ export const metadata: Metadata = { }; const BomWeightingScorePage: React.FC = async () => { - const { t } = await getServerI18n("common"); + const { t } = await getServerI18n("bomWeighting"); const bomWeightingScores = await fetchBomWeightingScores(); return ( <> - + diff --git a/src/app/(main)/settings/clientMonitor/page.tsx b/src/app/(main)/settings/clientMonitor/page.tsx index d7db777..66c082c 100644 --- a/src/app/(main)/settings/clientMonitor/page.tsx +++ b/src/app/(main)/settings/clientMonitor/page.tsx @@ -1,3 +1,4 @@ +import { I18nProvider } from "@/i18n"; import ClientMonitorPage from "@/components/ClientMonitor/ClientMonitorPage"; import { isMonitoringEnabled } from "@/config/monitoring"; import { Metadata } from "next"; @@ -11,5 +12,9 @@ export default function ClientMonitorRoutePage() { if (!isMonitoringEnabled) { redirect("/settings/user"); } - return ; + return ( + + + + ); } diff --git a/src/app/(main)/settings/deliveryOrderFloor/page.tsx b/src/app/(main)/settings/deliveryOrderFloor/page.tsx index bcbbb91..88701b1 100644 --- a/src/app/(main)/settings/deliveryOrderFloor/page.tsx +++ b/src/app/(main)/settings/deliveryOrderFloor/page.tsx @@ -8,10 +8,10 @@ export const metadata: Metadata = { }; export default async function DeliveryOrderFloorPage() { - const { t } = await getServerI18n("deliveryOrderFloor", "common"); + const { t } = await getServerI18n("deliveryOrderFloor"); return ( - + {t("title")} diff --git a/src/app/(main)/settings/equipment/EquipmentTabs.tsx b/src/app/(main)/settings/equipment/EquipmentTabs.tsx index d4e6a5b..519d9d0 100644 --- a/src/app/(main)/settings/equipment/EquipmentTabs.tsx +++ b/src/app/(main)/settings/equipment/EquipmentTabs.tsx @@ -13,7 +13,7 @@ type EquipmentTabsProps = { const EquipmentTabs: React.FC = ({ onTabChange }) => { const router = useRouter(); const searchParams = useSearchParams(); - const { t } = useTranslation("common"); + const { t } = useTranslation(["equipment", "common"]); const tabFromUrl = searchParams.get("tab"); const initialTabIndex = tabFromUrl ? parseInt(tabFromUrl, 10) : 0; diff --git a/src/app/(main)/settings/equipment/MaintenanceEdit/page.tsx b/src/app/(main)/settings/equipment/MaintenanceEdit/page.tsx index 65c233f..128811e 100644 --- a/src/app/(main)/settings/equipment/MaintenanceEdit/page.tsx +++ b/src/app/(main)/settings/equipment/MaintenanceEdit/page.tsx @@ -9,8 +9,8 @@ import UpdateMaintenanceForm from "@/components/UpdateMaintenance/UpdateMaintena type Props = {} & SearchParams; const MaintenanceEditPage: React.FC = async ({ searchParams }) => { - const type = "common"; - const { t } = await getServerI18n(type); + const type = "equipment"; + const { t } = await getServerI18n(type, "navigation", "common"); const id = isString(searchParams["id"]) ? parseInt(searchParams["id"]) : undefined; @@ -20,7 +20,7 @@ const MaintenanceEditPage: React.FC = async ({ searchParams }) => { return ( <> {t("Update Equipment Maintenance and Repair")} - + diff --git a/src/app/(main)/settings/equipment/create/page.tsx b/src/app/(main)/settings/equipment/create/page.tsx index ae48446..47c7979 100644 --- a/src/app/(main)/settings/equipment/create/page.tsx +++ b/src/app/(main)/settings/equipment/create/page.tsx @@ -9,11 +9,11 @@ type Props = {} & SearchParams; const materialSetting: React.FC = async ({ searchParams }) => { // const type = TypeEnum.PRODUCT; - const { t } = await getServerI18n("common"); + const { t } = await getServerI18n("equipment", "navigation", "common"); return ( <> {/* {t("Create Material")} */} - + diff --git a/src/app/(main)/settings/equipment/edit/page.tsx b/src/app/(main)/settings/equipment/edit/page.tsx index 41e401c..39bb0bc 100644 --- a/src/app/(main)/settings/equipment/edit/page.tsx +++ b/src/app/(main)/settings/equipment/edit/page.tsx @@ -9,8 +9,8 @@ import { notFound } from "next/navigation"; type Props = {} & SearchParams; const productSetting: React.FC = async ({ searchParams }) => { - const type = "common"; - const { t } = await getServerI18n(type); + const type = "equipment"; + const { t } = await getServerI18n(type, "navigation", "common"); const id = isString(searchParams["id"]) ? parseInt(searchParams["id"]) : undefined; @@ -20,7 +20,7 @@ const productSetting: React.FC = async ({ searchParams }) => { return ( <> {/* {t("Create Material")} */} - + diff --git a/src/app/(main)/settings/equipment/page.tsx b/src/app/(main)/settings/equipment/page.tsx index 3ef292d..29511f2 100644 --- a/src/app/(main)/settings/equipment/page.tsx +++ b/src/app/(main)/settings/equipment/page.tsx @@ -19,8 +19,8 @@ export const metadata: Metadata = { }; const productSetting: React.FC = async () => { - const type = "common"; - const { t } = await getServerI18n(type); + const type = "equipment"; + const { t } = await getServerI18n(type, "navigation", "common"); return ( <> @@ -35,7 +35,7 @@ const productSetting: React.FC = async () => { }> - + diff --git a/src/app/(main)/settings/equipmentType/create/page.tsx b/src/app/(main)/settings/equipmentType/create/page.tsx index e996ab1..2f16ee6 100644 --- a/src/app/(main)/settings/equipmentType/create/page.tsx +++ b/src/app/(main)/settings/equipmentType/create/page.tsx @@ -9,11 +9,11 @@ type Props = {} & SearchParams; const materialSetting: React.FC = async ({ searchParams }) => { // const type = TypeEnum.PRODUCT; - const { t } = await getServerI18n("common"); + const { t } = await getServerI18n("equipment", "navigation", "common"); return ( <> {/* {t("Create Material")} */} - + diff --git a/src/app/(main)/settings/equipmentType/edit/page.tsx b/src/app/(main)/settings/equipmentType/edit/page.tsx index b9dbc90..22c1af6 100644 --- a/src/app/(main)/settings/equipmentType/edit/page.tsx +++ b/src/app/(main)/settings/equipmentType/edit/page.tsx @@ -9,8 +9,8 @@ import { notFound } from "next/navigation"; type Props = {} & SearchParams; const productSetting: React.FC = async ({ searchParams }) => { - const type = "common"; - const { t } = await getServerI18n(type); + const type = "equipment"; + const { t } = await getServerI18n(type, "navigation", "common"); const id = isString(searchParams["id"]) ? parseInt(searchParams["id"]) : undefined; @@ -20,7 +20,7 @@ const productSetting: React.FC = async ({ searchParams }) => { return ( <> {/* {t("Create Material")} */} - + diff --git a/src/app/(main)/settings/equipmentType/page.tsx b/src/app/(main)/settings/equipmentType/page.tsx index 63803dc..c5b18f3 100644 --- a/src/app/(main)/settings/equipmentType/page.tsx +++ b/src/app/(main)/settings/equipmentType/page.tsx @@ -15,8 +15,8 @@ export const metadata: Metadata = { }; const productSetting: React.FC = async () => { - const type = "common"; - const { t } = await getServerI18n(type); + const type = "equipment"; + const { t } = await getServerI18n(type, "navigation", "common"); const equipmentTypes = await fetchAllEquipmentTypes(); // preloadClaims(); @@ -41,7 +41,7 @@ const productSetting: React.FC = async () => { */} }> - + diff --git a/src/app/(main)/settings/importBom/MaintenanceEdit/page.tsx b/src/app/(main)/settings/importBom/MaintenanceEdit/page.tsx index 65c233f..d44533a 100644 --- a/src/app/(main)/settings/importBom/MaintenanceEdit/page.tsx +++ b/src/app/(main)/settings/importBom/MaintenanceEdit/page.tsx @@ -9,8 +9,8 @@ import UpdateMaintenanceForm from "@/components/UpdateMaintenance/UpdateMaintena type Props = {} & SearchParams; const MaintenanceEditPage: React.FC = async ({ searchParams }) => { - const type = "common"; - const { t } = await getServerI18n(type); + const type = "importBom"; + const { t } = await getServerI18n(type, "navigation", "common"); const id = isString(searchParams["id"]) ? parseInt(searchParams["id"]) : undefined; @@ -20,10 +20,10 @@ const MaintenanceEditPage: React.FC = async ({ searchParams }) => { return ( <> {t("Update Equipment Maintenance and Repair")} - + ); }; -export default MaintenanceEditPage; \ No newline at end of file +export default MaintenanceEditPage; diff --git a/src/app/(main)/settings/importBom/create/page.tsx b/src/app/(main)/settings/importBom/create/page.tsx index ae48446..01a439d 100644 --- a/src/app/(main)/settings/importBom/create/page.tsx +++ b/src/app/(main)/settings/importBom/create/page.tsx @@ -13,7 +13,7 @@ const materialSetting: React.FC = async ({ searchParams }) => { return ( <> {/* {t("Create Material")} */} - + diff --git a/src/app/(main)/settings/importBom/edit/page.tsx b/src/app/(main)/settings/importBom/edit/page.tsx index 41e401c..1bd7012 100644 --- a/src/app/(main)/settings/importBom/edit/page.tsx +++ b/src/app/(main)/settings/importBom/edit/page.tsx @@ -1,16 +1,14 @@ import { SearchParams } from "@/app/utils/fetchUtil"; -import { TypeEnum } from "@/app/utils/typeEnum"; import CreateEquipmentType from "@/components/CreateEquipment"; import { I18nProvider, getServerI18n } from "@/i18n"; -import { Typography } from "@mui/material"; import isString from "lodash/isString"; import { notFound } from "next/navigation"; type Props = {} & SearchParams; const productSetting: React.FC = async ({ searchParams }) => { - const type = "common"; - const { t } = await getServerI18n(type); + const type = "importBom"; + await getServerI18n(type, "navigation", "common"); const id = isString(searchParams["id"]) ? parseInt(searchParams["id"]) : undefined; @@ -19,8 +17,7 @@ const productSetting: React.FC = async ({ searchParams }) => { } return ( <> - {/* {t("Create Material")} */} - + diff --git a/src/app/(main)/settings/importBom/page.tsx b/src/app/(main)/settings/importBom/page.tsx index 7285cd7..26f4b21 100644 --- a/src/app/(main)/settings/importBom/page.tsx +++ b/src/app/(main)/settings/importBom/page.tsx @@ -21,7 +21,7 @@ export default async function ImportBomPage() { Import BOM - + diff --git a/src/app/(main)/settings/importExcel/page.tsx b/src/app/(main)/settings/importExcel/page.tsx index e83b3b6..010c672 100644 --- a/src/app/(main)/settings/importExcel/page.tsx +++ b/src/app/(main)/settings/importExcel/page.tsx @@ -1,34 +1,34 @@ -import { Metadata } from "next"; -import { getServerI18n } from "@/i18n"; +import { I18nProvider, getServerI18n } from "@/i18n"; import Stack from "@mui/material/Stack"; import Typography from "@mui/material/Typography"; +import { Metadata } from "next"; import { Suspense } from "react"; import ExcelFileImport from "@/components/ExcelFileImport"; export const metadata: Metadata = { - title: "Excel File Import", + title: "Excel File Import", }; const ImportExcel: React.FC = async () => { - const { t } = await getServerI18n("common"); + const { t } = await getServerI18n("importExcel", "navigation", "common"); - return ( - <> - - - {t("Excel File Import")} - - - - - - - ) + return ( + + + + {t("title")} + + + + + + + ); }; export default ImportExcel; diff --git a/src/app/(main)/settings/itemPrice/page.tsx b/src/app/(main)/settings/itemPrice/page.tsx index d6d3cbc..9b386b3 100644 --- a/src/app/(main)/settings/itemPrice/page.tsx +++ b/src/app/(main)/settings/itemPrice/page.tsx @@ -9,13 +9,13 @@ export const metadata: Metadata = { }; const ItemPriceSetting: React.FC = async () => { - const { t } = await getServerI18n("inventory", "common"); + const { t } = await getServerI18n("itemPrice", "importExcel"); return ( <> - + - + }> diff --git a/src/app/(main)/settings/items/create/page.tsx b/src/app/(main)/settings/items/create/page.tsx index 5b1e314..463db53 100644 --- a/src/app/(main)/settings/items/create/page.tsx +++ b/src/app/(main)/settings/items/create/page.tsx @@ -13,7 +13,7 @@ const materialSetting: React.FC = async ({ searchParams }) => { return ( <> {/* {t("Create Material")} */} - + diff --git a/src/app/(main)/settings/items/edit/page.tsx b/src/app/(main)/settings/items/edit/page.tsx index ee32d7f..c1bdbfb 100644 --- a/src/app/(main)/settings/items/edit/page.tsx +++ b/src/app/(main)/settings/items/edit/page.tsx @@ -24,7 +24,7 @@ const productSetting: React.FC = async ({ searchParams }) => { return ( <> {/* {t("Create Material")} */} - + diff --git a/src/app/(main)/settings/items/page.tsx b/src/app/(main)/settings/items/page.tsx index 97ea0a8..79ea2b6 100644 --- a/src/app/(main)/settings/items/page.tsx +++ b/src/app/(main)/settings/items/page.tsx @@ -16,7 +16,7 @@ export const metadata: Metadata = { const productSetting: React.FC = async () => { const project = TypeEnum.PRODUCT; - const { t } = await getServerI18n("project"); + const { t } = await getServerI18n("items"); // preloadClaims(); return ( @@ -40,7 +40,7 @@ const productSetting: React.FC = async () => { */} - + }> diff --git a/src/app/(main)/settings/m18ImportTesting/page.tsx b/src/app/(main)/settings/m18ImportTesting/page.tsx index 3aa4a35..e376c29 100644 --- a/src/app/(main)/settings/m18ImportTesting/page.tsx +++ b/src/app/(main)/settings/m18ImportTesting/page.tsx @@ -21,7 +21,7 @@ const M18ImportTestingPage: React.FC = async () => { rowGap={2} > }> - + diff --git a/src/app/(main)/settings/mail/page.tsx b/src/app/(main)/settings/mail/page.tsx index 6bbb298..1dd8b96 100644 --- a/src/app/(main)/settings/mail/page.tsx +++ b/src/app/(main)/settings/mail/page.tsx @@ -29,7 +29,7 @@ const Customer: React.FC = async () => { {t("Mail")} - + }> diff --git a/src/app/(main)/settings/masterDataIssues/page.tsx b/src/app/(main)/settings/masterDataIssues/page.tsx index ebd698f..7b4f680 100644 --- a/src/app/(main)/settings/masterDataIssues/page.tsx +++ b/src/app/(main)/settings/masterDataIssues/page.tsx @@ -13,7 +13,7 @@ const MasterDataIssuesPage: React.FC = async () => { return ( <> - + diff --git a/src/app/(main)/settings/printer/create/page.tsx b/src/app/(main)/settings/printer/create/page.tsx index 8a5e509..ba8ef78 100644 --- a/src/app/(main)/settings/printer/create/page.tsx +++ b/src/app/(main)/settings/printer/create/page.tsx @@ -4,12 +4,12 @@ import { Suspense } from "react"; import CreatePrinter from "@/components/CreatePrinter"; const CreatePrinterPage: React.FC = async () => { - const { t } = await getServerI18n("common"); + const { t } = await getServerI18n("printer"); return ( <> {t("Create Printer") || "新增列印機"} - + }> diff --git a/src/app/(main)/settings/printer/edit/page.tsx b/src/app/(main)/settings/printer/edit/page.tsx index c2c02c9..f4cdc0c 100644 --- a/src/app/(main)/settings/printer/edit/page.tsx +++ b/src/app/(main)/settings/printer/edit/page.tsx @@ -10,7 +10,7 @@ import { fetchPrinterDetails } from "@/app/api/settings/printer/actions"; type Props = {} & SearchParams; const EditPrinterPage: React.FC = async ({ searchParams }) => { - const { t } = await getServerI18n("common"); + const { t } = await getServerI18n("printer"); const id = isString(searchParams["id"]) ? parseInt(searchParams["id"]) : undefined; @@ -26,7 +26,7 @@ const EditPrinterPage: React.FC = async ({ searchParams }) => { return ( <> {t("Edit")} {t("Printer")} - + Loading...
}>
diff --git a/src/app/(main)/settings/printer/page.tsx b/src/app/(main)/settings/printer/page.tsx index ee662a2..ee7b9ee 100644 --- a/src/app/(main)/settings/printer/page.tsx +++ b/src/app/(main)/settings/printer/page.tsx @@ -13,7 +13,7 @@ export const metadata: Metadata = { }; const Printer: React.FC = async () => { - const { t } = await getServerI18n("common"); + const { t } = await getServerI18n("printer"); return ( <> @@ -35,7 +35,7 @@ const Printer: React.FC = async () => { {t("Create Printer") || "新增列印機"} - + }> diff --git a/src/app/(main)/settings/qcCategory/edit/page.tsx b/src/app/(main)/settings/qcCategory/edit/page.tsx index cadcd7a..3bf58e4 100644 --- a/src/app/(main)/settings/qcCategory/edit/page.tsx +++ b/src/app/(main)/settings/qcCategory/edit/page.tsx @@ -43,7 +43,7 @@ const qcCategory: React.FC = async ({ searchParams }) => { {t("Edit Qc Category")} - + diff --git a/src/app/(main)/settings/qcCategory/page.tsx b/src/app/(main)/settings/qcCategory/page.tsx index 63f57ee..e27d2cd 100644 --- a/src/app/(main)/settings/qcCategory/page.tsx +++ b/src/app/(main)/settings/qcCategory/page.tsx @@ -36,9 +36,11 @@ const qcCategory: React.FC = async () => { {t("Create Qc Category")} - }> - - + + }> + + + ); }; diff --git a/src/app/(main)/settings/qcItem copy/create/page.tsx b/src/app/(main)/settings/qcItem copy/create/page.tsx index 1cb5c8a..b1a1bc0 100644 --- a/src/app/(main)/settings/qcItem copy/create/page.tsx +++ b/src/app/(main)/settings/qcItem copy/create/page.tsx @@ -16,7 +16,7 @@ const qcItem: React.FC = async () => { {t("Create Qc Item")} - + diff --git a/src/app/(main)/settings/qcItem copy/edit/page.tsx b/src/app/(main)/settings/qcItem copy/edit/page.tsx index 0e433fb..2529168 100644 --- a/src/app/(main)/settings/qcItem copy/edit/page.tsx +++ b/src/app/(main)/settings/qcItem copy/edit/page.tsx @@ -43,7 +43,7 @@ const qcItem: React.FC = async ({ searchParams }) => { {t("Edit Qc Item")} - + diff --git a/src/app/(main)/settings/qcItem copy/page.tsx b/src/app/(main)/settings/qcItem copy/page.tsx index f1b4e71..bc078ef 100644 --- a/src/app/(main)/settings/qcItem copy/page.tsx +++ b/src/app/(main)/settings/qcItem copy/page.tsx @@ -37,7 +37,7 @@ const qcItem: React.FC = async () => { }> - + diff --git a/src/app/(main)/settings/qcItem/create/page.tsx b/src/app/(main)/settings/qcItem/create/page.tsx index 1cb5c8a..b1a1bc0 100644 --- a/src/app/(main)/settings/qcItem/create/page.tsx +++ b/src/app/(main)/settings/qcItem/create/page.tsx @@ -16,7 +16,7 @@ const qcItem: React.FC = async () => { {t("Create Qc Item")} - + diff --git a/src/app/(main)/settings/qcItem/edit/page.tsx b/src/app/(main)/settings/qcItem/edit/page.tsx index 0e433fb..2529168 100644 --- a/src/app/(main)/settings/qcItem/edit/page.tsx +++ b/src/app/(main)/settings/qcItem/edit/page.tsx @@ -43,7 +43,7 @@ const qcItem: React.FC = async ({ searchParams }) => { {t("Edit Qc Item")} - + diff --git a/src/app/(main)/settings/qcItem/page.tsx b/src/app/(main)/settings/qcItem/page.tsx index f1b4e71..bc078ef 100644 --- a/src/app/(main)/settings/qcItem/page.tsx +++ b/src/app/(main)/settings/qcItem/page.tsx @@ -37,7 +37,7 @@ const qcItem: React.FC = async () => { }> - + diff --git a/src/app/(main)/settings/qcItemAll/page.tsx b/src/app/(main)/settings/qcItemAll/page.tsx index cb257ac..78cc17c 100644 --- a/src/app/(main)/settings/qcItemAll/page.tsx +++ b/src/app/(main)/settings/qcItemAll/page.tsx @@ -30,7 +30,7 @@ const qcItemAll: React.FC = async () => { Loading...}> - + } tab1Content={} diff --git a/src/app/(main)/settings/qrCodeHandle/page.tsx b/src/app/(main)/settings/qrCodeHandle/page.tsx index d363561..07bfea2 100644 --- a/src/app/(main)/settings/qrCodeHandle/page.tsx +++ b/src/app/(main)/settings/qrCodeHandle/page.tsx @@ -12,7 +12,7 @@ import Box from "@mui/material/Box"; export const metadata: Metadata = { title: "QR Code Handle" }; const QrCodeHandlePage: React.FC = async () => { - const { t } = await getServerI18n("common"); + const { t } = await getServerI18n("qrCodeHandle"); return ( @@ -20,25 +20,25 @@ const QrCodeHandlePage: React.FC = async () => { {t("QR Code Handle")} - + }> - + } equipmentTabContent={ }> - + } warehouseTabContent={ }> - + diff --git a/src/app/(main)/settings/rss/page.tsx b/src/app/(main)/settings/rss/page.tsx index 3c9e853..388d579 100644 --- a/src/app/(main)/settings/rss/page.tsx +++ b/src/app/(main)/settings/rss/page.tsx @@ -18,8 +18,7 @@ export const metadata: Metadata = { const roughScheduleSetting: React.FC = async () => { //const project = TypeEnum.PRODUCT - const project = "common" - const { t } = await getServerI18n(project); + const { t } = await getServerI18n("demandForecast", "navigation", "common"); // preloadClaims(); return ( @@ -43,7 +42,7 @@ const roughScheduleSetting: React.FC = async () => { */} }> - + diff --git a/src/app/(main)/settings/shop/board/page.tsx b/src/app/(main)/settings/shop/board/page.tsx index bee001e..2e47690 100644 --- a/src/app/(main)/settings/shop/board/page.tsx +++ b/src/app/(main)/settings/shop/board/page.tsx @@ -5,7 +5,7 @@ import GeneralLoading from "@/components/General/GeneralLoading"; import RouteBoard from "@/components/Shop/RouteBoard"; export default async function ShopRouteBoardPage() { - await getServerI18n("shop", "common", "routeboard"); + await getServerI18n("shop", "navigation"); return ( - + }> diff --git a/src/app/(main)/settings/shop/detail/page.tsx b/src/app/(main)/settings/shop/detail/page.tsx index 02c2334..7a22513 100644 --- a/src/app/(main)/settings/shop/detail/page.tsx +++ b/src/app/(main)/settings/shop/detail/page.tsx @@ -4,9 +4,9 @@ import { I18nProvider, getServerI18n } from "@/i18n"; import GeneralLoading from "@/components/General/GeneralLoading"; export default async function ShopDetailPage() { - const { t } = await getServerI18n("shop", "common"); + await getServerI18n("shop"); return ( - + }> diff --git a/src/app/(main)/settings/shop/page.tsx b/src/app/(main)/settings/shop/page.tsx index c4e8175..50901ac 100644 --- a/src/app/(main)/settings/shop/page.tsx +++ b/src/app/(main)/settings/shop/page.tsx @@ -8,9 +8,9 @@ import { notFound } from "next/navigation"; export default async function ShopPage() { - const { t } = await getServerI18n("shop", "common"); + await getServerI18n("shop"); return ( - + }> diff --git a/src/app/(main)/settings/shop/truckdetail/page.tsx b/src/app/(main)/settings/shop/truckdetail/page.tsx index 8c50c6d..a955368 100644 --- a/src/app/(main)/settings/shop/truckdetail/page.tsx +++ b/src/app/(main)/settings/shop/truckdetail/page.tsx @@ -4,9 +4,9 @@ import { I18nProvider, getServerI18n } from "@/i18n"; import GeneralLoading from "@/components/General/GeneralLoading"; export default async function TruckLaneDetailPage() { - const { t } = await getServerI18n("shop", "common"); + await getServerI18n("shop"); return ( - + }> diff --git a/src/app/(main)/settings/user/create/page.tsx b/src/app/(main)/settings/user/create/page.tsx index 88694b8..33351fd 100644 --- a/src/app/(main)/settings/user/create/page.tsx +++ b/src/app/(main)/settings/user/create/page.tsx @@ -17,7 +17,7 @@ const CreateStaffPage: React.FC = async () => { return ( <> {t("Create User")} - + }> diff --git a/src/app/(main)/settings/user/edit/page.tsx b/src/app/(main)/settings/user/edit/page.tsx index e489f72..ba08277 100644 --- a/src/app/(main)/settings/user/edit/page.tsx +++ b/src/app/(main)/settings/user/edit/page.tsx @@ -18,7 +18,7 @@ const User: React.FC = async ({ searchParams }) => { return ( <> {t("Edit User")} - + }> diff --git a/src/app/(main)/settings/user/page.tsx b/src/app/(main)/settings/user/page.tsx index 6876046..ccf7712 100644 --- a/src/app/(main)/settings/user/page.tsx +++ b/src/app/(main)/settings/user/page.tsx @@ -47,7 +47,7 @@ const User: React.FC = async () => { {t("Create User")} - + diff --git a/src/app/(main)/settings/warehouse/create/page.tsx b/src/app/(main)/settings/warehouse/create/page.tsx index 0ee2965..913c3b6 100644 --- a/src/app/(main)/settings/warehouse/create/page.tsx +++ b/src/app/(main)/settings/warehouse/create/page.tsx @@ -9,7 +9,7 @@ const CreateWarehousePage: React.FC = async () => { return ( <> {t("Create Warehouse")} - + }> diff --git a/src/app/(main)/settings/warehouse/page.tsx b/src/app/(main)/settings/warehouse/page.tsx index 5a57332..a842eb3 100644 --- a/src/app/(main)/settings/warehouse/page.tsx +++ b/src/app/(main)/settings/warehouse/page.tsx @@ -31,7 +31,7 @@ const Warehouse: React.FC = async () => { {t("Create Warehouse")} - + } diff --git a/src/app/(main)/stockIssue/page.tsx b/src/app/(main)/stockIssue/page.tsx index 11b49be..1881417 100644 --- a/src/app/(main)/stockIssue/page.tsx +++ b/src/app/(main)/stockIssue/page.tsx @@ -11,7 +11,7 @@ export const metadata: Metadata = { const SearchView: React.FC = async () => { return ( <> - + }> diff --git a/src/app/(main)/stockOutIssueRecord/detail/page.tsx b/src/app/(main)/stockOutIssueRecord/detail/page.tsx index ce6ed51..4c21581 100644 --- a/src/app/(main)/stockOutIssueRecord/detail/page.tsx +++ b/src/app/(main)/stockOutIssueRecord/detail/page.tsx @@ -1,24 +1,18 @@ - import { SearchParams } from "@/app/utils/fetchUtil"; import PickOrderDetail from "@/components/PickOrderDetail"; -import { getServerI18n, I18nProvider } from "@/i18n"; -import { Stack, Typography } from "@mui/material"; +import { I18nProvider } from "@/i18n"; import { Metadata } from "next"; import { Suspense } from "react"; export const metadata: Metadata = { - title: "Consolidated Pick Order Flow", + title: "Stock Out Issue Record Detail", }; type Props = {} & SearchParams; -const PickOrder: React.FC = async ({ searchParams }) => { - const { t } = await getServerI18n("pickOrder"); - - - +const StockOutIssueRecordDetail: React.FC = async ({ searchParams }) => { return ( <> - + }> @@ -27,4 +21,4 @@ const PickOrder: React.FC = async ({ searchParams }) => { ); }; -export default PickOrder; +export default StockOutIssueRecordDetail; diff --git a/src/app/(main)/stockOutIssueRecord/page.tsx b/src/app/(main)/stockOutIssueRecord/page.tsx index 70e91dd..36d5aa3 100644 --- a/src/app/(main)/stockOutIssueRecord/page.tsx +++ b/src/app/(main)/stockOutIssueRecord/page.tsx @@ -1,23 +1,16 @@ -import { PreloadPickOrder } from "@/app/api/pickOrder"; import PickOrderSearch from "@/components/PickOrderSearch"; -import { getServerI18n } from "@/i18n"; import { I18nProvider } from "@/i18n"; -import { Stack, Typography } from "@mui/material"; import { Metadata } from "next"; import { Suspense } from "react"; export const metadata: Metadata = { - title: "Pick Order", + title: "Stock Out Issue Record", }; -const PickOrder: React.FC = async () => { - const { t } = await getServerI18n("pickOrder"); - - // PreloadPickOrder(); - +const StockOutIssueRecord: React.FC = async () => { return ( <> - + }> @@ -26,4 +19,4 @@ const PickOrder: React.FC = async () => { ); }; -export default PickOrder; +export default StockOutIssueRecord; diff --git a/src/app/(main)/stockRecord/page.tsx b/src/app/(main)/stockRecord/page.tsx index d144167..3749834 100644 --- a/src/app/(main)/stockRecord/page.tsx +++ b/src/app/(main)/stockRecord/page.tsx @@ -9,11 +9,11 @@ export const metadata: Metadata = { }; const SearchView: React.FC = async () => { - const { t } = await getServerI18n("inventory"); + const { t } = await getServerI18n("stockRecord"); return ( <> - + }> diff --git a/src/app/(main)/stocktakemanagement/page.tsx b/src/app/(main)/stocktakemanagement/page.tsx index 1ed3fae..917a737 100644 --- a/src/app/(main)/stocktakemanagement/page.tsx +++ b/src/app/(main)/stocktakemanagement/page.tsx @@ -8,9 +8,9 @@ import { notFound } from "next/navigation"; export default async function InventoryManagementPage() { - const { t } = await getServerI18n("inventory"); + const { t } = await getServerI18n("stockTake"); return ( - + }> diff --git a/src/app/(main)/tasks/create/page.tsx b/src/app/(main)/tasks/create/page.tsx index 656139f..c14b50d 100644 --- a/src/app/(main)/tasks/create/page.tsx +++ b/src/app/(main)/tasks/create/page.tsx @@ -1,6 +1,6 @@ import { preloadAllTasks } from "@/app/api/tasks"; import CreateTaskTemplate from "@/components/CreateTaskTemplate"; -import { getServerI18n } from "@/i18n"; +import { getServerI18n, I18nProvider } from "@/i18n"; import Typography from "@mui/material/Typography"; import { Metadata } from "next"; @@ -14,8 +14,10 @@ const Projects: React.FC = async () => { return ( <> - {t("Create Task Template")} - + + {t("Create Task Template")} + + ); }; diff --git a/src/app/(main)/tasks/page.tsx b/src/app/(main)/tasks/page.tsx index b9e9bf8..12c8bc2 100644 --- a/src/app/(main)/tasks/page.tsx +++ b/src/app/(main)/tasks/page.tsx @@ -7,6 +7,7 @@ import Stack from "@mui/material/Stack"; import Typography from "@mui/material/Typography"; import { Metadata } from "next"; import Link from "next/link"; +import { I18nProvider } from "@/i18n"; import { Suspense } from "react"; export const metadata: Metadata = { @@ -14,7 +15,7 @@ export const metadata: Metadata = { }; const TaskTemplates: React.FC = async () => { - const { t } = await getServerI18n("projects"); + const { t } = await getServerI18n("project"); preloadTaskTemplates(); return ( @@ -37,9 +38,11 @@ const TaskTemplates: React.FC = async () => { {t("Create Template")} - }> - - + + }> + + + ); }; diff --git a/src/app/api/shop/actions.ts b/src/app/api/shop/actions.ts index a60950b..6339c99 100644 --- a/src/app/api/shop/actions.ts +++ b/src/app/api/shop/actions.ts @@ -492,6 +492,7 @@ export interface TruckLaneVersionResponse { truckLanceCode: string; note: string | null; created: string | null; + createdBy?: string | null; /** truck_lane_version.modifiedBy(BaseEntity) */ modifiedBy?: string | null; } @@ -602,4 +603,309 @@ export const updateTruckLaneVersionNoteAction = async ( body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }); +}; + +// ---- Truck lane schedule (server-side apply) ---- + +export type TruckLaneScheduleLineAction = + | "MOVE" + | "CREATE" + | "DELETE" + | "ENSURE_LANE"; + +export type TruckLaneMoveTargetRequest = { + truckRowId: number; + toTruckLanceCode: string; + toRemark?: string | null; + toStoreId: string; + toLoadingSequence: number; + toDistrictReference?: string | null; +}; + +export type TruckLaneScheduleLineRequest = { + action: TruckLaneScheduleLineAction; + truckRowId?: number | null; + toTruckLanceCode: string; + toRemark?: string | null; + toStoreId: string; + toLoadingSequence?: number | null; + toDistrictReference?: string | null; + shopId?: number | null; + shopCode?: string | null; + shopName?: string | null; + departureTime?: string | null; + logisticId?: number | null; +}; + +export type CreateTruckLaneScheduleRequest = { + executeAt: string; + note?: string | null; + lines?: TruckLaneScheduleLineRequest[] | null; + moves?: TruckLaneMoveTargetRequest[] | null; +}; + +export type TruckLaneScheduleLineResponse = { + id: number; + action: TruckLaneScheduleLineAction; + truckRowId: number | null; + shopCode: string | null; + shopName: string | null; + fromTruckLanceCode: string | null; + fromRemark: string | null; + fromStoreId: string | null; + fromLoadingSequence?: number | null; + fromDistrictReference?: string | null; + fromDepartureTime?: string | null; + toTruckLanceCode: string; + toRemark: string | null; + toStoreId: string; + toDistrictReference?: string | null; + toLoadingSequence?: number | null; + departureTime?: string | null; + lineStatus: string; + errorMessage: string | null; + appliedAt: string | null; +}; + +export type RouteExcelSchedulePlanPreviewRow = { + action: TruckLaneScheduleLineAction; + truckRowId: number | null; + shopCode: string | null; + shopName: string | null; + toTruckLanceCode: string; + toRemark: string | null; + toStoreId: string; + toLoadingSequence: number | null; +}; + +export type RouteExcelSchedulePlanError = { + shopCode: string; + shopName: string; + message: string; +}; + +export type RouteExcelSchedulePlanCounts = { + moves: number; + creates: number; + deletes: number; + ensureLanes: number; +}; + +export type RouteExcelSchedulePlanResponse = { + sheetCount: number; + rowCount: number; + lines: TruckLaneScheduleLineRequest[]; + previews: RouteExcelSchedulePlanPreviewRow[]; + errors: RouteExcelSchedulePlanError[]; + counts: RouteExcelSchedulePlanCounts; +}; + +export type TruckLaneScheduleResponse = { + id: number; + executeAt: string; + status: string; + source: string; + note: string | null; + appliedAt: string | null; + errorMessage: string | null; + snapshotVersionId: number | null; + preApplySnapshotVersionId?: number | null; + created: string | null; + createdBy?: string | null; + modifiedBy: string | null; + lines?: TruckLaneScheduleLineResponse[] | null; + lineCounts?: { + total: number; + applied: number; + failed: number; + pending: number; + } | null; +}; + +export type PendingTruckRowIdsResponse = { + truckRowIds: number[]; +}; + +export type TruckLaneScheduleExcelPreviewRow = { + rowIndex: number; + shopCode: string; + toTruckLanceCode: string; + toRemark: string | null; + toStoreId: string; + executeAt: string | null; + truckRowId: number | null; +}; + +export type TruckLaneScheduleExcelRowError = { + rowIndex: number; + message: string; +}; + +export type ParseTruckLaneScheduleExcelResponse = { + rowCount: number; + validCount: number; + errorCount: number; + rows: TruckLaneScheduleExcelPreviewRow[]; + errors: TruckLaneScheduleExcelRowError[]; +}; + +export const createTruckLaneScheduleAction = async ( + data: CreateTruckLaneScheduleRequest, +): Promise => { + const endpoint = `${BASE_API_URL}/truckLaneSchedule`; + return serverFetchJson(endpoint, { + method: "POST", + body: JSON.stringify(data), + headers: { "Content-Type": "application/json" }, + }); +}; + +export const planTruckLaneScheduleFromRouteExcelAction = async ( + formData: FormData, +): Promise => { + const response = await serverFetch( + `${BASE_API_URL}/truckLaneSchedule/planFromRouteExcel`, + { + method: "POST", + body: formData, + }, + ); + if (!response.ok) { + const text = await response.text().catch(() => ""); + throw new ServerFetchError( + `Plan import failed: ${response.status} ${text}`.trim(), + response, + ); + } + return (await response.json()) as RouteExcelSchedulePlanResponse; +}; + +export const listTruckLaneSchedulesAction = async ( + status?: string[], + limit: number = 200, +): Promise => { + const params = new URLSearchParams(); + for (const s of status ?? []) { + params.append("status", s); + } + params.set("limit", String(limit)); + const qs = params.toString(); + const base = `${BASE_API_URL}/truckLaneSchedule`; + return serverFetchJson( + `${base}${qs ? `?${qs}` : ""}`, + { + method: "GET", + headers: { "Content-Type": "application/json" }, + }, + ); +}; + +export const getTruckLaneScheduleAction = async ( + id: number, +): Promise => { + const endpoint = `${BASE_API_URL}/truckLaneSchedule/${id}`; + return serverFetchJson(endpoint, { + method: "GET", + headers: { "Content-Type": "application/json" }, + }); +}; + +export const pendingTruckLaneScheduleShopIdsAction = + async (): Promise => { + const endpoint = `${BASE_API_URL}/truckLaneSchedule/pendingShopIds`; + return serverFetchJson(endpoint, { + method: "GET", + headers: { "Content-Type": "application/json" }, + }); + }; + +export const cancelTruckLaneScheduleAction = async ( + id: number, +): Promise => { + const endpoint = `${BASE_API_URL}/truckLaneSchedule/${id}/cancel`; + return serverFetchJson(endpoint, { + method: "POST", + headers: { "Content-Type": "application/json" }, + }); +}; + +export const applyNowTruckLaneScheduleAction = async ( + id: number, +): Promise => { + const endpoint = `${BASE_API_URL}/truckLaneSchedule/${id}/applyNow`; + return serverFetchJson(endpoint, { + method: "POST", + headers: { "Content-Type": "application/json" }, + }); +}; + +export type RetryFailedTruckLaneScheduleRequest = { + executeAt?: string | null; +}; + +export const retryFailedTruckLaneScheduleAction = async ( + id: number, + body?: RetryFailedTruckLaneScheduleRequest, +): Promise => { + const endpoint = `${BASE_API_URL}/truckLaneSchedule/${id}/retry-failed`; + return serverFetchJson(endpoint, { + method: "POST", + body: JSON.stringify(body ?? {}), + headers: { "Content-Type": "application/json" }, + }); +}; + +export const ignoreTruckLaneScheduleAction = async ( + id: number, +): Promise => { + const endpoint = `${BASE_API_URL}/truckLaneSchedule/${id}/ignore`; + return serverFetchJson(endpoint, { + method: "POST", + headers: { "Content-Type": "application/json" }, + }); +}; + +export const parseTruckLaneScheduleExcelAction = async ( + formData: FormData, + defaultExecuteAt?: string | null, +): Promise => { + const qs = + defaultExecuteAt != null && defaultExecuteAt !== "" + ? `?defaultExecuteAt=${encodeURIComponent(defaultExecuteAt)}` + : ""; + const response = await serverFetch( + `${BASE_API_URL}/truckLaneSchedule/parseExcel${qs}`, + { method: "POST", body: formData }, + ); + if (!response.ok) { + const text = await response.text().catch(() => ""); + throw new ServerFetchError( + `Parse schedule Excel failed: ${response.status} ${text}`.trim(), + response, + ); + } + return (await response.json()) as ParseTruckLaneScheduleExcelResponse; +}; + +export const importTruckLaneScheduleExcelAction = async ( + formData: FormData, + defaultExecuteAt?: string | null, + note?: string | null, +): Promise => { + const params = new URLSearchParams(); + if (defaultExecuteAt) params.set("defaultExecuteAt", defaultExecuteAt); + if (note) params.set("note", note); + const qs = params.toString() ? `?${params.toString()}` : ""; + const response = await serverFetch( + `${BASE_API_URL}/truckLaneSchedule/importExcel${qs}`, + { method: "POST", body: formData }, + ); + if (!response.ok) { + const text = await response.text().catch(() => ""); + throw new ServerFetchError( + `Import schedule Excel failed: ${response.status} ${text}`.trim(), + response, + ); + } + return (await response.json()) as TruckLaneScheduleResponse[]; }; \ No newline at end of file diff --git a/src/app/api/shop/client.ts b/src/app/api/shop/client.ts index d86dcf0..d7b710f 100644 --- a/src/app/api/shop/client.ts +++ b/src/app/api/shop/client.ts @@ -30,7 +30,29 @@ import { diffTruckLaneVersionsAction, restoreTruckLaneVersionAction, updateTruckLaneVersionNoteAction, + createTruckLaneScheduleAction, + planTruckLaneScheduleFromRouteExcelAction, + listTruckLaneSchedulesAction, + getTruckLaneScheduleAction, + pendingTruckLaneScheduleShopIdsAction, + cancelTruckLaneScheduleAction, + applyNowTruckLaneScheduleAction, + retryFailedTruckLaneScheduleAction, + ignoreTruckLaneScheduleAction, + type RetryFailedTruckLaneScheduleRequest, + parseTruckLaneScheduleExcelAction, + importTruckLaneScheduleExcelAction, type CreateTruckLaneSnapshotRequest, + type CreateTruckLaneScheduleRequest, + type TruckLaneScheduleResponse, + type TruckLaneScheduleLineRequest, + type RouteExcelSchedulePlanPreviewRow, + type RouteExcelSchedulePlanError, + type RouteExcelSchedulePlanResponse, + type RouteExcelSchedulePlanCounts, + type TruckLaneMoveTargetRequest, + type PendingTruckRowIdsResponse, + type ParseTruckLaneScheduleExcelResponse, type UpdateTruckLaneVersionNoteRequest, type TruckLaneVersionResponse, type TruckLaneVersionLineResponse, @@ -184,4 +206,92 @@ export const updateTruckLaneVersionNoteClient = async ( return await updateTruckLaneVersionNoteAction(versionId, data); }; +export const planTruckLaneScheduleFromRouteExcelClient = async ( + formData: FormData, +): Promise => { + return await planTruckLaneScheduleFromRouteExcelAction(formData); +}; + +export const createTruckLaneScheduleClient = async ( + data: CreateTruckLaneScheduleRequest, +): Promise => { + return await createTruckLaneScheduleAction(data); +}; + +export const listTruckLaneSchedulesClient = async ( + status?: string[], + limit: number = 200, +): Promise => { + return await listTruckLaneSchedulesAction(status, limit); +}; + +export const getTruckLaneScheduleClient = async ( + id: number, +): Promise => { + return await getTruckLaneScheduleAction(id); +}; + +export const pendingTruckLaneScheduleShopIdsClient = + async (): Promise => { + return await pendingTruckLaneScheduleShopIdsAction(); + }; + +export const cancelTruckLaneScheduleClient = async ( + id: number, +): Promise => { + return await cancelTruckLaneScheduleAction(id); +}; + +export const applyNowTruckLaneScheduleClient = async ( + id: number, +): Promise => { + return await applyNowTruckLaneScheduleAction(id); +}; + +export const retryFailedTruckLaneScheduleClient = async ( + id: number, + body?: RetryFailedTruckLaneScheduleRequest, +): Promise => { + return await retryFailedTruckLaneScheduleAction(id, body); +}; + +export const ignoreTruckLaneScheduleClient = async ( + id: number, +): Promise => { + return await ignoreTruckLaneScheduleAction(id); +}; + +export const parseTruckLaneScheduleExcelClient = async ( + formData: FormData, + defaultExecuteAt?: string | null, +): Promise => { + return await parseTruckLaneScheduleExcelAction(formData, defaultExecuteAt); +}; + +export const importTruckLaneScheduleExcelClient = async ( + formData: FormData, + defaultExecuteAt?: string | null, + note?: string | null, +): Promise => { + return await importTruckLaneScheduleExcelAction( + formData, + defaultExecuteAt, + note, + ); +}; + +export type { + TruckLaneScheduleResponse, + TruckLaneScheduleLineResponse, + TruckLaneMoveTargetRequest, + TruckLaneScheduleLineRequest, + CreateTruckLaneScheduleRequest, + PendingTruckRowIdsResponse, + ParseTruckLaneScheduleExcelResponse, + RouteExcelSchedulePlanResponse, + RouteExcelSchedulePlanPreviewRow, + RouteExcelSchedulePlanError, + RouteExcelSchedulePlanCounts, +} from "./actions"; + export default fetchAllShopsClient; diff --git a/src/components/AppBar/AppBar.tsx b/src/components/AppBar/AppBar.tsx index cb70811..8fa9d95 100644 --- a/src/components/AppBar/AppBar.tsx +++ b/src/components/AppBar/AppBar.tsx @@ -16,7 +16,7 @@ export interface AppBarProps { const AppBar: React.FC = ({ avatarImageSrc, profileName }) => { return ( - + diff --git a/src/components/AppBar/Profile.tsx b/src/components/AppBar/Profile.tsx index dc6ed1d..7146f81 100644 --- a/src/components/AppBar/Profile.tsx +++ b/src/components/AppBar/Profile.tsx @@ -29,7 +29,7 @@ const Profile: React.FC = ({ avatarImageSrc, profileName }) => { return ( <> - + = ({ boms }) => { - const { t } = useTranslation("common"); + const { t } = useTranslation(["bomWeighting", "common"]); const columns = useMemo[]>( () => [ { field: "code", - headerName: t("Code"), + headerName: t("Item Code"), flex: 1, minWidth: 150, }, { field: "name", - headerName: t("Name"), + headerName: t("Item Name"), flex: 1.5, minWidth: 220, }, diff --git a/src/components/BomWeightingScoreTable/BomWeightingScoreTable.tsx b/src/components/BomWeightingScoreTable/BomWeightingScoreTable.tsx index 6767949..591dcd5 100644 --- a/src/components/BomWeightingScoreTable/BomWeightingScoreTable.tsx +++ b/src/components/BomWeightingScoreTable/BomWeightingScoreTable.tsx @@ -20,7 +20,7 @@ interface Props { } const BomWeightingScoreTable: React.FC & { Loading?: React.FC } = ({ bomWeightingScores: initialBomWeightingScores, onWeightingUpdated }) => { - const { t } = useTranslation("common"); + const { t } = useTranslation(["bomWeighting", "common"]); const [bomWeightingScores, setBomWeightingScores] = useState(initialBomWeightingScores); const [isEditMode, setIsEditMode] = useState(false); const [isSaving, setIsSaving] = useState(false); @@ -173,7 +173,7 @@ const BomWeightingScoreTable: React.FC & { Loading?: React.FC } = ({ bomW () => [ { field: "name", - headerName: t("Name"), + headerName: t("Scoring Item"), flex: 1, }, { diff --git a/src/components/BomWeightingTabs/BomWeightingTabs.tsx b/src/components/BomWeightingTabs/BomWeightingTabs.tsx index 6323dea..6afb68c 100644 --- a/src/components/BomWeightingTabs/BomWeightingTabs.tsx +++ b/src/components/BomWeightingTabs/BomWeightingTabs.tsx @@ -18,7 +18,7 @@ interface Props { } const BomWeightingTabs: React.FC = ({ bomWeightingScores: initialBomWeightingScores }) => { - const { t } = useTranslation("common"); + const { t } = useTranslation(["bomWeighting", "common"]); const [tab, setTab] = useState(0); const [bomWeightingScores, setBomWeightingScores] = useState(initialBomWeightingScores); const [bomScores, setBomScores] = useState(null); diff --git a/src/components/Breadcrumb/Breadcrumb.tsx b/src/components/Breadcrumb/Breadcrumb.tsx index df23a13..73b2ad2 100644 --- a/src/components/Breadcrumb/Breadcrumb.tsx +++ b/src/components/Breadcrumb/Breadcrumb.tsx @@ -7,75 +7,88 @@ import MUILink from "@mui/material/Link"; import { usePathname } from "next/navigation"; import { useTranslation } from "react-i18next"; -const pathToLabelMap: { [path: string]: string } = { - "": "總覽", - "/chart": "圖表報告", - "/chart/warehouse": "庫存與倉儲", - "/chart/purchase": "採購", - "/chart/delivery": "發貨與配送", - "/chart/joborder": "工單", - "/chart/joborder/board": "工單即時看板", - "/chart/forecast": "預測與計劃", - "/projects": "Projects", - "/projects/create": "Create Project", - "/tasks": "Task Template", - "/tasks/create": "Create Task Template", - "/settings/qcItem": "Qc Item", - "/settings/qcItemAll": "QC Item All", - "/settings/qrCodeHandle": "QR Code Handle", - "/settings/rss": "Demand Forecast Setting", - "/settings/equipment": "Equipment", - "/settings/equipment/MaintenanceEdit": "MaintenanceEdit", - "/settings/shop": "ShopAndTruck", - "/settings/shop/board": "Route Board", - "/settings/shop/detail": "Shop Detail", - "/settings/shop/truckdetail": "Truck Lane Detail", - "/settings/printer": "Printer", - "/scheduling/rough": "Demand Forecast", - "/scheduling/rough/edit": "FG & Material Demand Forecast Detail", - "/scheduling/detailed": "Detail Scheduling", - "/scheduling/detailed/edit": "FG Production Schedule", - "/inventory": "Inventory", - "/settings/importTesting": "Import Testing", - "/do": "Delivery Order", - "/doworkbench": "DO Workbench", - "/doworkbenchsearch": "DO Workbench Search", - "/doworkbench/pick": "DO Workbench pick", - "/doworkbench/edit": "DO Workbench detail", - "/pickOrder": "Pick Order", - "/po": "Purchase Order", - "/po/workbench": "PO Workbench", - "/dashboard": "dashboard", - "/jo": "Job Order", - "/jo/edit": "Edit Job Order", - "/jo/testing": "Job order testing", - "/jo/workbench": "Job Order Workbench", - "/putAway": "Put Away", - "/stockIssue": "Stock Issue", - "/report": "Report", - "/m18Syn": "M18 Sync", - "/bagPrint": "打袋機", - "/laserPrint": "檸檬機(激光機)", - "/settings/itemPrice": "Price Inquiry", - "/finishedGood": "Finished Good Order", - "/finishedGood/management": "Finished Good Management", +const pathToLabelKey: { [path: string]: string } = { + "": "nav.breadcrumb.home", + "/chart": "nav.breadcrumb.chart", + "/chart/warehouse": "nav.breadcrumb.chartWarehouse", + "/chart/purchase": "nav.breadcrumb.chartPurchase", + "/chart/delivery": "nav.breadcrumb.chartDelivery", + "/chart/joborder": "nav.breadcrumb.chartJobOrder", + "/chart/joborder/board": "nav.breadcrumb.chartJobOrderBoard", + "/chart/forecast": "nav.breadcrumb.chartForecast", + "/projects": "nav.breadcrumb.projects", + "/projects/create": "nav.breadcrumb.projectsCreate", + "/tasks": "nav.breadcrumb.tasks", + "/tasks/create": "nav.breadcrumb.tasksCreate", + "/settings/qcItem": "nav.breadcrumb.qcItem", + "/settings/qcItemAll": "nav.breadcrumb.qcItemAll", + "/settings/qrCodeHandle": "nav.breadcrumb.qrCodeHandle", + "/settings/deliveryOrderFloor": "nav.breadcrumb.deliveryOrderFloor", + "/settings/masterDataIssues": "nav.breadcrumb.masterDataIssues", + "/settings/rss": "nav.breadcrumb.demandForecast", + "/settings/equipment": "nav.breadcrumb.equipment", + "/settings/equipment/MaintenanceEdit": "nav.breadcrumb.equipmentMaintenanceEdit", + "/settings/shop": "nav.breadcrumb.shop", + "/settings/shop/board": "nav.breadcrumb.routeBoard", + "/settings/shop/detail": "nav.breadcrumb.shopDetail", + "/settings/shop/truckdetail": "nav.breadcrumb.truckLaneDetail", + "/settings/printer": "nav.breadcrumb.printer", + "/scheduling/rough": "nav.breadcrumb.schedulingRough", + "/scheduling/rough/edit": "nav.breadcrumb.schedulingRoughEdit", + "/scheduling/detailed": "nav.breadcrumb.schedulingDetailed", + "/scheduling/detailed/edit": "nav.breadcrumb.schedulingDetailedEdit", + "/inventory": "nav.breadcrumb.inventory", + "/settings/importTesting": "nav.breadcrumb.importTesting", + "/settings/m18ImportTesting": "nav.breadcrumb.importTesting", + "/do": "nav.deliveryOrder", + "/doworkbench": "nav.store.doWorkbench", + "/doworkbenchsearch": "nav.breadcrumb.doWorkbenchSearch", + "/doworkbench/pick": "nav.breadcrumb.doWorkbenchPick", + "/doworkbench/edit": "nav.breadcrumb.doWorkbenchEdit", + "/pickOrder": "nav.store.pickOrder", + "/po": "nav.store.purchaseOrder", + "/po/edit": "nav.breadcrumb.poEdit", + "/po/workbench": "nav.breadcrumb.poWorkbench", + "/dashboard": "nav.dashboard", + "/jo": "nav.jobOrder.searchCreate", + "/jo/edit": "nav.breadcrumb.joEdit", + "/jo/testing": "nav.breadcrumb.joTesting", + "/jo/workbench": "nav.breadcrumb.joWorkbench", + "/putAway": "nav.store.putAwayScan", + "/stockIssue": "nav.store.stockIssue", + "/stocktakemanagement": "nav.store.stockTake", + "/stockRecord": "nav.store.stockRecord", + "/report": "nav.breadcrumb.report", + "/m18Syn": "nav.breadcrumb.m18Sync", + "/bagPrint": "nav.breadcrumb.bagPrint", + "/laserPrint": "nav.breadcrumb.laserPrint", + "/settings/itemPrice": "nav.breadcrumb.priceInquiry", + "/finishedGood": "nav.breadcrumb.finishedGood", + "/finishedGood/management": "nav.breadcrumb.finishedGoodManagement", + "/ps": "nav.scheduling", + "/productionProcess": "nav.jobOrder.productionProcess", + "/jodetail": "nav.jobOrder.pickExecution", + "/bag": "nav.jobOrder.bagUsage", }; const Breadcrumb = () => { const pathname = usePathname(); const segments = pathname.split("/"); - const { t } = useTranslation("common"); + const { t } = useTranslation(["navigation", "common"]); return ( {segments.map((segment, index) => { const href = segments.slice(0, index + 1).join("/"); - const label = pathToLabelMap[href] || segment; + const labelKey = pathToLabelKey[href]; + const label = labelKey + ? t(labelKey) + : t(segment, { ns: "common", defaultValue: segment }); if (index === segments.length - 1) { return ( - {t(label)} + {label} ); } else { @@ -87,7 +100,7 @@ const Breadcrumb = () => { component={Link} href={href || "/"} > - {t(label)} + {label} ); } diff --git a/src/components/CreateClaim/ClaimInputGrid.tsx b/src/components/CreateClaim/ClaimInputGrid.tsx index 7ea37fb..52dd967 100644 --- a/src/components/CreateClaim/ClaimInputGrid.tsx +++ b/src/components/CreateClaim/ClaimInputGrid.tsx @@ -271,8 +271,8 @@ const ClaimInputGrid: React.FC = ({ ...props }) => { } - title="Save" - label="Save" + title={t("Save")} + label={t("Save")} sx={{ color: "primary.main", }} @@ -281,8 +281,8 @@ const ClaimInputGrid: React.FC = ({ ...props }) => { } - title="Cancel" - label="Cancel" + title={t("Cancel")} + label={t("Cancel")} className="textPrimary" onClick={handleCancelClick(id)} color="inherit" @@ -294,16 +294,16 @@ const ClaimInputGrid: React.FC = ({ ...props }) => { } - title="Edit" - label="Edit" + title={t("Edit")} + label={t("Edit")} className="textPrimary" onClick={handleEditClick(id)} color="inherit" />, } onClick={handleDeleteClick(id)} sx={{ color: "red" }} @@ -313,7 +313,7 @@ const ClaimInputGrid: React.FC = ({ ...props }) => { }, { field: "date", - headerName: "Invoice Date", + headerName: t("Invoice Date"), // width: 220, flex: 1, editable: true, @@ -321,7 +321,7 @@ const ClaimInputGrid: React.FC = ({ ...props }) => { }, { field: "description", - headerName: "Description", + headerName: t("Description"), // width: 220, flex: 2, editable: true, @@ -329,7 +329,7 @@ const ClaimInputGrid: React.FC = ({ ...props }) => { }, { field: "cost", - headerName: "Cost (HKD)", + headerName: t("Cost (HKD)"), editable: true, type: "number", valueFormatter: (params) => { @@ -338,7 +338,7 @@ const ClaimInputGrid: React.FC = ({ ...props }) => { }, { field: "document", - headerName: "Supporting Document", + headerName: t("Supporting Document"), type: "string", editable: true, flex: 2, @@ -360,7 +360,7 @@ const ClaimInputGrid: React.FC = ({ ...props }) => { {params.value} ) : ( - {/* ------------------------------------------- */} diff --git a/src/components/DetailedScheduleDetail/ViewByFGDetails.tsx b/src/components/DetailedScheduleDetail/ViewByFGDetails.tsx index 151da2b..0cf5f25 100644 --- a/src/components/DetailedScheduleDetail/ViewByFGDetails.tsx +++ b/src/components/DetailedScheduleDetail/ViewByFGDetails.tsx @@ -89,35 +89,35 @@ const ViewByFGDetails: React.FC = ({ // --- Added Avg Usage, Stock, Days Left, and Job Order Count --- { field: "avgQtyLastMonth", // This MUST match the key in the object - label: t("最近每日用量"), + label: t("Avg Qty Last Month"), type: "read-only", // Ensure 'row' has the property 'avgQtyLastMonth' renderCell: (row) => <>{decimalFormatter.format(row.avgQtyLastMonth ?? 0)}, }, { field: "stockQty", - label: t("存貨量"), + label: t("Stock Qty"), type: "read-only", style: { textAlign: "right" } as any, renderCell: (row) => <>{decimalFormatter.format(row.stockQty ?? 0)}, }, { field: "daysLeft", - label: t("可用日"), + label: t("Days Left"), type: "read-only", style: { textAlign: "right" } as any, renderCell: (row) => <>{row.daysLeft ?? 0}, }, { field: "outputQty", - label: t("每批次生產數"), + label: t("Output Qty"), type: "read-only", style: { textAlign: "right", fontWeight: "bold" } as any, renderCell: (row) => <>{row.outputQty ?? 0}, }, { field: "needNoOfJobOrder", - label: t("生產批次"), + label: t("Batch Need"), type: "read-only", style: { textAlign: "right", fontWeight: "bold" } as any, renderCell: (row) => <>{row.needNoOfJobOrder ?? 0}, diff --git a/src/components/DoWorkbench/WorkbenchGoodPickExecutionDetail.tsx b/src/components/DoWorkbench/WorkbenchGoodPickExecutionDetail.tsx index 48dac12..4a8c863 100644 --- a/src/components/DoWorkbench/WorkbenchGoodPickExecutionDetail.tsx +++ b/src/components/DoWorkbench/WorkbenchGoodPickExecutionDetail.tsx @@ -24,6 +24,7 @@ import dayjs from 'dayjs'; import { normalizeTargetDateInput } from "@/utils/workbenchTargetDate"; import TestQrCodeProvider from "@/components/QrCodeScannerProvider/TestQrCodeProvider"; import { fetchLotDetail } from "@/app/api/inventory/actions"; +import { formatDepartureTime } from "@/app/utils/formatUtil"; import React, { useCallback, useEffect, useState, useRef, useMemo, startTransition } from "react"; import { useTranslation } from "react-i18next"; import { usePathname, useRouter } from "next/navigation"; @@ -3698,7 +3699,7 @@ const handleSubmitAllScanned = useCallback(async () => { {t("Ticket No.")}: {fgPickOrders[0].ticketNo || '-'}{isExtraTicket ? ` (${t("isExtra order")})` : ""} - {t("Departure Time")}: {fgPickOrders[0].DepartureTime || '-'} + {t("Departure Time")}: {formatDepartureTime(fgPickOrders[0].DepartureTime)} diff --git a/src/components/DoWorkbench/WorkbenchTicketReleaseTable.tsx b/src/components/DoWorkbench/WorkbenchTicketReleaseTable.tsx index c663d3d..53c824a 100644 --- a/src/components/DoWorkbench/WorkbenchTicketReleaseTable.tsx +++ b/src/components/DoWorkbench/WorkbenchTicketReleaseTable.tsx @@ -30,7 +30,7 @@ import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { DatePicker } from "@mui/x-date-pickers/DatePicker"; import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; import dayjs, { Dayjs } from "dayjs"; -import { arrayToDayjs, requiredDeliveryDateToDayString } from "@/app/utils/formatUtil"; +import { arrayToDayjs, formatDepartureTime, requiredDeliveryDateToDayString } from "@/app/utils/formatUtil"; import { fetchWorkbenchTicketReleaseTable, forceCompleteWorkbenchTicket, @@ -329,7 +329,7 @@ const WorkbenchTicketReleaseTableTab: React.FC = () => { {row.truckLanceCode && } - {row.truckDepartureTime && } + {row.truckDepartureTime && } {row.shopName || "-"} diff --git a/src/components/EnterLeave/EnterLeaveModal.tsx b/src/components/EnterLeave/EnterLeaveModal.tsx index 0d8f8c0..53cb279 100644 --- a/src/components/EnterLeave/EnterLeaveModal.tsx +++ b/src/components/EnterLeave/EnterLeaveModal.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import Button from "@mui/material/Button"; +import { t } from "i18next"; import { Card, Modal, Typography } from "@mui/material"; import TimesheetInputGrid from "./LeaveInputGrid"; interface EnterTimesheetModalProps { @@ -18,13 +19,13 @@ const EnterTimesheetModal: React.FC = ({ { id: "projectCode", field: "projectCode", - headerName: "Project Code and Name", + headerName: t("Project Code and Name"), flex: 1, }, { id: "task", field: "task", - headerName: "Task", + headerName: t("Task"), flex: 1, }, ]; @@ -88,14 +89,14 @@ const EnterTimesheetModal: React.FC = ({ variant="contained" onClick={props.onClose} > - Confirm + {t("Confirm")} diff --git a/src/components/EnterLeave/LeaveInputGrid.tsx b/src/components/EnterLeave/LeaveInputGrid.tsx index 03eeaab..4b678da 100644 --- a/src/components/EnterLeave/LeaveInputGrid.tsx +++ b/src/components/EnterLeave/LeaveInputGrid.tsx @@ -350,7 +350,7 @@ const TimesheetInputGrid: React.FC = ({ { field: "actions", type: "actions", - headerName: "Actions", + headerName: t("Actions"), width: 100, cellClassName: "actions", getActions: ({ id }) => { @@ -361,8 +361,8 @@ const TimesheetInputGrid: React.FC = ({ } - title="Save" - label="Save" + title={t("Save")} + label={t("Save")} sx={{ color: "primary.main", }} @@ -371,8 +371,8 @@ const TimesheetInputGrid: React.FC = ({ } - title="Cancel" - label="Cancel" + title={t("Cancel")} + label={t("Cancel")} className="textPrimary" onClick={handleCancelClick(id)} color="inherit" @@ -384,16 +384,16 @@ const TimesheetInputGrid: React.FC = ({ } - title="Edit" - label="Edit" + title={t("Edit")} + label={t("Edit")} className="textPrimary" onClick={handleEditClick(id)} color="inherit" />, } onClick={handleDeleteClick(id)} sx={{ color: "red" }} @@ -403,7 +403,7 @@ const TimesheetInputGrid: React.FC = ({ }, { field: "projectCode", - headerName: "Project Code", + headerName: t("Project Code"), // width: 220, flex: 2, editable: true, @@ -412,7 +412,7 @@ const TimesheetInputGrid: React.FC = ({ }, { field: "task", - headerName: "Task", + headerName: t("Task"), // width: 220, flex: 3, editable: true, diff --git a/src/components/EnterTimesheet/EnterTimesheetModal.tsx b/src/components/EnterTimesheet/EnterTimesheetModal.tsx index 2a81613..f2d00ea 100644 --- a/src/components/EnterTimesheet/EnterTimesheetModal.tsx +++ b/src/components/EnterTimesheet/EnterTimesheetModal.tsx @@ -23,13 +23,13 @@ const EnterTimesheetModal: React.FC = ({ { id: "projectCode", field: "projectCode", - headerName: "Project Code and Name", + headerName: t("Project Code and Name"), flex: 1, }, { id: "task", field: "task", - headerName: "Task", + headerName: t("Task"), flex: 1, }, ]; @@ -93,14 +93,14 @@ const EnterTimesheetModal: React.FC = ({ variant="contained" onClick={props.onClose} > - Confirm + {t("Confirm")} diff --git a/src/components/EnterTimesheet/TimesheetInputGrid.tsx b/src/components/EnterTimesheet/TimesheetInputGrid.tsx index bc64c50..5c22e57 100644 --- a/src/components/EnterTimesheet/TimesheetInputGrid.tsx +++ b/src/components/EnterTimesheet/TimesheetInputGrid.tsx @@ -350,7 +350,7 @@ const TimesheetInputGrid: React.FC = ({ { field: "actions", type: "actions", - headerName: "Actions", + headerName: t("Actions"), width: 100, cellClassName: "actions", getActions: ({ id }) => { @@ -361,8 +361,8 @@ const TimesheetInputGrid: React.FC = ({ } - title="Save" - label="Save" + title={t("Save")} + label={t("Save")} sx={{ color: "primary.main", }} @@ -371,8 +371,8 @@ const TimesheetInputGrid: React.FC = ({ } - title="Cancel" - label="Cancel" + title={t("Cancel")} + label={t("Cancel")} className="textPrimary" onClick={handleCancelClick(id)} color="inherit" @@ -384,16 +384,16 @@ const TimesheetInputGrid: React.FC = ({ } - title="Edit" - label="Edit" + title={t("Edit")} + label={t("Edit")} className="textPrimary" onClick={handleEditClick(id)} color="inherit" />, } onClick={handleDeleteClick(id)} sx={{ color: "red" }} @@ -403,7 +403,7 @@ const TimesheetInputGrid: React.FC = ({ }, { field: "projectCode", - headerName: "Project Code", + headerName: t("Project Code"), // width: 220, flex: 2, editable: true, @@ -412,7 +412,7 @@ const TimesheetInputGrid: React.FC = ({ }, { field: "task", - headerName: "Task", + headerName: t("Task"), // width: 220, flex: 3, editable: true, diff --git a/src/components/EquipmentSearch/EquipmentSearch.tsx b/src/components/EquipmentSearch/EquipmentSearch.tsx index 82c4dd1..dbe7db7 100644 --- a/src/components/EquipmentSearch/EquipmentSearch.tsx +++ b/src/components/EquipmentSearch/EquipmentSearch.tsx @@ -42,7 +42,7 @@ type SearchParamNames = keyof SearchQuery; const EquipmentSearch: React.FC = ({ equipments, tabIndex = 0 }) => { const [filteredEquipments, setFilteredEquipments] = useState([]); - const { t } = useTranslation("common"); + const { t } = useTranslation(["equipment", "common"]); const router = useRouter(); const [filterObjByTab, setFilterObjByTab] = useState>({ 0: {}, diff --git a/src/components/FinishedGoodManagement/FinishedGoodManagementPage.tsx b/src/components/FinishedGoodManagement/FinishedGoodManagementPage.tsx index 3e9b57d..2108a20 100644 --- a/src/components/FinishedGoodManagement/FinishedGoodManagementPage.tsx +++ b/src/components/FinishedGoodManagement/FinishedGoodManagementPage.tsx @@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next"; import PickOrderSequenceTab from "./PickOrderSequenceTab"; export default function FinishedGoodManagementPage() { - const { t } = useTranslation("common"); + const { t } = useTranslation("finishedgoodmanagement"); const [tab, setTab] = React.useState(0); return ( diff --git a/src/components/FinishedGoodManagement/PickOrderSequenceTab.tsx b/src/components/FinishedGoodManagement/PickOrderSequenceTab.tsx index f68191c..5a5b3cb 100644 --- a/src/components/FinishedGoodManagement/PickOrderSequenceTab.tsx +++ b/src/components/FinishedGoodManagement/PickOrderSequenceTab.tsx @@ -87,7 +87,7 @@ function reorderByIndex(arr: T[], from: number, to: number) { } export default function PickOrderSequenceTab() { - const { t } = useTranslation("common"); + const { t } = useTranslation("finishedgoodmanagement"); const rowRefs = React.useRef(new Map()); const [loading, setLoading] = React.useState(false); @@ -358,8 +358,6 @@ export default function PickOrderSequenceTab() { value={filterCode} onChange={(e) => setFilterCode(e.target.value)} sx={{ width: { xs: "100%", md: 180 } }} - helperText=" " - placeholder="PP1074 / 1074" /> setFilterName(e.target.value)} sx={{ width: { xs: "100%", md: 220 } }} - helperText=" " /> @@ -461,8 +457,8 @@ export default function PickOrderSequenceTab() { {t("Order")} {t("Code")} {t("Name")} - 出貨倉 - 入貨倉 + {t("Shipping Warehouse")} + {t("Receiving Warehouse")} {t("Actions")} @@ -627,10 +623,6 @@ export default function PickOrderSequenceTab() { {t("Loading")} - ) : addRows.length === 0 ? ( - - {t("No data available")} - ) : ( addRows.map((r) => { const checked = (addSelection as number[]).includes(r.id); diff --git a/src/components/FinishedGoodSearch/FGPickOrderInfoCard.tsx b/src/components/FinishedGoodSearch/FGPickOrderInfoCard.tsx index 1afad92..713cfc9 100644 --- a/src/components/FinishedGoodSearch/FGPickOrderInfoCard.tsx +++ b/src/components/FinishedGoodSearch/FGPickOrderInfoCard.tsx @@ -3,6 +3,7 @@ import { Box, Card, CardContent, Grid, TextField, Stack } from "@mui/material"; import { useTranslation } from "react-i18next"; import { FGPickOrderResponse, DoPickOrderDetail } from "@/app/api/pickOrder/actions"; +import { formatDepartureTime } from "@/app/utils/formatUtil"; interface Props { fgOrder: FGPickOrderResponse; @@ -85,7 +86,7 @@ const FGPickOrderInfoCard: React.FC = ({ fgOrder, doPickOrderDetail }) => = ({ {t("Departure Time")}:{" "} - {fgPickOrders[0].DepartureTime || "-"} + {formatDepartureTime(fgPickOrders[0].DepartureTime)} @@ -4923,7 +4924,7 @@ const PickExecution: React.FC = ({ borderColor: "warning.main", color: "warning.main", }} - title="Report missing or bad items" + title={t("Report missing or bad items")} > {t("Edit")} diff --git a/src/components/ImportBom/ImportBomDetailTab.tsx b/src/components/ImportBom/ImportBomDetailTab.tsx index 88b0b7d..3f89316 100644 --- a/src/components/ImportBom/ImportBomDetailTab.tsx +++ b/src/components/ImportBom/ImportBomDetailTab.tsx @@ -61,7 +61,7 @@ function resolveEquipmentCode( } const ImportBomDetailTab: React.FC = () => { - const { t } = useTranslation( "common" ); + const { t } = useTranslation(["importBom", "common"]); const [bomList, setBomList] = useState([]); const [selectedBomId, setSelectedBomId] = useState(""); const [detail, setDetail] = useState(null); diff --git a/src/components/ImportBom/ImportBomResultForm.tsx b/src/components/ImportBom/ImportBomResultForm.tsx index d2e563b..20fcda4 100644 --- a/src/components/ImportBom/ImportBomResultForm.tsx +++ b/src/components/ImportBom/ImportBomResultForm.tsx @@ -42,7 +42,7 @@ type Props = { onBack, }: Props) { console.log("issueLogFileId from props", issueLogFileId); - const { t } = useTranslation("common"); + const { t } = useTranslation(["importBom", "common", "importExcel"]); const [search, setSearch] = useState(""); const [items, setItems] = useState(() => correctFileNames.map((fileName) => ({ diff --git a/src/components/InventorySearch/InventorySearch.tsx b/src/components/InventorySearch/InventorySearch.tsx index 7c60972..33becd7 100644 --- a/src/components/InventorySearch/InventorySearch.tsx +++ b/src/components/InventorySearch/InventorySearch.tsx @@ -147,8 +147,11 @@ const InventorySearch: React.FC = ({ inventories, printerCombo }) => { { label: t('Type'), paramName: 'itemType', - type: 'select', - options: uniq(inventories.map((i) => i.itemType)), + type: 'select-labelled', + options: uniq(inventories.map((i) => i.itemType)).map((type) => ({ + value: type, + label: t(type), + })), }, // { // label: t("Status"), diff --git a/src/components/ItemPriceSearch/ItemPriceSearch.tsx b/src/components/ItemPriceSearch/ItemPriceSearch.tsx index b356567..54556e9 100644 --- a/src/components/ItemPriceSearch/ItemPriceSearch.tsx +++ b/src/components/ItemPriceSearch/ItemPriceSearch.tsx @@ -24,7 +24,7 @@ type ItemPriceSearchComponent = React.FC & { type SearchParamNames = keyof SearchQuery; const ItemPriceSearch: ItemPriceSearchComponent = () => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["itemPrice", "inventory", "common", "importExcel"]); const [item, setItem] = useState(null); const [isSearching, setIsSearching] = useState(false); @@ -113,7 +113,7 @@ const ItemPriceSearch: ItemPriceSearchComponent = () => { a.click(); URL.revokeObjectURL(url); } catch { - alert(t("Download failed", { ns: "common" })); + alert(t("Download failed", { ns: "importExcel" })); } finally { setIsDownloading(false); } @@ -144,24 +144,24 @@ const ItemPriceSearch: ItemPriceSearchComponent = () => { if (!data.success) { const errMsg = (data.errors?.length ?? 0) > 0 - ? `${t("Upload failed", { ns: "common" })}\n\n${(data.errors ?? []).join("\n")}` - : t("Upload failed", { ns: "common" }); + ? `${t("Upload failed", { ns: "importExcel" })}\n\n${(data.errors ?? []).join("\n")}` + : t("Upload failed", { ns: "importExcel" }); alert(errMsg); return; } const updated = data.updated ?? 0; if ((data.errors?.length ?? 0) > 0) { - const successLabel = t("Upload successful", { ns: "common" }); - const countLabel = t("item(s) updated", { ns: "common" }); - const rowErrorsLabel = t("Upload row errors", { ns: "common" }); + const successLabel = t("Upload successful", { ns: "importExcel" }); + const countLabel = t("item(s) updated", { ns: "importExcel" }); + const rowErrorsLabel = t("Upload row errors", { ns: "importExcel" }); const msg = `${successLabel} ${updated} ${countLabel}\n\n${rowErrorsLabel}\n${(data.errors ?? []).join("\n")}`; alert(msg); } else { - alert(t("Upload successful", { ns: "common" })); + alert(t("Upload successful", { ns: "importExcel" })); } if (item) fetchExactItem({ code: item.code ?? "", name: item.name ?? "" }); } catch { - alert(t("Upload failed", { ns: "common" })); + alert(t("Upload failed", { ns: "importExcel" })); } finally { setIsUploading(false); e.target.value = ""; @@ -191,7 +191,7 @@ const ItemPriceSearch: ItemPriceSearchComponent = () => { disabled={isDownloading || isUploading} sx={{ borderColor: "#e2e8f0", color: "#334155" }} > - {isDownloading ? t("Downloading...", { ns: "common" }) : t("Download Template", { ns: "common" })} + {isDownloading ? t("Downloading...", { ns: "importExcel" }) : t("Download Template", { ns: "importExcel" })} diff --git a/src/components/PickOrderSearch/PickQcStockInModalVer3.tsx b/src/components/PickOrderSearch/PickQcStockInModalVer3.tsx index 98c5b86..3385037 100644 --- a/src/components/PickOrderSearch/PickQcStockInModalVer3.tsx +++ b/src/components/PickOrderSearch/PickQcStockInModalVer3.tsx @@ -404,7 +404,7 @@ const PickQcStockInModalVer3: React.FC = ({ if (qcDecision === "1" && !qcData.qcItems.every((q) => q.isPassed)) { submitDialogWithWarning(() => { closeHandler?.({}, 'escapeKeyDown'); - }, t, {title:"有不合格檢查項目,確認接受出庫?", confirmButtonText: "Confirm", html: ""}); + }, t, {title: t("confirm_accept_with_fail"), confirmButtonText: t("Confirm"), html: ""}); return; } @@ -463,7 +463,7 @@ const PickQcStockInModalVer3: React.FC = ({ } - label="合格" + label={t("passed")} sx={{ color: current.qcPassed === true ? "green" : "inherit", "& .Mui-checked": {color: "green"} @@ -472,7 +472,7 @@ const PickQcStockInModalVer3: React.FC = ({ } - label="不合格" + label={t("failed")} sx={{ color: current.qcPassed === false ? "red" : "inherit", "& .Mui-checked": {color: "red"} diff --git a/src/components/PickOrderSearch/QcFormVer2.tsx b/src/components/PickOrderSearch/QcFormVer2.tsx index ebea29d..02cbb12 100644 --- a/src/components/PickOrderSearch/QcFormVer2.tsx +++ b/src/components/PickOrderSearch/QcFormVer2.tsx @@ -216,16 +216,16 @@ const QcFormVer2: React.FC = ({ qc, itemDetail, disabled, qcItems, setQcI } - label="合格" + label={t("passed")} sx={{ color: currentValue === true ? "green" : "inherit", "& .Mui-checked": {color: "green"} }} /> - } - label="不合格" + } + label={t("failed")} sx={{ color: currentValue === false ? "red" : "inherit", "& .Mui-checked": {color: "red"} @@ -419,7 +419,7 @@ const QcFormVer2: React.FC = ({ qc, itemDetail, disabled, qcItems, setQcI field.onChange(value); }} > - } label="接受" /> + } label={t("passed")} /> = ({ qc, itemDetail, disabled, qcItems, setQcI helperText={errors.acceptQty?.message} /> - } label="不接受及上報" /> + } label={t("failed")} /> )} /> diff --git a/src/components/PickOrderSearch/WorkbenchPickExecution.tsx b/src/components/PickOrderSearch/WorkbenchPickExecution.tsx index a419267..134b991 100644 --- a/src/components/PickOrderSearch/WorkbenchPickExecution.tsx +++ b/src/components/PickOrderSearch/WorkbenchPickExecution.tsx @@ -537,7 +537,6 @@ const WorkbenchPickExecution: React.FC = ({ filterArgs }) => { } details = await fetchWorkbenchPickOrderLineDetailV2(pickOrderLineId); list = Array.isArray(details) ? details : []; - setMessage(t("Suggestion success")); } setLotRows( mapLotDetailsToRows(list, { @@ -1591,8 +1590,8 @@ const WorkbenchPickExecution: React.FC = ({ filterArgs }) => { ))} {lotRows.length === 0 ? ( - - + + {t("No lot rows. Select a line in the table above.")} diff --git a/src/components/PoDetail/QrModal.tsx b/src/components/PoDetail/QrModal.tsx index 73279b0..d5f3268 100644 --- a/src/components/PoDetail/QrModal.tsx +++ b/src/components/PoDetail/QrModal.tsx @@ -45,7 +45,7 @@ const style = { width: "auto", }; const QrModal: React.FC = ({ open, onClose, warehouse }) => { - const { t } = useTranslation(); + const { t } = useTranslation("purchaseOrder"); const [serverError, setServerError] = useState(""); const params = useSearchParams(); const formProps = useForm({ diff --git a/src/components/PoSearch/PoSearch.tsx b/src/components/PoSearch/PoSearch.tsx index 770b306..9e26723 100644 --- a/src/components/PoSearch/PoSearch.tsx +++ b/src/components/PoSearch/PoSearch.tsx @@ -8,8 +8,6 @@ import SearchBox, { Criterion } from "../SearchBox"; import SearchResults, { Column } from "../SearchResults"; import { EditNote } from "@mui/icons-material"; import { Backdrop, Button, CircularProgress, Grid, Tab, Tabs, TabsProps, Typography } from "@mui/material"; -import QrModal from "../PoDetail/QrModal"; -import { WarehouseResult } from "@/app/api/warehouse"; import NotificationIcon from "@mui/icons-material/NotificationImportant"; import { useSession } from "next-auth/react"; import { defaultPagingController } from "../SearchResults/SearchResults"; @@ -24,7 +22,6 @@ dayjs.extend(arraySupport); type Props = { po: PoResult[]; - warehouse: WarehouseResult[]; totalCount: number; }; type SearchQuery = Partial>; @@ -34,14 +31,13 @@ type SearchParamNames = keyof SearchQuery; // cal limit (pageSize) const PoSearch: React.FC = ({ po, - warehouse, totalCount: initTotalCount, }) => { const [selectedPoIds, setSelectedPoIds] = useState([]); const [selectAll, setSelectAll] = useState(false); const [filteredPo, setFilteredPo] = useState(po); const [filterArgs, setFilterArgs] = useState>({estimatedArrivalDate : dayjsToDateString(dayjs(), "input")}); - const { t } = useTranslation(["purchaseOrder", "dashboard"]); + const { t } = useTranslation(["purchaseOrder", "common", "dashboard"]); const router = useRouter(); const PO_DETAIL_SELECTION_KEY = "po-detail-selection"; const [pagingController, setPagingController] = useState( @@ -55,8 +51,11 @@ const PoSearch: React.FC = ({ { label: t("Escalated"), paramName: "escalated", - type: "select", - options: [t("Escalated"), t("NotEscalated")], + type: "select-labelled", + options: [ + { label: t("Escalated"), value: "true" }, + { label: t("NotEscalated"), value: "false" }, + ], }, { label: t("Order Date"), label2: t("Order Date To"), paramName: "orderDate", type: "dateRange" }, { @@ -264,17 +263,9 @@ const PoSearch: React.FC = ({ setFilteredPo(po); }, [po]); - const [isOpenScanner, setOpenScanner] = useState(false); const [autoSyncStatus, setAutoSyncStatus] = useState(null); const [isM18LookupLoading, setIsM18LookupLoading] = useState(false); const autoSyncInProgressRef = React.useRef(false); - const onOpenScanner = useCallback(() => { - setOpenScanner(true); - }, []); - - const onCloseScanner = useCallback(() => { - setOpenScanner(false); - }, []); const newPageFetch = useCallback( async ( @@ -404,21 +395,9 @@ const PoSearch: React.FC = ({ }, [filteredPo, selectedPoIds]); return ( <> - - - - {t("Purchase Receipt")} - - - - - - - + + {t("Purchase Receipt")} + <> = ({ escalated: query.escalated === "All" ? undefined - : query.escalated === t("Escalated"), + : query.escalated === "true", estimatedArrivalDate: query.estimatedArrivalDate === "Invalid Date" ? "" : query.estimatedArrivalDate, estimatedArrivalDateTo: query.estimatedArrivalDateTo === "Invalid Date" ? "" : query.estimatedArrivalDateTo, orderDate: query.orderDate === "Invalid Date" ? "" : query.orderDate, diff --git a/src/components/PoSearch/PoSearchWrapper.tsx b/src/components/PoSearch/PoSearchWrapper.tsx index 4abe5c6..de50555 100644 --- a/src/components/PoSearch/PoSearchWrapper.tsx +++ b/src/components/PoSearch/PoSearchWrapper.tsx @@ -10,7 +10,6 @@ import { fetchPoList, PoResult } from "@/app/api/po"; import dayjs from "dayjs"; import arraySupport from "dayjs/plugin/arraySupport"; import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; -import { fetchWarehouseList } from "@/app/api/warehouse"; import { defaultPagingController } from "../SearchResults/SearchResults"; dayjs.extend(arraySupport); @@ -28,14 +27,10 @@ const PoSearchWrapper: React.FC & SubComponents = async ( }, ) => { // console.log(defaultPagingController) - const [po, warehouse] = await Promise.all([ - fetchPoList({ - pageNum: 1, - pageSize: 10, - }), - // fetchPoList(), - fetchWarehouseList(), - ]); + const po = await fetchPoList({ + pageNum: 1, + pageSize: 10, + }); const fixPoDate = po.records.map((p) => { return { ...p, @@ -43,7 +38,7 @@ const PoSearchWrapper: React.FC & SubComponents = async ( }; }); return ( - + ); }; diff --git a/src/components/ProductionProcess/ProductionProcessDetail.tsx b/src/components/ProductionProcess/ProductionProcessDetail.tsx index 059b097..62c4627 100644 --- a/src/components/ProductionProcess/ProductionProcessDetail.tsx +++ b/src/components/ProductionProcess/ProductionProcessDetail.tsx @@ -72,7 +72,7 @@ const ProductionProcessDetail: React.FC = ({ }) => { console.log(" ProductionProcessDetail RENDER", { jobOrderId, fromJosave }); - const { t } = useTranslation("common"); + const { t } = useTranslation(["productionProcess", "common"]); const { data: session } = useSession() as { data: SessionWithTokens | null }; const currentUserId = session?.id ? parseInt(session.id) : undefined; const { values: qrValues, startScan, stopScan, resetScan } = useQrCodeScannerContext(); @@ -148,7 +148,7 @@ const fetchProcessDetailRef = useRef<() => Promise>(); const processesWithLines = await fetchProductProcessesByJobOrderId(jobOrderId); if (!processesWithLines || processesWithLines.length === 0) { - throw new Error("No processes found for this job order"); + throw new Error(t("No processes found for this job order")); } const currentProcess = processesWithLines[0]; diff --git a/src/components/ProductionProcess/ProductionProcessList.tsx b/src/components/ProductionProcess/ProductionProcessList.tsx index ba9c482..f559001 100644 --- a/src/components/ProductionProcess/ProductionProcessList.tsx +++ b/src/components/ProductionProcess/ProductionProcessList.tsx @@ -103,7 +103,7 @@ const ProductProcessList: React.FC = ({ listPersistedState, onListPersistedStateChange, }) => { - const { t } = useTranslation( ["common", "production","purchaseOrder","dashboard"]); + const { t } = useTranslation( ["common", "productionProcess","purchaseOrder","dashboard"]); const { data: session } = useSession() as { data: SessionWithTokens | null }; const sessionToken = session as SessionWithTokens | null; const [loading, setLoading] = useState(false); @@ -402,19 +402,19 @@ const ProductProcessList: React.FC = ({ } as Criterion]), { type: "text", - label: "Item Code", + label: t("Item Code"), paramName: "itemCode", preFilledValue: appliedSearch.itemCode ?? "", }, { type: "text", - label: "Job Order Code", + label: t("Job Order Code"), paramName: "jobOrderCode", preFilledValue: appliedSearch.jobOrderCode ?? "", }, { type: "select", - label: "Type", + label: t("Process Type"), paramName: "processType", options: ["all", "drink", "Powder_Mixture", "other"], preFilledValue: filter, @@ -600,7 +600,7 @@ const ProductProcessList: React.FC = ({ {statusLower == "pending" && ( - {t("t")} + {t("Pending")} diff --git a/src/components/ProductionProcess/ProductionRecordingModal.tsx b/src/components/ProductionProcess/ProductionRecordingModal.tsx index 372f119..b7a4f12 100644 --- a/src/components/ProductionProcess/ProductionRecordingModal.tsx +++ b/src/components/ProductionProcess/ProductionRecordingModal.tsx @@ -1,6 +1,7 @@ "use client"; import React from "react"; import { X, Save } from "@mui/icons-material"; +import { useTranslation } from "react-i18next"; import { useState } from "react"; import { Dialog, @@ -35,6 +36,7 @@ const ProductionRecordingModal: React.FC = ({ onClose, jobProcess, }) => { + const { t } = useTranslation("common"); const { control, handleSubmit, @@ -259,7 +261,7 @@ const ProductionRecordingModal: React.FC = ({ minRows={3} fullWidth size="small" - placeholder="Enter any additional production notes..." + placeholder={t("Enter any additional production notes...")} error={!!errors.notes} helperText={errors.notes?.message} /> diff --git a/src/components/ProductionProcess/QualityCheckModal.tsx b/src/components/ProductionProcess/QualityCheckModal.tsx index d3212e8..70f3e3a 100644 --- a/src/components/ProductionProcess/QualityCheckModal.tsx +++ b/src/components/ProductionProcess/QualityCheckModal.tsx @@ -1,6 +1,7 @@ "use client"; import React from "react"; import { X, Save } from "@mui/icons-material"; +import { useTranslation } from "react-i18next"; import { Dialog, DialogTitle, @@ -30,6 +31,7 @@ const QualityCheckModal: React.FC = ({ onClose, item = null, }) => { + const { t } = useTranslation("common"); const { control, handleSubmit, @@ -306,7 +308,7 @@ const QualityCheckModal: React.FC = ({ outline: "none", resize: "vertical", }} - placeholder="Enter any additional observations or notes..." + placeholder={t("Enter any additional observations or notes...")} /> )} diff --git a/src/components/ProjectSearch/ProjectSearch.tsx b/src/components/ProjectSearch/ProjectSearch.tsx index 903f7cc..90980a9 100644 --- a/src/components/ProjectSearch/ProjectSearch.tsx +++ b/src/components/ProjectSearch/ProjectSearch.tsx @@ -15,7 +15,7 @@ type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; const ProjectSearch: React.FC = ({ projects }) => { - const { t } = useTranslation("projects"); + const { t } = useTranslation("project"); // If project searching is done on the server-side, then no need for this. const [filteredProjects, setFilteredProjects] = useState(projects); diff --git a/src/components/Qc/QcComponent.tsx b/src/components/Qc/QcComponent.tsx index 9dea1b0..3c48e13 100644 --- a/src/components/Qc/QcComponent.tsx +++ b/src/components/Qc/QcComponent.tsx @@ -247,7 +247,7 @@ const fetchNewQcData = useCallback( setValue("qcResult", []); } } else { - throw new Error("QC Template not found"); + throw new Error(t("QC Template not found")); } } catch (e) { console.log("%c Error when fetching Qc Template: ", "color:red", e); diff --git a/src/components/RoughScheduleDetail/DetailInfoCard.tsx b/src/components/RoughScheduleDetail/DetailInfoCard.tsx index 7ef40b6..cdf5d8c 100644 --- a/src/components/RoughScheduleDetail/DetailInfoCard.tsx +++ b/src/components/RoughScheduleDetail/DetailInfoCard.tsx @@ -30,7 +30,7 @@ const DetailInfoCard: React.FC = ({ isEditing }) => { const { t, i18n: { language }, - } = useTranslation(); + } = useTranslation("schedule"); const { control, diff --git a/src/components/SearchBox/SearchBox.tsx b/src/components/SearchBox/SearchBox.tsx index 959ec2e..4533172 100644 --- a/src/components/SearchBox/SearchBox.tsx +++ b/src/components/SearchBox/SearchBox.tsx @@ -301,7 +301,7 @@ function SearchBox({ {c.type === "text" && ( ({ )} */} {c.type === "select" && ( - {t(c.label)} + {c.label} ({ )} /> @@ -468,7 +468,7 @@ function SearchBox({ ({ ({ ({ ({ ({ ({ setCheckboxIds = undefined, onRowClick = undefined, }: Props) { - const { t } = useTranslation(); + const { t } = useTranslation("common"); const [page, setPage] = React.useState(0); const [rowsPerPage, setRowsPerPage] = React.useState(100); diff --git a/src/components/Shop/RouteBoard.tsx b/src/components/Shop/RouteBoard.tsx index dee873e..3313fe0 100644 --- a/src/components/Shop/RouteBoard.tsx +++ b/src/components/Shop/RouteBoard.tsx @@ -12,6 +12,7 @@ import { Box, Badge, Button, + ButtonGroup, Chip, Card, CardContent, @@ -47,6 +48,7 @@ import { alpha } from "@mui/material/styles"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import FilterListIcon from "@mui/icons-material/FilterList"; import { + AlertTriangle, ArrowRight, Bell, Building2, @@ -88,6 +90,7 @@ import { importRouteLanesExcelClient, parseRouteLanesExcelClient, createTruckLaneSnapshotClient, + createTruckLaneScheduleClient, updateTruckLaneVersionNoteClient, updateTruckLaneClient, updateLaneLogisticClient, @@ -120,6 +123,7 @@ import { resolveVersionLogLaneLabel, resolveVersionLogShopHeadline, splitVersionCreated, + resolveVersionActor, summarizeVersionRows, VERSION_LOG_LOADING_SEQUENCE_LABEL, type StagedDeleteMeta, @@ -133,6 +137,26 @@ import { type TruckLaneWarningLaneRef, } from "@/components/Shop/computeTruckLaneWarnings"; import { mergeImportPreviewIntoLanes } from "@/components/Shop/routeBoardImportPreview"; +import ScheduleChangeModal, { + type ScheduleChangePayload, + type ScheduleShopRow, +} from "@/components/Shop/ScheduleChangeModal"; +import ScheduleTaskHistoryModal from "@/components/Shop/ScheduleTaskHistoryModal"; +import { extractApiErrorMessage } from "@/components/Shop/scheduleClientHelpers"; +import { formatScheduleValidationErrors } from "@/components/Shop/scheduleUiHelpers"; +import { useRouteBoardScheduleIndicators } from "@/components/Shop/useRouteBoardScheduleIndicators"; +import { + buildRequestFromPayload, + validatePayloadSubmit, +} from "@/components/Shop/truckLaneMovePlanner"; +import { + computeMovedLoadingSequence, + flattenDisplayOrder, + groupByDistrict, + formatShopCardSubtitle, + toDistrictDisplayName, + toDistrictRawValue, +} from "@/components/Shop/routeBoardDisplayOrder"; const JAVA_INT_MAX = 2_147_483_647; @@ -266,45 +290,6 @@ function resolveLogisticMasterRow( ); } -const groupByDistrict = (shops: ShopCard[]) => { - const map = new Map(); - for (const s of shops) { - const key = toDistrictDisplayName(s.districtReferenceRaw); - const arr = map.get(key) ?? []; - arr.push(s); - map.set(key, arr); - } - // stable order: 未分類 always first, then district ASC, then loadingSequence ASC (duplicates allowed) - return Array.from(map.entries()) - .sort(([a], [b]) => { - if (a === "未分類") return -1; - if (b === "未分類") return 1; - return a.localeCompare(b, "zh-Hant"); - }) - .map(([district, list]) => ({ - district, - shops: list - .slice() - .sort((x, y) => (x.loadingSequence ?? 0) - (y.loadingSequence ?? 0)), - })); -}; - -const flattenDisplayOrder = (shops: ShopCard[]): ShopCard[] => { - return groupByDistrict(shops).flatMap((g) => g.shops); -}; - -const toDistrictDisplayName = (district: string | null | undefined): string => { - const value = String(district ?? "").trim(); - return value === "" ? "未分類" : value; -}; - -const toDistrictRawValue = ( - district: string | null | undefined, -): string | null => { - const value = String(district ?? "").trim(); - return value === "" || value === "未分類" ? null : value; -}; - /** * 地區區塊標題編輯語意(RouteBoard): * 後端每筆 truck row 存 `districtReference`,無 lane-level overlay 表。 @@ -446,7 +431,7 @@ function warningTouchesPickedShop( function formatLaneWarningDetail( L: TruckLaneWarningLaneRef, - tr: TFunction<"routeboard">, + tr: TFunction<"shop">, ): string { const dash = tr("emDash"); const parts = [`${tr("warnClipboardStore")} ${L.storeId}`]; @@ -459,7 +444,7 @@ function formatLaneWarningDetail( function formatWarningSummary( w: TruckLaneWarning, - tr: TFunction<"routeboard">, + tr: TFunction<"shop">, ): string { if (w.rule === "RULE_1_WEEKDAY") { return tr("mtmsRouteWarn_conflict4f", { weekday: w.triggerValue }); @@ -469,7 +454,7 @@ function formatWarningSummary( function formatLaneWarningsClipboard( warnings: TruckLaneWarning[], - tr: TFunction<"routeboard">, + tr: TFunction<"shop">, ): string { return warnings .map((w, idx) => { @@ -490,9 +475,11 @@ function formatLaneWarningsClipboard( .join("\n\n"); } -function formatDiffFieldLabel(label: string, tr: TFunction<"routeboard">): string { - if (label === "物流公司") return tr("diffField_logisticsCompany"); - return label; +function formatDiffFieldLabel(label: string, tr: TFunction<"shop">): string { + if (label === "versionLogField_logisticId") return tr("diffField_logisticsCompany"); + // Translate i18n keys; fallback to raw label if not found + const translated = tr(label as any); + return translated !== label ? translated : label; } function downloadBase64Xlsx(base64: string, filename: string) { @@ -512,6 +499,61 @@ function downloadBase64Xlsx(base64: string, filename: string) { URL.revokeObjectURL(url); } +function sortLanesByCode(lanes: Lane[]): Lane[] { + return lanes.slice().sort((a, b) => { + const c = a.truckLanceCode.localeCompare(b.truckLanceCode, "zh-Hant"); + if (c !== 0) return c; + return String(a.remark ?? "").localeCompare( + String(b.remark ?? ""), + "zh-Hant", + ); + }); +} + +/** 由尚未儲存的新增車線 payload 建立看板 Lane(shops 為空) */ +function buildLaneFromPendingNewLane(p: PendingNewLane): Lane { + const payload = p.payload; + const code = String(payload.truckLanceCode || "").trim(); + const storeNorm = normalizeStoreId(payload.store_id); + const remarkRaw = + storeNorm === "4F" && + payload.remark != null && + String(payload.remark).trim() !== "" + ? String(payload.remark).trim() + : null; + const dep = parseTimeForBackend(payload.departureTime || "") || "00:00:00"; + return { + id: p.laneKey, + truckLanceCode: code, + logisticsCompany: "", + logisticId: + payload.logisticId != null && Number.isFinite(Number(payload.logisticId)) + ? Number(payload.logisticId) + : null, + plate: "", + driver: "", + phone: "", + startTime: dep, + storeId: storeNorm, + remark: remarkRaw, + shops: [], + }; +} + +/** 合併尚未寫入後端的新增車線,避免 loadLanes 覆蓋後從看板消失 */ +function mergePendingNewLanesIntoLanes( + lanes: Lane[], + pending: PendingNewLane[], +): Lane[] { + if (pending.length === 0) return lanes; + const ids = new Set(lanes.map((l) => l.id)); + const additions = pending + .filter((p) => !ids.has(p.laneKey)) + .map((p) => buildLaneFromPendingNewLane(p)); + if (additions.length === 0) return lanes; + return sortLanesByCode([...lanes, ...additions]); +} + /** 合併增量刷新後的車線(維持排序;追加後端新出現的 lane) */ function mergeRefreshedLanes(prev: Lane[], refreshed: Lane[]): Lane[] { const map = new Map(refreshed.map((l) => [l.id, l])); @@ -526,16 +568,7 @@ function mergeRefreshedLanes(prev: Lane[], refreshed: Lane[]): Lane[] { } } const additions = refreshed.filter((l) => !prevIds.has(l.id)); - const merged = [...replaced, ...additions]; - merged.sort((a, b) => { - const c = a.truckLanceCode.localeCompare(b.truckLanceCode, "zh-Hant"); - if (c !== 0) return c; - return String(a.remark ?? "").localeCompare( - String(b.remark ?? ""), - "zh-Hant", - ); - }); - return merged; + return sortLanesByCode([...replaced, ...additions]); } function buildLaneFromTruckRows( @@ -760,7 +793,7 @@ function dedupeShopMasterRows( * Logistic:後端 `truck.logistic` join;車線 Excel 見 MTMS_ROUTE_V1(PDF 圖1)。 */ const RouteBoard: React.FC = () => { - const { t } = useTranslation("routeboard"); + const { t } = useTranslation("shop"); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [laneWarnDrawerOpen, setLaneWarnDrawerOpen] = useState(false); @@ -984,7 +1017,7 @@ const RouteBoard: React.FC = () => { } if (!hasQ) return true; const note = v?.note != null ? String(v.note) : ""; - const editor = v?.modifiedBy != null ? String(v.modifiedBy).trim() : ""; + const editor = resolveVersionActor(v ?? {}); const hay = `${id} ${note} ${editor} ${created}`.toLowerCase(); return hay.includes(q); }); @@ -1004,6 +1037,18 @@ const RouteBoard: React.FC = () => { const [allShopsMaster, setAllShopsMaster] = useState< Array<{ id: number; name: string; code: string }> >([]); + const [scheduleModalOpen, setScheduleModalOpen] = useState(false); + const [scheduleHistoryOpen, setScheduleHistoryOpen] = useState(false); + const scheduleModalOpenRef = useRef(scheduleModalOpen); + scheduleModalOpenRef.current = scheduleModalOpen; + const { + pendingScheduleShopIds, + failedScheduleShopIds, + failedScheduleCount, + refreshScheduleIndicators, + } = useRouteBoardScheduleIndicators({ paused: scheduleModalOpen }); + const scheduledShopIdSet = pendingScheduleShopIds; + const [addShopDialogOpen, setAddShopDialogOpen] = useState(false); const [addShopLaneId, setAddShopLaneId] = useState(null); const [addShopPick, setAddShopPick] = useState<{ @@ -1346,7 +1391,10 @@ const RouteBoard: React.FC = () => { const next = filterStagedDeletedShops( applyLaneDisplayOverlays( enrichLanesWithLogisticMaster( - mergeRefreshedLanes(prev, mergedRefreshed), + mergePendingNewLanesIntoLanes( + mergeRefreshedLanes(prev, mergedRefreshed), + pendingNewLanesRef.current, + ), ), ), dirtyDeletesRef.current, @@ -1437,20 +1485,17 @@ const RouteBoard: React.FC = () => { b.rows, b.meta, ); - }) - .filter((l) => l.shops.length > 0); + }); - loaded.sort((a, b) => { - const c = a.truckLanceCode.localeCompare(b.truckLanceCode, "zh-Hant"); - if (c !== 0) return c; - return String(a.remark ?? "").localeCompare( - String(b.remark ?? ""), - "zh-Hant", - ); - }); + const loadedWithPending = mergePendingNewLanesIntoLanes( + sortLanesByCode(loaded), + pendingNewLanesRef.current, + ); const nextBoard = filterStagedDeletedShops( - applyLaneDisplayOverlays(enrichLanesWithLogisticMaster(loaded)), + applyLaneDisplayOverlays( + enrichLanesWithLogisticMaster(loadedWithPending), + ), dirtyDeletesRef.current, ); lanesRef.current = nextBoard; @@ -1958,10 +2003,10 @@ const RouteBoard: React.FC = () => { logisticRowsFromDb, ]); - /** 依左欄篩選後的車線,再依物流公司名稱(含 overlay)分組;併入 GET /logistic/all 有、但尚未掛車線的公司 */ + /** 依樓層(不含車線下拉搜尋關鍵字)分組;併入 GET /logistic/all 有、但尚未掛車線的公司 */ const lanesByLogisticsCompany = useMemo(() => { const map = new Map(); - for (const lane of visibleLaneOptions) { + for (const lane of lanesMatchingFloorOnly) { if ( selectedLaneIds.length > 0 && !selectedLaneIds.includes(lane.id) @@ -1985,7 +2030,7 @@ const RouteBoard: React.FC = () => { return a[0].localeCompare(b[0], "zh-Hant"); }); return entries; - }, [visibleLaneOptions, logisticNamesEffective, selectedLaneIds]); + }, [lanesMatchingFloorOnly, logisticNamesEffective, selectedLaneIds]); const addShopCandidates = useMemo(() => { if (!addShopLaneId) return []; @@ -2275,21 +2320,16 @@ const RouteBoard: React.FC = () => { : null, ); setLanes((prev) => { - const next = [...prev, draftLane]; - next.sort((a, b) => { - const c = a.truckLanceCode.localeCompare(b.truckLanceCode, "zh-Hant"); - if (c !== 0) return c; - return String(a.remark ?? "").localeCompare( - String(b.remark ?? ""), - "zh-Hant", - ); - }); + const next = sortLanesByCode([...prev, draftLane]); return applyLaneDisplayOverlays(enrichLanesWithLogisticMaster(next)); }); closeAddRouteDialog(); setSelectedLaneIds((prev) => prev.includes(laneKey) ? prev : [...prev, laneKey], ); + requestAnimationFrame(() => { + requestAnimationFrame(() => scrollBoardLaneCardIntoView(laneKey)); + }); setSaveResult(null); } catch (e: any) { setAddRouteError(e?.message ?? String(e) ?? t("route_err_create")); @@ -2515,33 +2555,6 @@ const RouteBoard: React.FC = () => { setDropIndicator(null); }; - const computeMovedLoadingSequence = ( - inserted: ShopCard[], - movedId: number, - preferPrevious = false, - ): number => { - const idx = inserted.findIndex((s) => s.id === movedId); - if (idx < 0) return 0; - if (preferPrevious) { - const prev = inserted[idx - 1]; - if (prev && typeof prev.loadingSequence === "number") - return prev.loadingSequence; - } - // If inserted before some shop, join that shop's sequence group. - const next = inserted[idx + 1]; - if (next && typeof next.loadingSequence === "number") - return next.loadingSequence; - // Otherwise append: keep current group (if any), else use max existing. - const prev = inserted[idx - 1]; - if (prev && typeof prev.loadingSequence === "number") - return prev.loadingSequence; - const max = inserted.reduce( - (m, s) => Math.max(m, Number(s.loadingSequence ?? 0) || 0), - 0, - ); - return max; - }; - const getBeforeShopIdByPointer = ( laneId: string, clientY: number, @@ -2743,6 +2756,112 @@ const RouteBoard: React.FC = () => { clearDragState(); }; + const scheduleLaneOptions = useMemo( + () => + lanes.map((lane) => ({ + id: lane.id, + label: formatLaneLabel(lane.truckLanceCode, lane.remark), + truckLanceCode: lane.truckLanceCode, + remark: lane.remark, + storeId: normalizeStoreId(lane.storeId), + shops: lane.shops + .filter((s) => s.id >= 0) + .map((s) => ({ + truckRowId: s.id, + districtReferenceRaw: s.districtReferenceRaw ?? null, + loadingSequence: s.loadingSequence ?? 0, + })), + })), + [lanes], + ); + + const scheduleShopRows = useMemo((): ScheduleShopRow[] => { + const rows: ScheduleShopRow[] = []; + for (const lane of lanes) { + const currentLaneLabel = formatLaneLabel( + lane.truckLanceCode, + lane.remark, + ); + for (const shop of lane.shops) { + if (shop.id < 0) continue; + const codeLower = String(shop.shopCode || "") + .trim() + .toLowerCase(); + const realName = shopNameByCodeMap.get(codeLower); + const displayName = + realName && String(realName).trim() !== "" + ? String(realName).trim() + : String(shop.branchName || "").trim() || "-"; + const district = toDistrictDisplayName(shop.districtReferenceRaw); + const location = [ + district, + shop.shopCode || "-", + shop.storeId ? normalizeStoreId(shop.storeId) : "", + ] + .filter(Boolean) + .join(" · "); + rows.push({ + truckRowId: shop.id, + shopCode: String(shop.shopCode || "").trim() || "-", + displayName, + branchName: String(shop.branchName || "").trim(), + storeId: shop.storeId ? normalizeStoreId(shop.storeId) : "", + location, + districtDisplay: district, + districtReferenceRaw: shop.districtReferenceRaw ?? null, + currentLaneId: lane.id, + currentLaneLabel, + }); + } + } + return rows.sort((a, b) => + a.displayName.localeCompare(b.displayName, "zh-Hant"), + ); + }, [lanes, shopNameByCodeMap]); + + const handleScheduleConfirmManual = useCallback( + async (payload: ScheduleChangePayload) => { + const validation = validatePayloadSubmit({ + payload, + lanes: scheduleLaneOptions, + shops: scheduleShopRows, + pendingTruckRowIds: pendingScheduleShopIds, + }); + if (!validation.ok) { + setError(formatScheduleValidationErrors(t, validation.errors)); + return; + } + const request = buildRequestFromPayload(payload, scheduleLaneOptions); + if (!request || (request.lines?.length ?? 0) === 0) { + setError(t("schedule_err_conflict")); + return; + } + try { + await createTruckLaneScheduleClient(request); + setLaneWarnSnackbar( + t("schedule_registered_snackbar", { count: request.lines?.length ?? 0 }), + ); + } catch (err: unknown) { + setError(extractApiErrorMessage(err) ?? t("schedule_err_generic")); + throw err; + } + }, + [t, scheduleLaneOptions, scheduleShopRows, pendingScheduleShopIds], + ); + + + const prevPendingScheduleSizeRef = useRef(0); + useEffect(() => { + const n = pendingScheduleShopIds.size; + if ( + prevPendingScheduleSizeRef.current > 0 && + n < prevPendingScheduleSizeRef.current + ) { + void loadLanes(); + } + prevPendingScheduleSizeRef.current = n; + }, [pendingScheduleShopIds]); + const handleDragOver: React.DragEventHandler = (e) => { e.preventDefault(); }; @@ -4118,24 +4237,33 @@ const RouteBoard: React.FC = () => { style={{ display: "none" }} onChange={handleImportRouteExcelChange} /> - - + + + + + + 0 + ? t("schedule_log_failed_hint", { count: failedScheduleCount }) + : "" + } + > + + + 0 @@ -4390,6 +4570,31 @@ const RouteBoard: React.FC = () => { + {scheduleModalOpen ? ( + setScheduleModalOpen(false)} + lanes={scheduleLaneOptions} + shops={scheduleShopRows} + pendingTruckRowIds={pendingScheduleShopIds} + onConfirmManual={handleScheduleConfirmManual} + onAfterScheduleChange={async () => { + await refreshScheduleIndicators(); + }} + /> + ) : null} + {scheduleHistoryOpen ? ( + setScheduleHistoryOpen(false)} + lanes={scheduleLaneOptions} + onAfterChange={async () => { + await refreshScheduleIndicators(); + await loadLanes(); + }} + /> + ) : null} + { > {t("version_ui_id", { id })} - {v?.modifiedBy != null && - String(v.modifiedBy).trim() !== "" && ( + {(() => { + const actor = resolveVersionActor(v ?? {}); + return actor ? ( {t("version_ui_editedBy", { - name: String(v.modifiedBy).trim(), + name: actor, })} - )} + ) : null; + })()} e.stopPropagation()} onClick={(e) => e.stopPropagation()} @@ -4763,11 +4970,7 @@ const RouteBoard: React.FC = () => { idx >= 0 && idx < logVersions.length - 1; const sel = logVersions[idx]; const note = sel?.note != null ? String(sel.note) : ""; - const editor = - sel?.modifiedBy != null && - String(sel.modifiedBy).trim() !== "" - ? String(sel.modifiedBy).trim() - : null; + const editor = sel ? resolveVersionActor(sel) : null; return ( <> @@ -4963,21 +5166,15 @@ const RouteBoard: React.FC = () => { - {hasOlder && ( + {hasOlder && stagedLogEntriesView.length > 0 && ( - {t("diff_staged_serverCountsOnly")} - {stagedLogEntriesView.length > 0 && ( - <> - {" · "} - {t("diff_staged_boardPendingLine", { - count: stagedLogEntriesView.length, - })} - - )} + {t("diff_staged_boardPendingLine", { + count: stagedLogEntriesView.length, + })} )} @@ -5403,7 +5600,7 @@ const RouteBoard: React.FC = () => { (fe, fei) => { const isLogistic = fe.label === - "物流公司"; + "versionLogField_logisticId"; const resolveLogisticDisplay = (raw: string) => { const s = String( @@ -6268,7 +6465,13 @@ const RouteBoard: React.FC = () => { fullWidth renderValue={(selected) => { const arr = selected as string[]; - if (arr.length === 0) return t("lane_selectedNone"); + if (arr.length === 0) { + return ( + + {t("lane_selectedNone")} + + ); + } if (filterActive) return t("lane_selectedCount", { count: selectedVisible, @@ -6404,10 +6607,7 @@ const RouteBoard: React.FC = () => { - {visibleLaneOptions.length === 0 ? ( - {t("lane_noMatchFilter")} - ) : ( - visibleLaneOptions.map((lane) => { + {visibleLaneOptions.map((lane) => { const checked = selectedLaneIds.includes(lane.id); const rem = lane.remark != null && @@ -6456,8 +6656,7 @@ const RouteBoard: React.FC = () => { /> ); - }) - )} + })} { sx={{ mr: 1, display: "inline-flex", + alignItems: "center", color: "text.secondary", }} > ), + sx: { + alignItems: "center", + }, + }} + sx={{ + "& .MuiInputBase-root": { + alignItems: "center", + }, + "& .MuiInputBase-input": { + textAlign: "left", + py: 0.75, + }, + "& .MuiInputBase-input::placeholder": { + opacity: 1, + color: "text.disabled", + textAlign: "left", + }, }} /> + + + + {tab === "manual" ? ( + + + + + + + + + ) : ( + + { + const f = e.target.files?.[0]; + if (f) void handleImportFile(f); + }} + /> + + + + {t("schedule_import_title")} + + + {t("schedule_import_hint")} + + + {importFileName && ( + + {importFileName} + + )} + + {importError && ( + setImportError(null)}> + {importError} + + )} + {stagedLines.length > 0 && + (!scheduledDate.trim() || !scheduledTime.trim()) && ( + + {t("schedule_import_need_datetime")} + + )} + {routeImportMeta && stagedLines.length > 0 && ( + 0 ? "warning" : "success"} + > + {t("schedule_import_plan_summary", { + file: importFileName || "—", + sheets: routeImportMeta.sheetCount, + rows: routeImportMeta.rowCount, + moves: stagedPlanCounts?.moves ?? 0, + creates: stagedPlanCounts?.creates ?? 0, + deletes: stagedPlanCounts?.deletes ?? 0, + ensureLanes: stagedPlanCounts?.ensureLanes ?? 0, + errors: stagedRowErrors.length, + })} + + )} + {stagedPreviews.length > 0 && ( + + + + {t("schedule_import_col_action")} + + + {t("schedule_import_col_shop")} + + + {t("schedule_import_col_name")} + + + {t("schedule_import_col_lane")} + + + {t("schedule_import_col_seq")} + + + + {stagedPreviews.map((row, idx) => ( + alpha(theme.palette.warning.main, 0.08) + : undefined, + "&:last-child": { borderBottom: 0 }, + }} + > + + + {row.shopCode ?? "—"} + + + {row.shopName ?? "—"} + + + {formatScheduleTargetLaneLabel( + row.toTruckLanceCode, + row.toRemark, + )} + + + {row.toLoadingSequence ?? "—"} + + + ))} + + + )} + {stagedRowErrors.length > 0 && ( + + + {t("schedule_import_skipped_title", { + count: stagedRowErrors.length, + })} + + {stagedRowErrors.map((e, i) => ( + + {t("schedule_import_row_error", { + shop: e.shopCode, + name: e.shopName, + reason: resolveImportRowError(e), + })} + + ))} + + )} + + )} + + + {(submitError || plannedValidationMessage) && ( + setSubmitError(null) + : undefined + } + > + {submitError ?? plannedValidationMessage} + + )} + + {tab === "manual" && + (modifications.length > 0 || pendingDeletes.length > 0) && ( + } + sx={{ + bgcolor: alpha("#ed6c02", 0.08), + border: 1, + borderColor: "warning.light", + }} + > + + {t("schedule_summary_changes", { + count: modifications.length + pendingDeletes.length, + })} + + {scheduledDate && scheduledTime && ( + + {t("schedule_planned_label")}: {scheduledDate} {scheduledTime} + + )} + + )} + + + + + {tab === "manual" ? ( + + ) : ( + + )} + + + + ); +}; + +export default memo(ScheduleChangeModal); diff --git a/src/components/Shop/ScheduleDragWorkspacePane.tsx b/src/components/Shop/ScheduleDragWorkspacePane.tsx new file mode 100644 index 0000000..0ce17d0 --- /dev/null +++ b/src/components/Shop/ScheduleDragWorkspacePane.tsx @@ -0,0 +1,970 @@ +"use client"; + +import React, { memo, useCallback, useRef, useState } from "react"; +import { + Box, + Button, + Chip, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, + Paper, + Stack, + TextField, + Tooltip, + Typography, +} from "@mui/material"; +import { alpha } from "@mui/material/styles"; +import { MapPin, Move, Pencil, Plus, Trash2, Undo2, X } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { + PlannedLane, + PlannedShop, + ScheduleDragWorkspaceState, +} from "@/components/Shop/scheduleDragWorkspace"; +import type { ScheduleLaneOption } from "@/components/Shop/ScheduleChangeModal"; +import { + buildLaneDistrictSections, + districtDisplayExistsInShops, + formatShopCardSubtitle, +} from "@/components/Shop/routeBoardDisplayOrder"; + +type Props = { + lanes: ScheduleLaneOption[]; + leftLaneId: string; + rightLaneId: string; + plannedLanes: ScheduleDragWorkspaceState; + pendingEmptyDistrictsByLane: Record; + onLeftLaneChange: (laneId: string) => void; + onRightLaneChange: (laneId: string) => void; + onMoveShop: ( + shopId: number, + fromLaneId: string, + toLaneId: string, + beforeShopId: number | null, + targetDistrict?: string | null, + ) => void; + onRevertShop: (shopId: number, currentLaneId: string) => void; + onSetLoadingSequence: ( + laneId: string, + shopId: number, + loadingSequence: number, + ) => void; + onAddEmptyDistrict: (laneId: string, display: string) => void; + onRemoveEmptyDistrict: (laneId: string, display: string) => void; + onDeleteShop: (shopId: number, laneId: string) => void; +}; + +function getBeforeShopIdByPointer( + laneId: string, + clientY: number, +): number | null { + const laneEl = document.querySelector( + `[data-schedule-lane-id="${CSS.escape(laneId)}"]`, + ); + if (!laneEl) return null; + const cards = Array.from( + laneEl.querySelectorAll("[data-schedule-shop-id]"), + ); + for (const cardEl of cards) { + const rect = cardEl.getBoundingClientRect(); + const midY = rect.top + rect.height / 2; + if (clientY < midY) { + const idStr = cardEl.getAttribute("data-schedule-shop-id"); + const id = idStr ? Number(idStr) : NaN; + return Number.isFinite(id) ? id : null; + } + } + return null; +} + +const nativeSelectSx = { + fontSize: "0.75rem", + fontWeight: 600, + py: 0.5, + px: 1, + borderRadius: 1, + border: "1px solid", + borderColor: "divider", + bgcolor: "background.paper", + flex: 1, + minWidth: 0, + width: "100%", + maxWidth: "100%", +} as const; + +const laneSelectorLabelSx = { + fontWeight: 700, + color: "text.secondary", + flexShrink: 0, + minWidth: "4.5em", + whiteSpace: "nowrap", +} as const; + +type SeqEditTarget = { + laneId: string; + shopId: number; + draft: string; +}; + +const LoadingSequenceDisplay = memo(function LoadingSequenceDisplay({ + loadingSequence, + seqLabel, + editAriaLabel, + onEdit, +}: { + loadingSequence: number; + seqLabel: (seq: number) => string; + editAriaLabel: string; + onEdit: () => void; +}) { + return ( + + + e.stopPropagation()} + onClick={(e) => { + e.stopPropagation(); + onEdit(); + }} + sx={{ + width: 22, + height: 22, + color: "primary.main", + flexShrink: 0, + }} + > + + + + ); +}); + +const DistrictSectionHeader = memo(function DistrictSectionHeader({ + district, + count, + isPendingEmpty, + editAriaLabel, + removeAriaLabel, + onRemove, +}: { + district: string; + count: number; + isPendingEmpty?: boolean; + editAriaLabel?: string; + removeAriaLabel?: string; + onRemove?: () => void; +}) { + return ( + + + + {district} + + + + {count} + + {isPendingEmpty && onRemove && ( + e.stopPropagation()} + onClick={(e) => { + e.stopPropagation(); + onRemove(); + }} + aria-label={removeAriaLabel ?? editAriaLabel} + > + + + )} + + ); +}); + +const PlannedShopCard = memo(function PlannedShopCard({ + shop, + laneId, + isMoved, + isHovered, + hoveredPosition, + movedBadgeLabel, + seqLabel, + seqEditAriaLabel, + onDragStartShop, + onDragOverShop, + onDropShop, + onRevertShop, + onDeleteShop, + removeFromLaneTooltip, + onStartSeqEdit, +}: { + shop: PlannedShop; + laneId: string; + isMoved: boolean; + isHovered: boolean; + hoveredPosition: "before" | "after"; + movedBadgeLabel: string; + seqLabel: (seq: number) => string; + seqEditAriaLabel: string; + removeFromLaneTooltip: string; + onDragStartShop: (shopId: number) => void; + onDragOverShop: (e: React.DragEvent, shopId: number) => void; + onDropShop: (e: React.DragEvent, shopId: number) => void; + onRevertShop: (shopId: number) => void; + onDeleteShop: (shopId: number, laneId: string) => void; + onStartSeqEdit: (laneId: string, shopId: number, current: number) => void; +}) { + return ( + + {isHovered && hoveredPosition === "before" && ( + + )} + onDragStartShop(shop.truckRowId)} + onDragOver={(e) => onDragOverShop(e, shop.truckRowId)} + onDrop={(e) => onDropShop(e, shop.truckRowId)} + sx={{ + p: 1.25, + cursor: "grab", + bgcolor: isMoved ? alpha("#ed6c02", 0.06) : "background.paper", + borderColor: isHovered + ? "primary.main" + : isMoved + ? "warning.light" + : "divider", + borderStyle: isHovered ? "dashed" : "solid", + "&:active": { cursor: "grabbing" }, + }} + > + + + + {shop.displayName} + + + {formatShopCardSubtitle(shop)} + + + + {isMoved && ( + e.stopPropagation()} + onClick={(e) => { + e.stopPropagation(); + onRevertShop(shop.truckRowId); + }} + sx={{ color: "warning.dark" }} + aria-label="revert" + > + + + )} + + + e.stopPropagation()} + onClick={(e) => { + e.stopPropagation(); + onDeleteShop(shop.truckRowId, laneId); + }} + sx={{ color: "error.main" }} + aria-label={removeFromLaneTooltip} + > + + + + + + + + + onStartSeqEdit(laneId, shop.truckRowId, shop.loadingSequence) + } + /> + {isMoved && ( + + )} + + + {isHovered && hoveredPosition === "after" && ( + + )} + + ); +}); + +const LaneColumn = memo(function LaneColumn({ + lane, + pendingEmptyDistricts, + isOver, + onDragOverColumn, + onDragLeaveColumn, + onDropColumn, + onDropDistrict, + onDragStartShop, + onDragOverShop, + onDropShop, + onRevertShop, + onDeleteShop, + onAddDistrict, + onRemoveEmptyDistrict, + hoveredShopId, + hoveredPosition, + movedBadgeLabel, + seqLabel, + seqEditAriaLabel, + removeFromLaneTooltip, + removeEmptyDistrictAriaLabel, + onStartSeqEdit, + dropHint, + addDistrictLabel, +}: { + lane: PlannedLane; + pendingEmptyDistricts: string[]; + isOver: boolean; + onDragOverColumn: (e: React.DragEvent) => void; + onDragLeaveColumn: () => void; + onDropColumn: (e: React.DragEvent) => void; + onDropDistrict: (e: React.DragEvent, district: string) => void; + onDragStartShop: (shopId: number) => void; + onDragOverShop: (e: React.DragEvent, shopId: number) => void; + onDropShop: (e: React.DragEvent, shopId: number) => void; + onRevertShop: (shopId: number) => void; + onDeleteShop: (shopId: number, laneId: string) => void; + onAddDistrict: () => void; + onRemoveEmptyDistrict: (district: string) => void; + removeFromLaneTooltip: string; + hoveredShopId: number | null; + hoveredPosition: "before" | "after"; + movedBadgeLabel: string; + seqLabel: (seq: number) => string; + seqEditAriaLabel: string; + removeEmptyDistrictAriaLabel: string; + onStartSeqEdit: (laneId: string, shopId: number, current: number) => void; + dropHint: string; + addDistrictLabel: string; +}) { + const districtSections = buildLaneDistrictSections( + lane.shops, + pendingEmptyDistricts, + ); + return ( + + + + + + {lane.label} + + + + + + + + + {lane.shops.length === 0 && districtSections.length === 0 ? ( + + + + + {dropHint} + + + + + ) : ( + + {districtSections.map(({ district, shops, isPendingEmpty }) => ( + { + e.preventDefault(); + onDragOverColumn(e); + }} + onDrop={(e) => onDropDistrict(e, district)} + > + onRemoveEmptyDistrict(district) + : undefined + } + /> + + {shops.map((shop) => ( + + ))} + + + ))} + + + )} + + + ); +}); + +const ScheduleDragWorkspacePane: React.FC = ({ + lanes, + leftLaneId, + rightLaneId, + plannedLanes, + pendingEmptyDistrictsByLane, + onLeftLaneChange, + onRightLaneChange, + onMoveShop, + onRevertShop, + onSetLoadingSequence, + onAddEmptyDistrict, + onRemoveEmptyDistrict, + onDeleteShop, +}) => { + const { t } = useTranslation("shop"); + const draggedRef = useRef<{ shopId: number; fromLaneId: string } | null>( + null, + ); + const [isOverColumnId, setIsOverColumnId] = useState(null); + const [hoveredShopId, setHoveredShopId] = useState(null); + const [hoveredPosition, setHoveredPosition] = useState<"before" | "after">( + "after", + ); + const [seqEditTarget, setSeqEditTarget] = useState( + null, + ); + const [districtAddLaneId, setDistrictAddLaneId] = useState( + null, + ); + const [districtAddDraft, setDistrictAddDraft] = useState(""); + const [districtAddError, setDistrictAddError] = useState( + null, + ); + + const leftLane = plannedLanes.find((l) => l.id === leftLaneId); + const rightLane = plannedLanes.find((l) => l.id === rightLaneId); + + const clearDrag = useCallback(() => { + draggedRef.current = null; + setIsOverColumnId(null); + setHoveredShopId(null); + }, []); + + const handleDragStart = (shopId: number, fromLaneId: string) => { + draggedRef.current = { shopId, fromLaneId }; + }; + + const handleDragOverShop = (e: React.DragEvent, shopId: number) => { + e.preventDefault(); + e.stopPropagation(); + const rect = (e.currentTarget as HTMLElement).getBoundingClientRect(); + const offset = e.clientY - rect.top; + setHoveredShopId(shopId); + setHoveredPosition(offset < rect.height / 2 ? "before" : "after"); + }; + + const handleDropOnShop = ( + e: React.DragEvent, + targetLaneId: string, + targetShopId: number, + ) => { + e.preventDefault(); + e.stopPropagation(); + const dragged = draggedRef.current; + if (!dragged) return; + const lane = plannedLanes.find((l) => l.id === targetLaneId); + const flat = lane?.shops ?? []; + let beforeShopId: number | null; + if (hoveredShopId === targetShopId && hoveredPosition === "before") { + beforeShopId = targetShopId; + } else if (hoveredShopId === targetShopId && hoveredPosition === "after") { + const idx = flat.findIndex((s) => s.truckRowId === targetShopId); + beforeShopId = + idx >= 0 && idx < flat.length - 1 + ? (flat[idx + 1]?.truckRowId ?? null) + : null; + } else { + beforeShopId = targetShopId; + } + onMoveShop( + dragged.shopId, + dragged.fromLaneId, + targetLaneId, + beforeShopId, + ); + clearDrag(); + }; + + const handleDropOnColumn = (e: React.DragEvent, targetLaneId: string) => { + e.preventDefault(); + const dragged = draggedRef.current; + if (!dragged) return; + const beforeShopId = getBeforeShopIdByPointer(targetLaneId, e.clientY); + onMoveShop( + dragged.shopId, + dragged.fromLaneId, + targetLaneId, + beforeShopId, + ); + clearDrag(); + }; + + const handleDropOnDistrict = useCallback( + (e: React.DragEvent, targetLaneId: string, district: string) => { + e.preventDefault(); + e.stopPropagation(); + const dragged = draggedRef.current; + if (!dragged) return; + onMoveShop( + dragged.shopId, + dragged.fromLaneId, + targetLaneId, + null, + district, + ); + clearDrag(); + }, + [onMoveShop, clearDrag], + ); + + const renderLaneSelectors = ( + + + + {t("schedule_lane_left")} + + ) => + onLeftLaneChange(e.target.value) + } + sx={nativeSelectSx} + > + {lanes.map((l) => ( + + ))} + + + + + {t("schedule_lane_right")} + + ) => + onRightLaneChange(e.target.value) + } + sx={nativeSelectSx} + > + {lanes.map((l) => ( + + ))} + + + + ); + + const seqLabel = (seq: number) => + t("schedule_drag_seq", { seq: String(seq) }); + + const handleStartSeqEdit = useCallback( + (laneId: string, shopId: number, current: number) => { + setSeqEditTarget({ + laneId, + shopId, + draft: String(current), + }); + }, + [], + ); + + const handleCommitSeqEdit = useCallback(() => { + setSeqEditTarget((prev) => { + if (!prev) return null; + const n = Number(prev.draft); + const seq = Number.isFinite(n) ? Math.max(0, Math.trunc(n)) : 0; + onSetLoadingSequence(prev.laneId, prev.shopId, seq); + return null; + }); + }, [onSetLoadingSequence]); + + const handleCancelSeqEdit = useCallback(() => { + setSeqEditTarget(null); + }, []); + + const openDistrictAdd = useCallback((laneId: string) => { + setDistrictAddLaneId(laneId); + setDistrictAddDraft(""); + setDistrictAddError(null); + }, []); + + const closeDistrictAdd = useCallback(() => { + setDistrictAddLaneId(null); + setDistrictAddDraft(""); + setDistrictAddError(null); + }, []); + + const applyDistrictAdd = useCallback(() => { + if (!districtAddLaneId) return; + const lane = plannedLanes.find((l) => l.id === districtAddLaneId); + if (!lane) { + closeDistrictAdd(); + return; + } + const trimmed = districtAddDraft.trim(); + if (!trimmed) { + setDistrictAddError(t("district_err_name")); + return; + } + if (trimmed === "未分類") { + setDistrictAddError(t("district_err_reserved")); + return; + } + const pendingExtra = pendingEmptyDistrictsByLane[districtAddLaneId] ?? []; + if (districtDisplayExistsInShops(lane.shops, pendingExtra, trimmed)) { + setDistrictAddError(t("district_err_exists")); + return; + } + onAddEmptyDistrict(districtAddLaneId, trimmed); + closeDistrictAdd(); + }, [ + closeDistrictAdd, + districtAddDraft, + districtAddLaneId, + onAddEmptyDistrict, + pendingEmptyDistrictsByLane, + plannedLanes, + t, + ]); + + if (!leftLane || !rightLane) { + return ( + + {renderLaneSelectors} + + {t("schedule_no_shops")} + + + ); + } + + const columnProps = (lane: PlannedLane) => ({ + lane, + pendingEmptyDistricts: pendingEmptyDistrictsByLane[lane.id] ?? [], + isOver: isOverColumnId === lane.id, + onDragOverColumn: (e: React.DragEvent) => { + e.preventDefault(); + setIsOverColumnId(lane.id); + }, + onDragLeaveColumn: () => setIsOverColumnId(null), + onDropColumn: (e: React.DragEvent) => handleDropOnColumn(e, lane.id), + onDropDistrict: (e: React.DragEvent, district: string) => + handleDropOnDistrict(e, lane.id, district), + onDragStartShop: (shopId: number) => handleDragStart(shopId, lane.id), + onDragOverShop: handleDragOverShop, + onDropShop: (e: React.DragEvent, shopId: number) => + handleDropOnShop(e, lane.id, shopId), + onRevertShop: (shopId: number) => onRevertShop(shopId, lane.id), + onDeleteShop, + removeFromLaneTooltip: t("tooltip_removeFromLane"), + onAddDistrict: () => openDistrictAdd(lane.id), + onRemoveEmptyDistrict: (district: string) => + onRemoveEmptyDistrict(lane.id, district), + hoveredShopId, + hoveredPosition, + movedBadgeLabel: t("schedule_moved_badge"), + seqLabel, + seqEditAriaLabel: t("schedule_seq_edit_btn"), + removeEmptyDistrictAriaLabel: t("aria_removeEmptyDistrict"), + onStartSeqEdit: handleStartSeqEdit, + dropHint: t("schedule_drop_hint"), + addDistrictLabel: t("btn_addDistrict"), + }); + + return ( + <> + + {renderLaneSelectors} + + + + + + + + {t("seqDialog_title")} + + + setSeqEditTarget((prev) => + prev ? { ...prev, draft: e.target.value } : prev, + ) + } + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + handleCommitSeqEdit(); + } + }} + inputProps={{ step: 1, min: 0 }} + sx={{ mt: 1 }} + /> + + {t("schedule_seq_dialog_hint")} + + + + + + + + + + {t("district_dialog_add")} + + { + setDistrictAddDraft(e.target.value); + setDistrictAddError(null); + }} + error={Boolean(districtAddError)} + helperText={districtAddError ?? undefined} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + applyDistrictAdd(); + } + }} + sx={{ mt: 1 }} + /> + + + + + + + + ); +}; + +export default memo(ScheduleDragWorkspacePane); diff --git a/src/components/Shop/ScheduleReviewQueue.tsx b/src/components/Shop/ScheduleReviewQueue.tsx new file mode 100644 index 0000000..5174c03 --- /dev/null +++ b/src/components/Shop/ScheduleReviewQueue.tsx @@ -0,0 +1,349 @@ +"use client"; + +import React, { memo } from "react"; +import { + Box, + Button, + Chip, + Paper, + Stack, + Typography, +} from "@mui/material"; +import { alpha } from "@mui/material/styles"; +import { ArrowRight, Clock, HelpCircle, Trash2, Undo2 } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { + ScheduleModification, + ScheduledDeleteSnapshot, +} from "@/components/Shop/scheduleDragWorkspace"; + +type Props = { + modifications: ScheduleModification[]; + pendingDeletes: ScheduledDeleteSnapshot[]; + onRevert: (shopId: number, currentLaneId: string) => void; + onRevertDelete: (truckRowId: number) => void; + errorTruckRowIds?: Set; + validationErrorsByTruckRowId?: Map; +}; + +const ScheduleReviewQueue: React.FC = ({ + modifications, + pendingDeletes, + onRevert, + onRevertDelete, + errorTruckRowIds = new Set(), + validationErrorsByTruckRowId = new Map(), +}) => { + const { t } = useTranslation("shop"); + const totalCount = modifications.length + pendingDeletes.length; + const isEmpty = totalCount === 0; + + return ( + + + + + + {t("schedule_review_queue")} + + + + + + + {isEmpty ? ( + + + + {t("schedule_review_empty")} + + + {t("schedule_review_empty_hint")} + + + ) : ( + + {pendingDeletes.map((del) => ( + + + + + + + {del.shopCode} + + + + {del.displayName} + + + + + + + + + {t("schedule_review_delete_action")} + + + {del.fromLaneLabel} + + + {del.location && ( + + {del.location} + + )} + + + ))} + + {modifications.map((mod) => { + const hasError = errorTruckRowIds.has(mod.truckRowId); + const errorMessage = validationErrorsByTruckRowId.get(mod.truckRowId); + return ( + + + + + {mod.shopCode} + + + {mod.displayName} + + + + + + + {hasError && errorMessage && ( + + {errorMessage} + + )} + {mod.isLaneChanged && ( + + + {t("schedule_review_lane_change")} + + + {mod.fromLaneLabel} + + + + {mod.toLaneLabel} + + + )} + {mod.isDistrictChanged && ( + + + {t("schedule_review_district_change")} + + + {mod.oldDistrictLabel} + + + + {mod.newDistrictLabel} + + + )} + + + {t("schedule_review_seq")} + + {mod.isSeqChanged ? ( + <> + + {mod.oldLoadingSequence} + + + + {mod.newLoadingSequence} + + + ) : ( + + {mod.newLoadingSequence} + + )} + + + + ); + })} + + )} + + + ); +}; + +export default memo(ScheduleReviewQueue); diff --git a/src/components/Shop/ScheduleTaskHistoryModal.tsx b/src/components/Shop/ScheduleTaskHistoryModal.tsx new file mode 100644 index 0000000..2c5da4a --- /dev/null +++ b/src/components/Shop/ScheduleTaskHistoryModal.tsx @@ -0,0 +1,995 @@ +"use client"; + +import React, { memo, useCallback, useEffect, useMemo, useState } from "react"; +import { + Alert, + Box, + Button, + Chip, + CircularProgress, + Collapse, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, + Stack, + Typography, +} from "@mui/material"; +import { + AlertCircle, + AlertTriangle, + Ban, + Calendar, + Check, + CheckCircle2, + ChevronDown, + ChevronUp, + ClipboardList, + Clock, + FileText, + EyeOff, + Play, + RotateCcw, + X, +} from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { + applyNowTruckLaneScheduleClient, + cancelTruckLaneScheduleClient, + createTruckLaneScheduleClient, + getTruckLaneScheduleClient, + ignoreTruckLaneScheduleClient, + listTruckLaneSchedulesClient, + retryFailedTruckLaneScheduleClient, + type TruckLaneScheduleLineResponse, + type TruckLaneScheduleResponse, +} from "@/app/api/shop/client"; +import type { ScheduleLaneOption } from "@/components/Shop/ScheduleChangeModal"; +import { extractApiErrorMessage } from "@/components/Shop/scheduleClientHelpers"; +import { resolveRescheduleExecuteAt, formatScheduleDisplayDateTime } from "@/components/Shop/scheduleExecuteAt"; +import { + buildScheduleLineDescription, +} from "@/components/Shop/scheduleLineDisplay"; + +type UiFilter = "all" | "pending" | "success" | "failed"; + +type UiTaskStatus = "pending" | "success" | "failed" | "cancelled" | "ignored"; + +function uiStatusFromLineCounts( + counts: NonNullable, +): UiTaskStatus | null { + const { total, applied, failed, pending } = counts; + if (failed > 0) return "failed"; + if (pending === 0 && applied > 0) return "success"; + if (pending === 0 && failed > 0 && applied === 0) return "failed"; + if (applied > 0 || failed > 0) return failed > 0 ? "failed" : "success"; + if (total > 0 && pending === total) return "pending"; + return null; +} + +function uiStatusFromLines( + lines: TruckLaneScheduleLineResponse[], +): UiTaskStatus | null { + if (lines.length === 0) return null; + const failed = lines.filter((l) => l.lineStatus === "FAILED").length; + const applied = lines.filter((l) => l.lineStatus === "APPLIED").length; + const pending = lines.filter((l) => l.lineStatus === "PENDING").length; + if (failed > 0) return "failed"; + if (pending === 0 && applied > 0) return "success"; + if (pending > 0 && (applied > 0 || failed > 0)) { + return failed > 0 ? "failed" : "pending"; + } + if (pending === lines.length) return "pending"; + return null; +} + +/** Prefer line outcomes over header status when they disagree (e.g. PARTIAL still stored as PENDING). */ +function resolveUiStatus( + task: TruckLaneScheduleResponse, + detail?: TruckLaneScheduleResponse | null, +): UiTaskStatus { + const st = String(task.status ?? "").toUpperCase(); + if (st === "CANCELLED") return "cancelled"; + if (st === "IGNORED") return "ignored"; + + const fromDetailLines = detail?.lines?.length + ? uiStatusFromLines(detail.lines) + : null; + if (fromDetailLines) return fromDetailLines; + + if (task.lineCounts) { + const fromCounts = uiStatusFromLineCounts(task.lineCounts); + if (fromCounts) return fromCounts; + } + + if (st === "APPLIED") return "success"; + if (st === "FAILED") return "failed"; + if (st === "PARTIAL") { + return (task.lineCounts?.failed ?? 0) > 0 ? "failed" : "success"; + } + if (st === "PENDING" || st === "APPLYING") return "pending"; + return "pending"; +} + +function canManagePendingSchedule( + task: TruckLaneScheduleResponse, + detail?: TruckLaneScheduleResponse | null, +): boolean { + const st = String(task.status ?? "").toUpperCase(); + if (st !== "PENDING" && st !== "APPLYING") return false; + const lines = detail?.lines ?? []; + if (lines.length > 0) { + return lines.every((l) => l.lineStatus === "PENDING"); + } + const c = task.lineCounts; + if (!c) return true; + return c.pending > 0 && c.applied === 0 && c.failed === 0; +} + +function splitExecuteAt(executeAt: string): { date: string; time: string } { + const raw = executeAt.trim(); + if (!raw) return { date: "-", time: "-" }; + const tIdx = raw.indexOf("T"); + if (tIdx < 0) return { date: raw, time: "" }; + return { + date: raw.slice(0, tIdx), + time: raw.slice(tIdx + 1, tIdx + 6), + }; +} + +type Props = { + open: boolean; + onClose: () => void; + lanes: ScheduleLaneOption[]; + onAfterChange?: () => void | Promise; +}; + +const ScheduleTaskHistoryModal: React.FC = ({ + open, + onClose, + lanes, + onAfterChange, +}) => { + const { t } = useTranslation("shop"); + const [filter, setFilter] = useState("all"); + const [loading, setLoading] = useState(false); + const [tasks, setTasks] = useState([]); + const [expandedId, setExpandedId] = useState(null); + const [detailById, setDetailById] = useState< + Record + >({}); + const [detailLoadingId, setDetailLoadingId] = useState(null); + const [actionId, setActionId] = useState(null); + const [actionError, setActionError] = useState(null); + const [actionNotice, setActionNotice] = useState(null); + + const laneById = useMemo( + () => new Map(lanes.map((l) => [l.id, l])), + [lanes], + ); + + const loadTasks = useCallback(async () => { + setLoading(true); + try { + const list = await listTruckLaneSchedulesClient( + ["PENDING", "APPLYING", "APPLIED", "PARTIAL", "FAILED", "CANCELLED", "IGNORED"], + 200, + ); + const sorted = [...list].sort((a, b) => + String(b.created ?? b.executeAt).localeCompare( + String(a.created ?? a.executeAt), + ), + ); + setTasks(sorted); + + // Warm detail cache so badges match expanded view (and line-level errors are available). + const warmTargets = sorted + .filter((task) => { + const st = String(task.status ?? "").toUpperCase(); + if (st === "CANCELLED" || st === "APPLIED" || st === "IGNORED") return false; + if (task.lineCounts) return false; + return true; + }) + .slice(0, 30); + if (warmTargets.length > 0) { + const warmed = await Promise.all( + warmTargets.map((task) => + getTruckLaneScheduleClient(task.id).catch(() => null), + ), + ); + const next: Record = {}; + for (const d of warmed) { + if (d?.id) next[d.id] = d; + } + if (Object.keys(next).length > 0) { + setDetailById((prev) => ({ ...prev, ...next })); + } + } + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + if (!open) return; + setFilter("all"); + setExpandedId(null); + setDetailById({}); + setActionError(null); + setActionNotice(null); + void loadTasks(); + }, [open, loadTasks]); + + const counts = useMemo(() => { + let pending = 0; + let success = 0; + let failed = 0; + for (const task of tasks) { + const ui = resolveUiStatus(task, detailById[task.id]); + if (ui === "pending") pending++; + else if (ui === "success") success++; + else if (ui === "failed") failed++; + } + return { pending, success, failed, all: tasks.length }; + }, [tasks, detailById]); + + const filteredTasks = useMemo(() => { + return tasks.filter((task) => { + const detail = detailById[task.id]; + const ui = resolveUiStatus(task, detail); + if (filter === "all") return ui !== "cancelled"; + if (filter === "pending") return ui === "pending"; + if (filter === "success") return ui === "success"; + return ui === "failed"; + }); + }, [tasks, filter, detailById]); + + const ensureDetail = async (id: number) => { + if (detailById[id]) return detailById[id]; + setDetailLoadingId(id); + try { + const detail = await getTruckLaneScheduleClient(id); + setDetailById((prev) => ({ ...prev, [id]: detail })); + return detail; + } finally { + setDetailLoadingId(null); + } + }; + + const toggleExpand = async (id: number) => { + if (expandedId === id) { + setExpandedId(null); + return; + } + setExpandedId(id); + await ensureDetail(id); + }; + + const handleCancel = async (id: number) => { + setActionId(id); + setActionError(null); + try { + await cancelTruckLaneScheduleClient(id); + await onAfterChange?.(); + await loadTasks(); + } catch (err: unknown) { + setActionError(extractApiErrorMessage(err) ?? t("schedule_err_generic")); + } finally { + setActionId(null); + } + }; + + const handleApplyNow = async (id: number) => { + setActionId(id); + setActionError(null); + try { + await applyNowTruckLaneScheduleClient(id); + await onAfterChange?.(); + await loadTasks(); + } catch (err: unknown) { + setActionError(extractApiErrorMessage(err) ?? t("schedule_err_generic")); + } finally { + setActionId(null); + } + }; + + const handleReschedule = async (task: TruckLaneScheduleResponse) => { + if (String(task.status ?? "").toUpperCase() === "PARTIAL") { + setActionError(t("schedule_retry_rejects_partial")); + return; + } + setActionId(task.id); + setActionError(null); + setActionNotice(null); + try { + const { executeAt, adjusted } = resolveRescheduleExecuteAt(task.executeAt); + try { + await retryFailedTruckLaneScheduleClient(task.id, { executeAt }); + } catch (retryErr: unknown) { + let detail = detailById[task.id]; + if (!detail?.lines?.length) { + detail = (await ensureDetail(task.id)) ?? undefined; + } + const retryLines = (detail?.lines ?? []).filter( + (l) => l.lineStatus === "FAILED" || l.lineStatus === "PENDING", + ); + if (retryLines.length === 0) throw retryErr; + const lines = retryLines.map((l) => ({ + action: l.action, + truckRowId: l.truckRowId, + toTruckLanceCode: l.toTruckLanceCode, + toRemark: l.toRemark, + toStoreId: l.toStoreId, + toLoadingSequence: l.toLoadingSequence ?? 0, + toDistrictReference: l.toDistrictReference ?? null, + shopCode: l.shopCode, + shopName: l.shopName, + departureTime: l.departureTime, + })); + await createTruckLaneScheduleClient({ executeAt, lines }); + } + if (adjusted) { + setActionNotice( + t("schedule_reschedule_time_adjusted", { at: executeAt }), + ); + } + await onAfterChange?.(); + await loadTasks(); + } catch (err: unknown) { + setActionError(extractApiErrorMessage(err) ?? t("schedule_err_generic")); + } finally { + setActionId(null); + } + }; + + const handleIgnore = async (id: number) => { + setActionId(id); + setActionError(null); + try { + await ignoreTruckLaneScheduleClient(id); + await onAfterChange?.(); + await loadTasks(); + } catch (err: unknown) { + setActionError(extractApiErrorMessage(err) ?? t("schedule_err_generic")); + } finally { + setActionId(null); + } + }; + + const resolveActionLabel = useCallback( + (action: string) => { + switch (action) { + case "MOVE": + return t("schedule_action_move"); + case "CREATE": + return t("schedule_action_create"); + case "DELETE": + return t("schedule_action_delete"); + case "ENSURE_LANE": + return t("schedule_action_ensure_lane"); + default: + return action; + } + }, + [t], + ); + + const resolveActionChipColor = useCallback( + ( + action: string, + ): "default" | "primary" | "success" | "warning" | "info" | "error" => { + switch (action) { + case "MOVE": + return "primary"; + case "CREATE": + return "success"; + case "DELETE": + return "warning"; + case "ENSURE_LANE": + return "info"; + default: + return "default"; + } + }, + [], + ); + + const statusBadge = ( + task: TruckLaneScheduleResponse, + ui: UiTaskStatus, + ) => { + if (ui === "pending") { + return ( + } + label={t("schedule_history_status_pending")} + sx={{ + fontWeight: 700, + bgcolor: "warning.50", + color: "warning.dark", + border: 1, + borderColor: "warning.200", + "@keyframes schedulePulse": { + "0%, 100%": { opacity: 1 }, + "50%": { opacity: 0.65 }, + }, + animation: "schedulePulse 2s ease-in-out infinite", + }} + /> + ); + } + if (ui === "success") { + return ( + } + label={t("schedule_history_status_success")} + sx={{ + fontWeight: 700, + bgcolor: "success.50", + color: "success.dark", + border: 1, + borderColor: "success.200", + }} + /> + ); + } + if (ui === "ignored") { + return ( + } + label={t("schedule_history_status_ignored")} + sx={{ + fontWeight: 700, + bgcolor: "grey.100", + color: "text.secondary", + border: 1, + borderColor: "grey.300", + }} + /> + ); + } + return ( + } + label={t("schedule_history_status_failed")} + sx={{ + fontWeight: 700, + bgcolor: "error.50", + color: "error.dark", + border: 1, + borderColor: "error.200", + }} + /> + ); + }; + + const filterBtn = ( + key: UiFilter, + label: string, + count: number, + selectedBg?: string, + selectedColor?: string, + ) => ( + + ); + + return ( + + + + + + + + + {t("schedule_history_title")} + + + {t("schedule_history_subtitle")} + + + + + + + + + + + {filterBtn("all", t("schedule_history_filter_all"), counts.all)} + {filterBtn( + "pending", + t("schedule_history_filter_pending"), + counts.pending, + "warning.main", + )} + {filterBtn( + "success", + t("schedule_history_filter_success"), + counts.success, + "success.main", + )} + {filterBtn( + "failed", + t("schedule_history_filter_failed"), + counts.failed, + "error.main", + )} + + + {actionError && ( + setActionError(null)}> + {actionError} + + )} + {actionNotice && ( + setActionNotice(null)}> + {actionNotice} + + )} + + + {loading ? ( + + + + ) : filteredTasks.length === 0 ? ( + + + + {t("schedule_history_empty")} + + + ) : ( + filteredTasks.map((task) => { + const expanded = expandedId === task.id; + const detail = detailById[task.id]; + const ui = resolveUiStatus(task, detail); + const { date, time } = splitExecuteAt(task.executeAt); + const lines = detail?.lines ?? []; + const failedLines = lines.filter((l) => l.lineStatus === "FAILED"); + const appliedLines = lines.filter( + (l) => l.lineStatus === "APPLIED", + ); + const busy = actionId === task.id; + const showPendingActions = canManagePendingSchedule(task, detail); + + return ( + + + + + + #{task.id} + + {statusBadge(task, ui)} + {task.created && ( + + {t("schedule_history_created", { + at: formatScheduleDisplayDateTime(task.created), + by: + task.createdBy?.trim() || + task.modifiedBy?.trim() || + t("schedule_history_unknown_user"), + })} + + )} + + + + } + label={`${date} ${time}`} + sx={{ fontWeight: 700 }} + /> + {task.lineCounts && ( + + {t("schedule_history_line_counts", { + total: task.lineCounts.total, + applied: task.lineCounts.applied, + failed: task.lineCounts.failed, + pending: task.lineCounts.pending, + })} + + )} + + + {ui === "success" && task.appliedAt && ( + + + {t("schedule_history_applied_at", { + at: formatScheduleDisplayDateTime(task.appliedAt), + })} + + )} + + + + + + {detailLoadingId === task.id && !detail ? ( + + ) : lines.length === 0 ? ( + + {t("schedule_history_no_lines")} + + ) : ( + + {lines.map((line) => { + const shopLabel = + [line.shopCode, line.shopName] + .filter(Boolean) + .join(" — ") || + (line.truckRowId != null + ? `#${line.truckRowId}` + : resolveActionLabel(line.action)); + const { headline, details } = + buildScheduleLineDescription( + line, + laneById, + t, + ); + const lineOk = line.lineStatus === "APPLIED"; + const linePending = + line.lineStatus === "PENDING" || + line.lineStatus === "APPLYING"; + const lineFailed = line.lineStatus === "FAILED"; + const lineChipColor = lineOk + ? "success" + : linePending + ? "warning" + : lineFailed + ? "error" + : "default"; + return ( + + + + + {shopLabel} + + + + + {headline} + {details.length > 0 + ? ` · ${details.join(" · ")}` + : ""} + + {line.errorMessage && ( + + {line.errorMessage} + + )} + + ); + })} + + )} + {!detailLoadingId && + detail && + failedLines.length > 0 && ( + + {t("schedule_history_failed_lines", { + count: failedLines.length, + })} + + )} + {detail && appliedLines.length > 0 && ( + + {t("schedule_history_success_lines", { + count: appliedLines.length, + })} + + )} + + + + + + {showPendingActions && ( + <> + + + + )} + {ui === "success" && ( + + {t("schedule_history_archived")} + + )} + {ui === "failed" && ( + <> + + + {!showPendingActions && ( + + {t("schedule_history_needs_attention")} + + )} + + )} + {ui === "ignored" && ( + + {t("schedule_history_status_ignored")} + + )} + + + + ); + }) + )} + + + + + + + + + ); +}; + +export default memo(ScheduleTaskHistoryModal); diff --git a/src/components/Shop/Shop.tsx b/src/components/Shop/Shop.tsx index 8f7a2e8..a78aac8 100644 --- a/src/components/Shop/Shop.tsx +++ b/src/components/Shop/Shop.tsx @@ -41,7 +41,7 @@ type SearchQuery = { type SearchParamNames = keyof SearchQuery; const Shop: React.FC = () => { - const { t } = useTranslation("common"); + const { t } = useTranslation("shop"); const router = useRouter(); const searchParams = useSearchParams(); const [activeTab, setActiveTab] = useState(0); @@ -315,9 +315,9 @@ const Shop: React.FC = () => { borderBottom: '1px solid #e0e0e0' }}> - 店鋪路線管理 + {t("ShopAndTruck")} diff --git a/src/components/Shop/ShopDetail.tsx b/src/components/Shop/ShopDetail.tsx index f475193..135902d 100644 --- a/src/components/Shop/ShopDetail.tsx +++ b/src/components/Shop/ShopDetail.tsx @@ -100,7 +100,7 @@ const departureTimeToStringForSave = (timeValue: Truck["departureTime"]): string }; const ShopDetail: React.FC = () => { - const { t } = useTranslation("common"); + const { t } = useTranslation("shop"); const router = useRouter(); const searchParams = useSearchParams(); const shopId = searchParams.get("id"); diff --git a/src/components/Shop/TruckLane.tsx b/src/components/Shop/TruckLane.tsx index 146318f..f6c99d6 100644 --- a/src/components/Shop/TruckLane.tsx +++ b/src/components/Shop/TruckLane.tsx @@ -5,14 +5,6 @@ import { Card, CardContent, Typography, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TablePagination, - TableRow, - Paper, Button, CircularProgress, Alert, @@ -30,14 +22,20 @@ import { } from "@mui/material"; import AddIcon from "@mui/icons-material/Add"; import SaveIcon from "@mui/icons-material/Save"; -import { useState, useMemo } from "react"; +import { useState, useMemo, useCallback } from "react"; import { useRouter } from "next/navigation"; import { useTranslation } from "react-i18next"; import { findAllUniqueTruckLaneCombinationsClient, createTruckWithoutShopClient } from "@/app/api/shop/client"; import type { Truck } from "@/app/api/shop/actions"; import SearchBox, { Criterion } from "../SearchBox"; +import SearchResults, { + Column, + defaultPagingController, +} from "../SearchResults/SearchResults"; import { formatDepartureTime, normalizeStoreId } from "@/app/utils/formatUtil"; +type TruckRow = Omit & { id: string | number }; + type SearchQuery = { truckLanceCode: string; departureTime: string; @@ -47,14 +45,13 @@ type SearchQuery = { type SearchParamNames = keyof SearchQuery; const TruckLane: React.FC = () => { - const { t } = useTranslation("common"); + const { t } = useTranslation("shop"); const router = useRouter(); const [truckData, setTruckData] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [filters, setFilters] = useState>({}); - const [page, setPage] = useState(0); - const [rowsPerPage, setRowsPerPage] = useState(10); + const [pagingController, setPagingController] = useState(defaultPagingController); const [addDialogOpen, setAddDialogOpen] = useState(false); const [newTruck, setNewTruck] = useState({ truckLanceCode: "", @@ -93,11 +90,80 @@ const TruckLane: React.FC = () => { }); }, [truckData, filters]); - // Paginated rows - const paginatedRows = useMemo(() => { - const startIndex = page * rowsPerPage; - return filteredRows.slice(startIndex, startIndex + rowsPerPage); - }, [filteredRows, page, rowsPerPage]); + const tableRows = useMemo( + () => + filteredRows.map((truck) => ({ + ...truck, + id: truck.id ?? (String(truck.truckLanceCode ?? "").trim() || `truck-${truck.truckLanceCode}`), + })), + [filteredRows], + ); + + const handleViewDetail = useCallback( + (truck: TruckRow) => { + const truckLanceCode = String(truck.truckLanceCode || "").trim(); + if (truckLanceCode) { + const url = new URL(`/settings/shop/truckdetail`, window.location.origin); + url.searchParams.set("truckLanceCode", truckLanceCode); + router.push(url.pathname + url.search); + } + }, + [router], + ); + + const columns = useMemo[]>( + () => [ + { + name: "truckLanceCode", + label: t("TruckLance Code"), + sx: { width: "250px", minWidth: "250px", maxWidth: "250px" }, + renderCell: (item) => String(item.truckLanceCode ?? "-"), + }, + { + name: "departureTime", + label: t("Departure Time"), + sx: { width: "200px", minWidth: "200px", maxWidth: "200px" }, + renderCell: (item) => + formatDepartureTime( + Array.isArray(item.departureTime) + ? item.departureTime + : item.departureTime + ? String(item.departureTime) + : null, + ), + }, + { + name: "storeId", + label: t("Store ID"), + sx: { width: "150px", minWidth: "150px", maxWidth: "150px" }, + renderCell: (item) => + normalizeStoreId( + item.storeId + ? typeof item.storeId === "string" || item.storeId instanceof String + ? String(item.storeId) + : String(item.storeId) + : null, + ), + }, + { + name: "id", + label: t("Actions"), + align: "right", + headerAlign: "right", + sx: { width: "150px", minWidth: "150px", maxWidth: "150px" }, + renderCell: (item) => ( + + ), + }, + ], + [handleViewDetail, t], + ); const handleSearch = async (inputs: Record) => { setLoading(true); @@ -113,7 +179,7 @@ const TruckLane: React.FC = () => { }); setTruckData(Array.from(uniqueCodes.values())); setFilters(inputs); - setPage(0); + setPagingController(defaultPagingController); } catch (err: any) { console.error("Failed to load truck lanes:", err); setError(err?.message ?? String(err) ?? t("Failed to load truck lanes")); @@ -122,26 +188,6 @@ const TruckLane: React.FC = () => { } }; - const handlePageChange = (event: unknown, newPage: number) => { - setPage(newPage); - }; - - const handleRowsPerPageChange = (event: React.ChangeEvent) => { - setRowsPerPage(parseInt(event.target.value, 10)); - setPage(0); // Reset to first page when changing rows per page - }; - - const handleViewDetail = (truck: Truck) => { - // Navigate to truck lane detail page using truckLanceCode - const truckLanceCode = String(truck.truckLanceCode || "").trim(); - if (truckLanceCode) { - // Use router.push with proper URL encoding - const url = new URL(`/settings/shop/truckdetail`, window.location.origin); - url.searchParams.set("truckLanceCode", truckLanceCode); - router.push(url.pathname + url.search); - } - }; - const handleOpenAddDialog = () => { setNewTruck({ truckLanceCode: "", @@ -270,77 +316,12 @@ const TruckLane: React.FC = () => { ) : ( - - - - - - {t("TruckLance Code")} - - - {t("Departure Time")} - - - {t("Store ID")} - - - {t("Actions")} - - - - - {paginatedRows.length === 0 ? ( - - - - {t("No Truck Lane data available")} - - - - ) : ( - paginatedRows.map((truck) => ( - - - {String(truck.truckLanceCode ?? "-")} - - - {formatDepartureTime( - Array.isArray(truck.departureTime) - ? truck.departureTime - : (truck.departureTime ? String(truck.departureTime) : null) - )} - - - {normalizeStoreId( - truck.storeId ? (typeof truck.storeId === 'string' || truck.storeId instanceof String - ? String(truck.storeId) - : String(truck.storeId)) : null - )} - - - - - - )) - )} - -
- -
)} diff --git a/src/components/Shop/TruckLaneDetail.tsx b/src/components/Shop/TruckLaneDetail.tsx index 5874f2a..9f617eb 100644 --- a/src/components/Shop/TruckLaneDetail.tsx +++ b/src/components/Shop/TruckLaneDetail.tsx @@ -71,7 +71,7 @@ const truckDepartureTimeToInputValue = (timeValue: Truck["departureTime"]): stri }; const TruckLaneDetail: React.FC = () => { - const { t } = useTranslation("common"); + const { t } = useTranslation("shop"); const router = useRouter(); const searchParams = useSearchParams(); const truckLanceCodeParam = searchParams.get("truckLanceCode"); diff --git a/src/components/Shop/routeBoardDisplayOrder.ts b/src/components/Shop/routeBoardDisplayOrder.ts new file mode 100644 index 0000000..891f6d2 --- /dev/null +++ b/src/components/Shop/routeBoardDisplayOrder.ts @@ -0,0 +1,158 @@ +import { normalizeStoreId } from "@/app/utils/formatUtil"; + +/** Shared display-order helpers for RouteBoard and schedule drag workspace. */ + +export type DisplayOrderShop = { + id: number; + districtReferenceRaw?: string | null; + loadingSequence?: number | null; +}; + +export function toDistrictDisplayName( + district: string | null | undefined, +): string { + const value = String(district ?? "").trim(); + return value === "" ? "未分類" : value; +} + +export function toDistrictRawValue( + district: string | null | undefined, +): string | null { + const value = String(district ?? "").trim(); + return value === "" || value === "未分類" ? null : value; +} + +/** Shop card subtitle on RouteBoard (branch · code · store; district is in section header). */ +export function formatShopCardSubtitle(shop: { + branchName?: string | null; + shopCode: string; + storeId?: string | null; +}): string { + const parts: string[] = []; + const branch = String(shop.branchName ?? "").trim(); + if (branch !== "") parts.push(branch); + parts.push(String(shop.shopCode || "").trim() || "-"); + const store = normalizeStoreId(shop.storeId); + if (store !== "" && store !== "-") parts.push(store); + return parts.join(" · "); +} + +/** Subtitle for schedule review queue; always reflects current districtReferenceRaw. */ +export function formatPlannedShopLocation(shop: { + districtReferenceRaw: string | null; + shopCode: string; + storeId?: string | null; +}): string { + const district = toDistrictDisplayName(shop.districtReferenceRaw); + const parts = [district, String(shop.shopCode || "").trim() || "-"]; + const store = normalizeStoreId(shop.storeId); + if (store !== "" && store !== "-") parts.push(store); + return parts.join(" · "); +} + +export function dedupeDistrictPendingOrder(items: string[]): string[] { + const seen = new Set(); + const out: string[] = []; + for (const x of items) { + if (seen.has(x)) continue; + seen.add(x); + out.push(x); + } + return out; +} + +export function districtDisplayExistsInShops( + shops: T[], + pendingExtra: string[] | undefined, + display: string, +): boolean { + const set = new Set(groupByDistrict(shops).map((g) => g.district)); + for (const d of pendingExtra ?? []) set.add(d); + return set.has(display); +} + +export function buildLaneDistrictSections( + shops: T[], + pendingExtraDistrictDisplays: string[] | undefined, +): Array<{ district: string; shops: T[]; isPendingEmpty: boolean }> { + const grouped = groupByDistrict(shops); + const keysFromShops = new Set(grouped.map((g) => g.district)); + const extras = (pendingExtraDistrictDisplays ?? []).filter( + (d) => !keysFromShops.has(d), + ); + const merged = [ + ...grouped.map((g) => ({ + district: g.district, + shops: g.shops, + isPendingEmpty: false, + })), + ...extras.map((district) => ({ + district, + shops: [] as T[], + isPendingEmpty: true, + })), + ]; + merged.sort((a, b) => { + if (a.district === "未分類") return -1; + if (b.district === "未分類") return 1; + return a.district.localeCompare(b.district, "zh-Hant"); + }); + return merged; +} + +export function groupByDistrict( + shops: T[], +): Array<{ district: string; shops: T[] }> { + const map = new Map(); + for (const s of shops) { + const key = toDistrictDisplayName(s.districtReferenceRaw); + const arr = map.get(key) ?? []; + arr.push(s); + map.set(key, arr); + } + return Array.from(map.entries()) + .sort(([a], [b]) => { + if (a === "未分類") return -1; + if (b === "未分類") return 1; + return a.localeCompare(b, "zh-Hant"); + }) + .map(([district, list]) => ({ + district, + shops: list + .slice() + .sort( + (x, y) => (x.loadingSequence ?? 0) - (y.loadingSequence ?? 0), + ), + })); +} + +export function flattenDisplayOrder( + shops: T[], +): T[] { + return groupByDistrict(shops).flatMap((g) => g.shops); +} + +export function computeMovedLoadingSequence( + inserted: T[], + movedId: number, + preferPrevious = false, +): number { + const idx = inserted.findIndex((s) => s.id === movedId); + if (idx < 0) return 0; + if (preferPrevious) { + const prev = inserted[idx - 1]; + if (prev && typeof prev.loadingSequence === "number") + return prev.loadingSequence; + } + const next = inserted[idx + 1]; + if (next && typeof next.loadingSequence === "number") + return next.loadingSequence; + const prev = inserted[idx - 1]; + if (prev && typeof prev.loadingSequence === "number") + return prev.loadingSequence; + const max = inserted.reduce( + (m, s) => Math.max(m, Number(s.loadingSequence ?? 0) || 0), + 0, + ); + return max; +} diff --git a/src/components/Shop/routeBoardVersionLog.ts b/src/components/Shop/routeBoardVersionLog.ts index 05efb07..8482ad8 100644 --- a/src/components/Shop/routeBoardVersionLog.ts +++ b/src/components/Shop/routeBoardVersionLog.ts @@ -27,18 +27,45 @@ function pickField( return changes.find((c) => c.field === field); } -export const VERSION_LOG_LOADING_SEQUENCE_LABEL = "裝載順序"; +function isBlankDiffValue(raw: string | null | undefined): boolean { + return raw == null || String(raw).trim() === ""; +} + +/** 新版本已無此列:車線/店碼等由有值變空(勿用 line 上的 ctx fallback 當 to 車線) */ +function isRowRemovedInDiff(ch: DiffFieldChange[]): boolean { + const lane = pickField(ch, "truckLanceCode"); + const code = pickField(ch, "shopCode"); + if (!lane && !code) return false; + const laneOk = + !lane || (!isBlankDiffValue(lane.from) && isBlankDiffValue(lane.to)); + const codeOk = + !code || (!isBlankDiffValue(code.from) && isBlankDiffValue(code.to)); + return laneOk && codeOk; +} + +function isRowInsertedInDiff(ch: DiffFieldChange[]): boolean { + const lane = pickField(ch, "truckLanceCode"); + const code = pickField(ch, "shopCode"); + if (!lane && !code) return false; + const laneOk = + !lane || (isBlankDiffValue(lane.from) && !isBlankDiffValue(lane.to)); + const codeOk = + !code || (isBlankDiffValue(code.from) && !isBlankDiffValue(code.to)); + return laneOk && codeOk; +} + +export const VERSION_LOG_LOADING_SEQUENCE_LABEL = "versionLogField_loadingSequence"; const VERSION_LOG_FIELD_LABEL: Record = { - departureTime: "發車時段", + departureTime: "versionLogField_departureTime", loadingSequence: VERSION_LOG_LOADING_SEQUENCE_LABEL, - branchName: "分店名稱", - districtReference: "區域", - shopCode: "店鋪代碼", - storeId: "樓層/店別", - remark: "備註", - truckLanceCode: "車線代碼", - logisticId: "物流公司", + branchName: "versionLogField_branchName", + districtReference: "versionLogField_districtReference", + shopCode: "versionLogField_shopCode", + storeId: "versionLogField_storeId", + remark: "versionLogField_remark", + truckLanceCode: "versionLogField_truckLanceCode", + logisticId: "versionLogField_logisticId", }; /** 版本 LOG 用:時段顯示為 HH:mm,避免 ISO / 帶秒過長 */ @@ -118,6 +145,13 @@ export function formatLaneLabel( return `${c} · ${r}`; } +export function resolveVersionActor(version: { + modifiedBy?: string | null; + createdBy?: string | null; +}): string { + return String(version.modifiedBy ?? version.createdBy ?? "").trim(); +} + export function splitVersionCreated(created: string | null | undefined): { date: string; time: string; @@ -164,15 +198,17 @@ export function diffLineToShopRow( const fromLane = formatLaneLabel(tc?.from ?? ctxCode, rem?.from ?? ctxRem); const toLane = formatLaneLabel(tc?.to ?? ctxCode, rem?.to ?? ctxRem); - const fromEmpty = fromLane === "—"; - const toEmpty = toLane === "—"; + const toLaneStrict = formatLaneLabel(tc?.to, rem?.to); let type: VersionLogShopRow["type"]; - if (fromEmpty && !toEmpty) type = "added"; - else if (!fromEmpty && toEmpty) type = "deleted"; + if (isRowInsertedInDiff(ch)) type = "added"; + else if (isRowRemovedInDiff(ch)) type = "deleted"; else if (fromLane !== toLane) type = "moved"; else type = "edited"; + const fromEmpty = fromLane === "—"; + const toEmpty = type === "deleted" ? true : toLaneStrict === "—"; + return { type, shopName, @@ -638,7 +674,7 @@ export function buildStagedBoardLogEntries(input: { } /** - * 異動列標題:優先用 shop 主檔名稱,其次快照 branchName(與代碼不同時),最後才代碼。 + * 異動列標題:優先用 shop 主檔名稱,其次版本 branchName(與代碼不同時),最後才代碼。 */ export function resolveVersionLogShopHeadline( row: VersionLogShopRow, @@ -659,7 +695,7 @@ export function resolveVersionLogShopHeadline( const bits: string[] = []; if (!snapIsEmpty && !snapIsJustCode && snap !== master) - bits.push(`快照分店:${snap}`); + bits.push(`分店:${snap}`); const detail = bits.length > 0 ? bits.join(" · ") : undefined; return { headline, detail }; diff --git a/src/components/Shop/scheduleApiAdapter.ts b/src/components/Shop/scheduleApiAdapter.ts new file mode 100644 index 0000000..01a69e9 --- /dev/null +++ b/src/components/Shop/scheduleApiAdapter.ts @@ -0,0 +1,215 @@ +import type { + CreateTruckLaneScheduleRequest, + TruckLaneMoveTargetRequest, + TruckLaneScheduleLineRequest, +} from "@/app/api/shop/actions"; +import { movesToScheduleLines } from "@/components/Shop/scheduleLineAdapter"; +import { normalizeStoreId } from "@/app/utils/formatUtil"; +import type { + ScheduleLaneOption, + ScheduleMoveSelection, +} from "@/components/Shop/ScheduleChangeModal"; +import type { + ScheduleModification, + ScheduleDragWorkspaceState, + ScheduledDeleteSnapshot, +} from "@/components/Shop/scheduleDragWorkspace"; + +export function buildScheduleMoveFromSelection( + truckRowId: number, + selection: ScheduleMoveSelection, + lane: ScheduleLaneOption, + toDistrictReference?: string | null, +): TruckLaneMoveTargetRequest { + return { + truckRowId, + toTruckLanceCode: lane.truckLanceCode, + toRemark: + lane.remark != null && String(lane.remark).trim() !== "" + ? String(lane.remark).trim() + : null, + toStoreId: normalizeStoreId(lane.storeId), + toLoadingSequence: selection.toLoadingSequence, + toDistrictReference: toDistrictReference ?? null, + }; +} + +export function buildDeleteScheduleLine( + truckRowId: number, + lane: ScheduleLaneOption, + shopCode: string, + shopName: string, +): TruckLaneScheduleLineRequest { + return { + action: "DELETE", + truckRowId, + toTruckLanceCode: lane.truckLanceCode, + toRemark: + lane.remark != null && String(lane.remark).trim() !== "" + ? String(lane.remark).trim() + : null, + toStoreId: normalizeStoreId(lane.storeId), + shopCode, + shopName, + }; +} + +function districtByTruckRowIdFromPlanned( + plannedLanes?: ScheduleDragWorkspaceState, +): Map { + const map = new Map(); + if (!plannedLanes) return map; + for (const lane of plannedLanes) { + for (const shop of lane.shops) { + map.set(shop.truckRowId, shop.districtReferenceRaw); + } + } + return map; +} + +export function buildScheduleMovesFromMappings( + mappings: Record, + lanes: ScheduleLaneOption[], + plannedLanes?: ScheduleDragWorkspaceState, +): TruckLaneMoveTargetRequest[] { + const laneById = new Map(lanes.map((l) => [l.id, l])); + const districtByTruckRowId = districtByTruckRowIdFromPlanned(plannedLanes); + const out: TruckLaneMoveTargetRequest[] = []; + for (const [id, sel] of Object.entries(mappings)) { + const truckRowId = Number(id); + const lane = laneById.get(sel.laneId); + if (!lane || !Number.isFinite(truckRowId)) continue; + const seq = sel.toLoadingSequence; + if (!Number.isFinite(seq) || seq < 0) continue; + out.push( + buildScheduleMoveFromSelection( + truckRowId, + sel, + lane, + districtByTruckRowId.get(truckRowId), + ), + ); + } + return out; +} + +export function buildScheduleLinesFromPlan(input: { + modifications: ScheduleModification[]; + pendingDeletes: ScheduledDeleteSnapshot[]; + lanes: ScheduleLaneOption[]; + plannedLanes?: ScheduleDragWorkspaceState; +}): TruckLaneScheduleLineRequest[] { + const laneById = new Map(input.lanes.map((l) => [l.id, l])); + const deleteIds = new Set(input.pendingDeletes.map((d) => d.truckRowId)); + const mappings = Object.fromEntries( + input.modifications + .filter((m) => !deleteIds.has(m.truckRowId)) + .map((m) => [ + m.truckRowId, + { laneId: m.toLaneId, toLoadingSequence: m.newLoadingSequence }, + ]), + ); + const moveLines = movesToScheduleLines( + buildScheduleMovesFromMappings( + mappings, + input.lanes, + input.plannedLanes, + ), + ); + const deleteLines = input.pendingDeletes + .map((d) => { + const lane = laneById.get(d.fromLaneId); + if (!lane) return null; + return buildDeleteScheduleLine( + d.truckRowId, + lane, + d.shopCode, + d.displayName, + ); + }) + .filter((line): line is TruckLaneScheduleLineRequest => line != null); + return [...moveLines, ...deleteLines]; +} + +export function buildScheduleMovesFromModifications( + modifications: ScheduleModification[], + lanes: ScheduleLaneOption[], + plannedLanes?: ScheduleDragWorkspaceState, + pendingDeletes: ScheduledDeleteSnapshot[] = [], +): TruckLaneMoveTargetRequest[] { + const deleteIds = new Set(pendingDeletes.map((d) => d.truckRowId)); + const mappings = Object.fromEntries( + modifications + .filter((m) => !deleteIds.has(m.truckRowId)) + .map((m) => [ + m.truckRowId, + { laneId: m.toLaneId, toLoadingSequence: m.newLoadingSequence }, + ]), + ); + return buildScheduleMovesFromMappings(mappings, lanes, plannedLanes); +} + +export function buildCreateScheduleRequest(input: { + executeAt: string; + note?: string | null; + mappings: Record; + lanes: ScheduleLaneOption[]; + plannedLanes?: ScheduleDragWorkspaceState; + pendingDeletes?: ScheduledDeleteSnapshot[]; + modifications?: ScheduleModification[]; +}): CreateTruckLaneScheduleRequest { + if (input.modifications != null) { + return { + executeAt: input.executeAt, + note: input.note ?? null, + lines: buildScheduleLinesFromPlan({ + modifications: input.modifications, + pendingDeletes: input.pendingDeletes ?? [], + lanes: input.lanes, + plannedLanes: input.plannedLanes, + }), + }; + } + const moves = buildScheduleMovesFromMappings( + input.mappings, + input.lanes, + input.plannedLanes, + ); + const deleteLines = (input.pendingDeletes ?? []) + .map((d) => { + const lane = input.lanes.find((l) => l.id === d.fromLaneId); + if (!lane) return null; + return buildDeleteScheduleLine( + d.truckRowId, + lane, + d.shopCode, + d.displayName, + ); + }) + .filter((line): line is TruckLaneScheduleLineRequest => line != null); + return { + executeAt: input.executeAt, + note: input.note ?? null, + lines: [...movesToScheduleLines(moves), ...deleteLines], + }; +} + +export function buildCreateScheduleRequestFromModifications(input: { + executeAt: string; + note?: string | null; + modifications: ScheduleModification[]; + lanes: ScheduleLaneOption[]; + plannedLanes?: ScheduleDragWorkspaceState; + pendingDeletes?: ScheduledDeleteSnapshot[]; +}): CreateTruckLaneScheduleRequest { + return { + executeAt: input.executeAt, + note: input.note ?? null, + lines: buildScheduleLinesFromPlan({ + modifications: input.modifications, + pendingDeletes: input.pendingDeletes ?? [], + lanes: input.lanes, + plannedLanes: input.plannedLanes, + }), + }; +} diff --git a/src/components/Shop/scheduleClientHelpers.ts b/src/components/Shop/scheduleClientHelpers.ts new file mode 100644 index 0000000..8fd1321 --- /dev/null +++ b/src/components/Shop/scheduleClientHelpers.ts @@ -0,0 +1,57 @@ +import type { TruckLaneScheduleResponse } from "@/app/api/shop/actions"; + +const DEFAULT_LIST_LIMIT = 200; + +export function buildTruckLaneScheduleListQuery( + status?: string[], + limit: number = DEFAULT_LIST_LIMIT, +): string { + const params = new URLSearchParams(); + for (const s of status ?? []) { + params.append("status", s); + } + params.set("limit", String(limit)); + const qs = params.toString(); + return qs ? `?${qs}` : ""; +} + +export function collectFailedTruckRowIdsFromSchedules( + schedules: TruckLaneScheduleResponse[], + lineDetails: Array, +): Set { + const out = new Set(); + for (const detail of lineDetails) { + for (const line of detail?.lines ?? []) { + if (line.lineStatus === "FAILED" && line.truckRowId != null && line.truckRowId > 0) { + out.add(line.truckRowId); + } + } + } + for (const schedule of schedules) { + if ( + schedule.status === "FAILED" && + (schedule.lineCounts?.failed ?? 0) > 0 && + !lineDetails.some((d) => d?.id === schedule.id) + ) { + // header-only failure without warmed lines — no shop ids available + } + } + return out; +} + +export function filterFailedSchedules( + schedules: TruckLaneScheduleResponse[], +): TruckLaneScheduleResponse[] { + return schedules.filter( + (s) => + s.status !== "IGNORED" && + (s.status === "FAILED" || + (s.status === "PARTIAL" && (s.lineCounts?.failed ?? 0) > 0)), + ); +} + +export function extractApiErrorMessage(err: unknown): string | null { + if (err instanceof Error && err.message.trim()) return err.message.trim(); + if (typeof err === "string" && err.trim()) return err.trim(); + return null; +} diff --git a/src/components/Shop/scheduleDragWorkspace.ts b/src/components/Shop/scheduleDragWorkspace.ts new file mode 100644 index 0000000..1f09a59 --- /dev/null +++ b/src/components/Shop/scheduleDragWorkspace.ts @@ -0,0 +1,381 @@ +import type { + ScheduleLaneOption, + ScheduleMoveSelection, + ScheduleShopRow, +} from "@/components/Shop/ScheduleChangeModal"; +import { + computeMovedLoadingSequence, + flattenDisplayOrder, + formatPlannedShopLocation, + toDistrictDisplayName, + toDistrictRawValue, +} from "@/components/Shop/routeBoardDisplayOrder"; + +export type PlannedShop = { + id: number; + truckRowId: number; + shopCode: string; + displayName: string; + branchName: string; + storeId: string; + districtReferenceRaw: string | null; + loadingSequence: number; + originalLaneId: string; + originalLoadingSequence: number; + originalDistrictReferenceRaw: string | null; +}; + +export type PlannedLane = { + id: string; + label: string; + truckLanceCode: string; + remark?: string | null; + storeId: string; + shops: PlannedShop[]; +}; + +export type ScheduleDragWorkspaceState = PlannedLane[]; + +export type ScheduleModification = { + truckRowId: number; + shopCode: string; + displayName: string; + location: string; + fromLaneId: string; + fromLaneLabel: string; + toLaneId: string; + toLaneLabel: string; + oldLoadingSequence: number; + newLoadingSequence: number; + isLaneChanged: boolean; + isSeqChanged: boolean; + isDistrictChanged: boolean; + oldDistrictLabel: string; + newDistrictLabel: string; +}; + +export type ScheduledDeleteSnapshot = { + truckRowId: number; + shopCode: string; + displayName: string; + location: string; + fromLaneId: string; + fromLaneLabel: string; + shop: PlannedShop; +}; + +type MoveShopArgs = { + shopId: number; + fromLaneId: string; + toLaneId: string; + beforeShopId?: number | null; + targetDistrict?: string | null; +}; + +function laneHasShopCode(lane: PlannedLane, shopCode: string, excludeId: number): boolean { + return lane.shops.some( + (s) => s.truckRowId !== excludeId && s.shopCode === shopCode, + ); +} + +export function initPlannedLanes( + lanes: ScheduleLaneOption[], + shops: ScheduleShopRow[], +): ScheduleDragWorkspaceState { + const shopRowById = new Map(shops.map((s) => [s.truckRowId, s])); + + return lanes.map((lane) => { + const plannedShops: PlannedShop[] = lane.shops + .map((snap) => { + const row = shopRowById.get(snap.truckRowId); + if (!row) return null; + const seq = snap.loadingSequence ?? 0; + return { + id: row.truckRowId, + truckRowId: row.truckRowId, + shopCode: row.shopCode, + displayName: row.displayName, + branchName: row.branchName, + storeId: row.storeId, + districtReferenceRaw: row.districtReferenceRaw, + loadingSequence: seq, + originalLaneId: row.currentLaneId, + originalLoadingSequence: seq, + originalDistrictReferenceRaw: row.districtReferenceRaw, + } satisfies PlannedShop; + }) + .filter((s): s is PlannedShop => s != null); + + return { + id: lane.id, + label: lane.label, + truckLanceCode: lane.truckLanceCode, + remark: lane.remark, + storeId: lane.storeId, + shops: plannedShops, + }; + }); +} + +export function moveShop( + state: ScheduleDragWorkspaceState, + args: MoveShopArgs, +): ScheduleDragWorkspaceState { + const { shopId, fromLaneId, toLaneId, beforeShopId = null, targetDistrict } = + args; + + if (beforeShopId != null && beforeShopId === shopId) { + return state; + } + + const next = state.map((lane) => ({ ...lane, shops: lane.shops.slice() })); + const from = next.find((l) => l.id === fromLaneId); + const to = next.find((l) => l.id === toLaneId); + if (!from || !to) return state; + + const shop = from.shops.find((s) => s.truckRowId === shopId); + if (!shop) return state; + + if (from.id !== to.id && laneHasShopCode(to, shop.shopCode, shop.truckRowId)) { + return state; + } + + const fromFlat = flattenDisplayOrder( + from.shops.filter((s) => s.truckRowId !== shopId), + ); + const toFlatRaw = flattenDisplayOrder(to.shops); + const toFlat = + from.id === to.id + ? toFlatRaw.filter((s) => s.truckRowId !== shopId) + : toFlatRaw.slice(); + + const beforeShop = + beforeShopId != null + ? toFlat.find((s) => s.truckRowId === beforeShopId) + : null; + const isCrossLane = from.id !== to.id; + const targetDistrictRaw = + targetDistrict !== undefined + ? toDistrictRawValue(targetDistrict) + : isCrossLane + ? shop.districtReferenceRaw + : beforeShop + ? toDistrictRawValue(beforeShop.districtReferenceRaw) + : shop.districtReferenceRaw; + + const moved: PlannedShop = { + ...shop, + districtReferenceRaw: targetDistrictRaw, + }; + + const insertIdx = + beforeShopId != null + ? toFlat.findIndex((s) => s.truckRowId === beforeShopId) + : targetDistrict !== undefined + ? (() => { + const targetDisplay = toDistrictDisplayName(targetDistrictRaw); + for (let i = toFlat.length - 1; i >= 0; i -= 1) { + if ( + toDistrictDisplayName(toFlat[i].districtReferenceRaw) === + targetDisplay + ) { + return i + 1; + } + } + return -1; + })() + : -1; + + const inserted = + insertIdx >= 0 + ? [...toFlat.slice(0, insertIdx), moved, ...toFlat.slice(insertIdx)] + : [...toFlat, moved]; + + const newSeq = computeMovedLoadingSequence( + inserted, + moved.truckRowId, + targetDistrict !== undefined && beforeShopId == null, + ); + + from.shops = fromFlat; + to.shops = inserted.map((s) => + s.truckRowId === moved.truckRowId ? { ...s, loadingSequence: newSeq } : s, + ); + + return next; +} + +export function setShopLoadingSequence( + state: ScheduleDragWorkspaceState, + laneId: string, + shopId: number, + loadingSequence: number, +): ScheduleDragWorkspaceState { + const seq = Math.max(0, Math.trunc(loadingSequence)); + const lane = state.find((l) => l.id === laneId); + const shop = lane?.shops.find((s) => s.truckRowId === shopId); + if (!shop || shop.loadingSequence === seq) return state; + + return state.map((l) => + l.id !== laneId + ? l + : { + ...l, + shops: l.shops.map((s) => + s.truckRowId === shopId ? { ...s, loadingSequence: seq } : s, + ), + }, + ); +} + +export function revertShop( + state: ScheduleDragWorkspaceState, + shopId: number, + currentLaneId: string, +): ScheduleDragWorkspaceState { + const currentLane = state.find((l) => l.id === currentLaneId); + const shop = currentLane?.shops.find((s) => s.truckRowId === shopId); + if (!shop) return state; + + let next = moveShop(state, { + shopId, + fromLaneId: currentLaneId, + toLaneId: shop.originalLaneId, + beforeShopId: null, + }); + + next = next.map((lane) => { + if (lane.id !== shop.originalLaneId) return lane; + return { + ...lane, + shops: lane.shops.map((s) => + s.truckRowId === shopId + ? { + ...s, + loadingSequence: shop.originalLoadingSequence, + districtReferenceRaw: shop.originalDistrictReferenceRaw, + } + : s, + ), + }; + }); + + return next; +} + +export function findShopLaneId( + state: ScheduleDragWorkspaceState, + shopId: number, +): string | null { + for (const lane of state) { + if (lane.shops.some((s) => s.truckRowId === shopId)) return lane.id; + } + return null; +} + +export function removeShopFromPlan( + state: ScheduleDragWorkspaceState, + truckRowId: number, +): { + next: ScheduleDragWorkspaceState; + removed: PlannedShop | null; + laneId: string | null; +} { + for (const lane of state) { + const shop = lane.shops.find((s) => s.truckRowId === truckRowId); + if (!shop) continue; + return { + next: state.map((l) => + l.id === lane.id + ? { ...l, shops: l.shops.filter((s) => s.truckRowId !== truckRowId) } + : l, + ), + removed: shop, + laneId: lane.id, + }; + } + return { next: state, removed: null, laneId: null }; +} + +export function restoreDeletedShop( + state: ScheduleDragWorkspaceState, + snapshot: ScheduledDeleteSnapshot, +): ScheduleDragWorkspaceState { + const lane = state.find((l) => l.id === snapshot.fromLaneId); + if (!lane) return state; + if (lane.shops.some((s) => s.truckRowId === snapshot.truckRowId)) return state; + + const shop: PlannedShop = { + ...snapshot.shop, + loadingSequence: snapshot.shop.originalLoadingSequence, + districtReferenceRaw: snapshot.shop.originalDistrictReferenceRaw, + }; + + return state.map((l) => + l.id === snapshot.fromLaneId ? { ...l, shops: [...l.shops, shop] } : l, + ); +} + +export function listModifications( + state: ScheduleDragWorkspaceState, +): ScheduleModification[] { + const laneById = new Map(state.map((l) => [l.id, l])); + const out: ScheduleModification[] = []; + + for (const lane of state) { + for (const shop of lane.shops) { + const isLaneChanged = shop.originalLaneId !== lane.id; + const isSeqChanged = shop.originalLoadingSequence !== shop.loadingSequence; + const isDistrictChanged = + (shop.originalDistrictReferenceRaw ?? "") !== + (shop.districtReferenceRaw ?? ""); + if (!isLaneChanged && !isSeqChanged && !isDistrictChanged) continue; + + const fromLane = laneById.get(shop.originalLaneId); + out.push({ + truckRowId: shop.truckRowId, + shopCode: shop.shopCode, + displayName: shop.displayName, + location: formatPlannedShopLocation(shop), + fromLaneId: shop.originalLaneId, + fromLaneLabel: fromLane?.label ?? shop.originalLaneId, + toLaneId: lane.id, + toLaneLabel: lane.label, + oldLoadingSequence: shop.originalLoadingSequence, + newLoadingSequence: shop.loadingSequence, + isLaneChanged, + isSeqChanged, + isDistrictChanged, + oldDistrictLabel: toDistrictDisplayName(shop.originalDistrictReferenceRaw), + newDistrictLabel: toDistrictDisplayName(shop.districtReferenceRaw), + }); + } + } + + return out.sort((a, b) => a.shopCode.localeCompare(b.shopCode, "zh-Hant")); +} + +export function toScheduleMappings( + modifications: ScheduleModification[], + excludeTruckRowIds: Set = new Set(), +): Record { + const out: Record = {}; + for (const mod of modifications) { + if (excludeTruckRowIds.has(mod.truckRowId)) continue; + out[mod.truckRowId] = { + laneId: mod.toLaneId, + toLoadingSequence: mod.newLoadingSequence, + }; + } + return out; +} + +export function defaultFocusedLaneIds( + lanes: ScheduleLaneOption[], +): { leftLaneId: string; rightLaneId: string } { + const ids = lanes.map((l) => l.id); + return { + leftLaneId: ids[0] ?? "", + rightLaneId: ids[1] ?? ids[0] ?? "", + }; +} diff --git a/src/components/Shop/scheduleExecuteAt.ts b/src/components/Shop/scheduleExecuteAt.ts new file mode 100644 index 0000000..e2eae36 --- /dev/null +++ b/src/components/Shop/scheduleExecuteAt.ts @@ -0,0 +1,81 @@ +/** Backend allows executeAt down to now - 2 minutes. */ +export const SCHEDULE_EXECUTE_AT_GRACE_MS = 2 * 60 * 1000; + +/** Default offset when rescheduling a past executeAt. */ +export const RESCHEDULE_DEFAULT_OFFSET_MS = 5 * 60 * 1000; + +export function parseExecuteAtIso(iso: string): Date | null { + const raw = iso.trim(); + if (!raw) return null; + const normalized = raw.includes("T") ? raw : raw.replace(" ", "T"); + const d = new Date(normalized); + return Number.isFinite(d.getTime()) ? d : null; +} + +export function formatExecuteAtIso(date: Date): string { + const y = date.getFullYear(); + const mo = String(date.getMonth() + 1).padStart(2, "0"); + const d = String(date.getDate()).padStart(2, "0"); + const h = String(date.getHours()).padStart(2, "0"); + const mi = String(date.getMinutes()).padStart(2, "0"); + const s = String(date.getSeconds()).padStart(2, "0"); + return `${y}-${mo}-${d}T${h}:${mi}:${s}`; +} + +/** Display API timestamps as `yyyy-MM-dd HH:mm:ss` (no `T`). */ +export function formatScheduleDisplayDateTime(iso: string): string { + const raw = iso.trim(); + if (!raw) return raw; + const tIdx = raw.indexOf("T"); + if (tIdx < 0) return raw; + const time = raw + .slice(tIdx + 1) + .replace(/\.\d+.*$/, "") + .replace(/Z$/i, "") + .replace(/[+-]\d{2}:\d{2}$/, ""); + return `${raw.slice(0, tIdx)} ${time}`; +} + +export function isExecuteAtTooEarly( + executeAtIso: string, + now: Date = new Date(), +): boolean { + const parsed = parseExecuteAtIso(executeAtIso); + if (!parsed) return true; + const min = new Date(now.getTime() - SCHEDULE_EXECUTE_AT_GRACE_MS); + return parsed.getTime() < min.getTime(); +} + +export function buildExecuteAtIso( + date: string, + time: string, +): string | null { + const d = date.trim(); + const t = time.trim(); + if (!d || !t) return null; + return `${d}T${t.length === 5 ? `${t}:00` : t}`; +} + +export type RescheduleExecuteAtResult = { + executeAt: string; + adjusted: boolean; +}; + +/** + * Keeps original executeAt if still valid; otherwise shifts to now + offset. + */ +export function resolveRescheduleExecuteAt( + originalIso: string, + now: Date = new Date(), + offsetMs: number = RESCHEDULE_DEFAULT_OFFSET_MS, +): RescheduleExecuteAtResult { + if (!isExecuteAtTooEarly(originalIso, now)) { + return { executeAt: originalIso.trim(), adjusted: false }; + } + const next = new Date(now.getTime() + offsetMs); + return { executeAt: formatExecuteAtIso(next), adjusted: true }; +} + +export function defaultRetryExecuteAt(now: Date = new Date()): string { + return formatExecuteAtIso(new Date(now.getTime() + 60 * 1000)); +} diff --git a/src/components/Shop/scheduleLineAdapter.ts b/src/components/Shop/scheduleLineAdapter.ts new file mode 100644 index 0000000..38ced03 --- /dev/null +++ b/src/components/Shop/scheduleLineAdapter.ts @@ -0,0 +1,24 @@ +import type { + TruckLaneMoveTargetRequest, + TruckLaneScheduleLineRequest, +} from "@/app/api/shop/actions"; + +export function moveToScheduleLine( + move: TruckLaneMoveTargetRequest, +): TruckLaneScheduleLineRequest { + return { + action: "MOVE", + truckRowId: move.truckRowId, + toTruckLanceCode: move.toTruckLanceCode, + toRemark: move.toRemark ?? null, + toStoreId: move.toStoreId, + toLoadingSequence: move.toLoadingSequence, + toDistrictReference: move.toDistrictReference ?? null, + }; +} + +export function movesToScheduleLines( + moves: TruckLaneMoveTargetRequest[], +): TruckLaneScheduleLineRequest[] { + return moves.map(moveToScheduleLine); +} diff --git a/src/components/Shop/scheduleLineDisplay.ts b/src/components/Shop/scheduleLineDisplay.ts new file mode 100644 index 0000000..6504aa9 --- /dev/null +++ b/src/components/Shop/scheduleLineDisplay.ts @@ -0,0 +1,145 @@ +import type { TruckLaneScheduleLineResponse } from "@/app/api/shop/actions"; +import type { ScheduleLaneOption } from "@/components/Shop/ScheduleChangeModal"; +import { toDistrictDisplayName } from "@/components/Shop/routeBoardDisplayOrder"; +import { formatScheduleTargetLaneLabel } from "@/components/Shop/scheduleRouteExcelToMoves"; + +export function resolveScheduleLaneLabel( + line: TruckLaneScheduleLineResponse, + kind: "from" | "to", + laneById?: Map, +): string { + const code = + kind === "from" ? line.fromTruckLanceCode : line.toTruckLanceCode; + const remark = kind === "from" ? line.fromRemark : line.toRemark; + const storeId = kind === "from" ? line.fromStoreId : line.toStoreId; + if (!code?.trim()) return "—"; + if (laneById) { + const match = Array.from(laneById.values()).find( + (l) => + l.truckLanceCode === code && + (l.remark ?? "") === (remark ?? "") && + l.storeId === storeId, + ); + if (match) return match.label; + } + return formatScheduleTargetLaneLabel(code, remark); +} + +export function isSameScheduleLaneBucket( + line: TruckLaneScheduleLineResponse, +): boolean { + const norm = (v?: string | null) => (v ?? "").trim(); + return ( + norm(line.fromTruckLanceCode) === norm(line.toTruckLanceCode) && + norm(line.fromRemark) === norm(line.toRemark) && + norm(line.fromStoreId) === norm(line.toStoreId) + ); +} + +function normDistrict(v?: string | null): string { + return toDistrictDisplayName(v ?? null) || "—"; +} + +function formatTimeLabel(v?: string | null): string | null { + const raw = v?.trim(); + if (!raw) return null; + const tIdx = raw.indexOf("T"); + if (tIdx >= 0) return raw.slice(tIdx + 1, tIdx + 6); + return raw.length >= 5 ? raw.slice(0, 5) : raw; +} + +function formatSeqValue(from?: number | null, to?: number | null): string | null { + if (from != null && to != null && from !== to) return `${from} → ${to}`; + if (to != null) return String(to); + if (from != null) return String(from); + return null; +} + +export type ScheduleLineDescription = { + headline: string; + details: string[]; +}; + +type TranslateFn = (key: string, opts?: Record) => string; + +export function buildScheduleLineDescription( + line: TruckLaneScheduleLineResponse, + laneById: Map | undefined, + t: TranslateFn, +): ScheduleLineDescription { + const toLane = resolveScheduleLaneLabel(line, "to", laneById); + const fromLane = resolveScheduleLaneLabel(line, "from", laneById); + const details: string[] = []; + + const pushSeq = () => { + const seq = formatSeqValue(line.fromLoadingSequence, line.toLoadingSequence); + if (seq) details.push(t("schedule_line_seq", { value: seq })); + }; + + const pushDistrict = () => { + const fromDist = normDistrict(line.fromDistrictReference); + const toDist = normDistrict(line.toDistrictReference); + if (fromDist !== toDist) { + details.push( + t("schedule_line_district_change", { from: fromDist, to: toDist }), + ); + } else if (line.action === "CREATE" && toDist !== "—") { + details.push(t("schedule_line_district_target", { district: toDist })); + } + }; + + const pushDeparture = () => { + const fromDep = formatTimeLabel(line.fromDepartureTime); + const toDep = formatTimeLabel(line.departureTime); + if (fromDep && toDep && fromDep !== toDep) { + details.push( + t("schedule_line_departure_change", { from: fromDep, to: toDep }), + ); + } else if (toDep && line.action === "CREATE") { + details.push(t("schedule_line_departure_target", { time: toDep })); + } + }; + + switch (line.action) { + case "DELETE": + return { + headline: t("schedule_line_remove_from", { + lane: fromLane !== "—" ? fromLane : toLane, + }), + details, + }; + case "ENSURE_LANE": + pushDeparture(); + return { + headline: t("schedule_line_ensure_lane", { lane: toLane }), + details, + }; + case "CREATE": + pushSeq(); + pushDistrict(); + pushDeparture(); + return { + headline: t("schedule_line_add_to", { lane: toLane }), + details, + }; + case "MOVE": + default: { + if (isSameScheduleLaneBucket(line)) { + pushSeq(); + pushDistrict(); + pushDeparture(); + return { + headline: t("schedule_line_on_lane", { lane: toLane }), + details, + }; + } + pushSeq(); + pushDistrict(); + pushDeparture(); + return { + headline: t("schedule_line_lane_change", { from: fromLane, to: toLane }), + details, + }; + } + } +} diff --git a/src/components/Shop/scheduleLineValidation.ts b/src/components/Shop/scheduleLineValidation.ts new file mode 100644 index 0000000..17ab799 --- /dev/null +++ b/src/components/Shop/scheduleLineValidation.ts @@ -0,0 +1,230 @@ +import type { + TruckLaneScheduleLineAction, + TruckLaneScheduleLineRequest, +} from "@/app/api/shop/actions"; +import type { ScheduleLaneOption } from "@/components/Shop/ScheduleChangeModal"; +import type { ScheduleDragWorkspaceState } from "@/components/Shop/scheduleDragWorkspace"; +import { isExecuteAtTooEarly } from "@/components/Shop/scheduleExecuteAt"; + +export type ScheduleLineValidationError = { + truckRowId: number; + messageKey: string; + messageParams?: Record; +}; + +export type ValidateScheduleLinesInput = { + lines: TruckLaneScheduleLineRequest[]; + lanes: ScheduleLaneOption[]; + plannedLanes?: ScheduleDragWorkspaceState; + pendingTruckRowIds: Set; + executeAt: string; + shopCodeByTruckRowId?: Map; +}; + +export type ValidateScheduleLinesResult = + | { ok: true } + | { ok: false; errors: ScheduleLineValidationError[] }; + +function laneBucketKey(lane: ScheduleLaneOption): string { + return `${lane.truckLanceCode}|${lane.storeId}|${lane.remark ?? ""}`; +} + +function lineTargetBucketKey(line: TruckLaneScheduleLineRequest): string { + return `${line.toTruckLanceCode.trim()}|${line.toStoreId.trim()}|${(line.toRemark ?? "").trim()}`; +} + +function findLaneForLine( + line: TruckLaneScheduleLineRequest, + lanes: ScheduleLaneOption[], +): ScheduleLaneOption | undefined { + const code = line.toTruckLanceCode.trim(); + const storeId = line.toStoreId.trim(); + const remark = (line.toRemark ?? "").trim(); + return lanes.find( + (l) => + l.truckLanceCode.trim() === code && + l.storeId.trim() === storeId && + (l.remark ?? "").trim() === remark, + ); +} + +function effectiveShopsOnLane( + lane: ScheduleLaneOption, + plannedLanes?: ScheduleDragWorkspaceState, +): ScheduleLaneOption["shops"] { + const planned = plannedLanes?.find((l) => l.id === lane.id); + if (planned) { + return planned.shops.map((s) => ({ + truckRowId: s.truckRowId, + districtReferenceRaw: s.districtReferenceRaw, + loadingSequence: s.loadingSequence, + })); + } + return lane.shops; +} + +export function validateScheduleLines( + input: ValidateScheduleLinesInput, +): ValidateScheduleLinesResult { + const errors: ScheduleLineValidationError[] = []; + const { + lines, + lanes, + plannedLanes, + pendingTruckRowIds, + executeAt, + shopCodeByTruckRowId = new Map(), + } = input; + + if (lines.length === 0) { + errors.push({ + truckRowId: 0, + messageKey: "schedule_err_no_moves", + }); + } + + if (isExecuteAtTooEarly(executeAt)) { + errors.push({ + truckRowId: 0, + messageKey: "schedule_err_execute_at_past", + }); + } + + const ensuredLaneKeys = new Set( + lines + .filter((l) => l.action === "ENSURE_LANE") + .map((l) => lineTargetBucketKey(l)), + ); + + const shopCodesInTargetBucket = new Map>(); + + for (const line of lines) { + const action = line.action as TruckLaneScheduleLineAction; + const truckRowId = line.truckRowId ?? 0; + + if ( + (action === "MOVE" || action === "DELETE") && + truckRowId > 0 && + pendingTruckRowIds.has(truckRowId) + ) { + errors.push({ + truckRowId, + messageKey: "schedule_err_open_pending", + messageParams: { id: truckRowId }, + }); + } + + if (action === "ENSURE_LANE" || action === "DELETE") { + continue; + } + + const lane = findLaneForLine(line, lanes); + const targetKey = lineTargetBucketKey(line); + const laneEnsured = ensuredLaneKeys.has(targetKey); + + if (!lane && !laneEnsured) { + errors.push({ + truckRowId, + messageKey: "schedule_err_target_lane_missing", + messageParams: { lane: line.toTruckLanceCode }, + }); + continue; + } + + if (lane) { + const persistedShops = effectiveShopsOnLane(lane, plannedLanes).filter( + (s) => s.truckRowId > 0, + ); + if (persistedShops.length === 0 && !laneEnsured && action !== "CREATE") { + errors.push({ + truckRowId, + messageKey: "schedule_err_target_lane_empty", + messageParams: { lane: lane.label }, + }); + continue; + } + } + + if (action === "MOVE" && truckRowId > 0) { + const shopCode = shopCodeByTruckRowId.get(truckRowId)?.trim(); + if (shopCode && lane) { + const bucket = laneBucketKey(lane); + const laneShops = effectiveShopsOnLane(lane, plannedLanes); + const duplicateOnLane = laneShops.some((s) => { + if (s.truckRowId === truckRowId) return false; + const otherCode = shopCodeByTruckRowId.get(s.truckRowId)?.trim(); + return otherCode === shopCode; + }); + const seen = shopCodesInTargetBucket.get(bucket) ?? new Set(); + const duplicateInBatch = seen.has(shopCode); + + if (duplicateOnLane || duplicateInBatch) { + errors.push({ + truckRowId, + messageKey: "schedule_err_duplicate_shop", + messageParams: { shop: shopCode }, + }); + } else { + seen.add(shopCode); + shopCodesInTargetBucket.set(bucket, seen); + } + } + } + + if (action === "CREATE") { + const shopCode = line.shopCode?.trim(); + const bucketKey = targetKey; + if (shopCode) { + const laneForCreate = lane ?? findLaneForLine(line, lanes); + if (laneForCreate) { + const bucket = laneBucketKey(laneForCreate); + const laneShops = effectiveShopsOnLane(laneForCreate, plannedLanes); + const duplicateOnLane = laneShops.some((s) => { + const otherCode = shopCodeByTruckRowId.get(s.truckRowId)?.trim(); + return otherCode === shopCode; + }); + const seen = shopCodesInTargetBucket.get(bucket) ?? new Set(); + if (duplicateOnLane || seen.has(shopCode)) { + errors.push({ + truckRowId: 0, + messageKey: "schedule_err_duplicate_shop", + messageParams: { shop: shopCode }, + }); + } else { + seen.add(shopCode); + shopCodesInTargetBucket.set(bucket, seen); + } + } else { + const seen = shopCodesInTargetBucket.get(bucketKey) ?? new Set(); + if (seen.has(shopCode)) { + errors.push({ + truckRowId: 0, + messageKey: "schedule_err_duplicate_shop", + messageParams: { shop: shopCode }, + }); + } else { + seen.add(shopCode); + shopCodesInTargetBucket.set(bucketKey, seen); + } + } + } + } + } + + if (errors.length > 0) { + return { ok: false, errors }; + } + return { ok: true }; +} + +export function formatScheduleLineValidationErrors( + t: (key: string, params?: Record) => string, + errors: ScheduleLineValidationError[], +): string { + return errors + .map((e) => { + const params = e.messageParams ?? {}; + return t(e.messageKey, params); + }) + .join("\n"); +} diff --git a/src/components/Shop/scheduleMoveValidation.ts b/src/components/Shop/scheduleMoveValidation.ts new file mode 100644 index 0000000..26aa485 --- /dev/null +++ b/src/components/Shop/scheduleMoveValidation.ts @@ -0,0 +1,131 @@ +import type { TruckLaneMoveTargetRequest } from "@/app/api/shop/actions"; +import type { ScheduleLaneOption } from "@/components/Shop/ScheduleChangeModal"; +import { isExecuteAtTooEarly } from "@/components/Shop/scheduleExecuteAt"; + +export type ScheduleMoveValidationError = { + truckRowId: number; + messageKey: string; + messageParams?: Record; +}; + +export type ValidateScheduleMovesInput = { + moves: TruckLaneMoveTargetRequest[]; + lanes: ScheduleLaneOption[]; + /** truck row ids with an open pending schedule on the server */ + pendingTruckRowIds: Set; + executeAt: string; + /** shopCode by truckRowId for duplicate detection */ + shopCodeByTruckRowId?: Map; +}; + +export type ValidateScheduleMovesResult = + | { ok: true } + | { ok: false; errors: ScheduleMoveValidationError[] }; + +function laneBucketKey(lane: ScheduleLaneOption): string { + return `${lane.truckLanceCode}|${lane.storeId}|${lane.remark ?? ""}`; +} + +function findLaneForMove( + move: TruckLaneMoveTargetRequest, + lanes: ScheduleLaneOption[], +): ScheduleLaneOption | undefined { + const code = move.toTruckLanceCode.trim(); + const storeId = move.toStoreId.trim(); + const remark = (move.toRemark ?? "").trim(); + return lanes.find( + (l) => + l.truckLanceCode.trim() === code && + l.storeId.trim() === storeId && + (l.remark ?? "").trim() === remark, + ); +} + +export function validateScheduleMoves( + input: ValidateScheduleMovesInput, +): ValidateScheduleMovesResult { + const errors: ScheduleMoveValidationError[] = []; + const { + moves, + lanes, + pendingTruckRowIds, + executeAt, + shopCodeByTruckRowId = new Map(), + } = input; + + if (moves.length === 0) { + errors.push({ + truckRowId: 0, + messageKey: "schedule_err_no_moves", + }); + } + + if (isExecuteAtTooEarly(executeAt)) { + errors.push({ + truckRowId: 0, + messageKey: "schedule_err_execute_at_past", + }); + } + + const shopCodesInTargetBucket = new Map>(); + + for (const move of moves) { + const truckRowId = move.truckRowId; + + if (pendingTruckRowIds.has(truckRowId)) { + errors.push({ + truckRowId, + messageKey: "schedule_err_open_pending", + messageParams: { id: truckRowId }, + }); + } + + const lane = findLaneForMove(move, lanes); + if (!lane) { + errors.push({ + truckRowId, + messageKey: "schedule_err_target_lane_missing", + messageParams: { lane: move.toTruckLanceCode }, + }); + continue; + } + + const persistedShops = lane.shops.filter((s) => s.truckRowId > 0); + if (persistedShops.length === 0) { + errors.push({ + truckRowId, + messageKey: "schedule_err_target_lane_empty", + messageParams: { lane: lane.label }, + }); + continue; + } + + const shopCode = shopCodeByTruckRowId.get(truckRowId)?.trim(); + if (shopCode) { + const bucket = laneBucketKey(lane); + const duplicateOnLane = lane.shops.some((s) => { + if (s.truckRowId === truckRowId) return false; + const otherCode = shopCodeByTruckRowId.get(s.truckRowId)?.trim(); + return otherCode === shopCode; + }); + const seen = shopCodesInTargetBucket.get(bucket) ?? new Set(); + const duplicateInBatch = seen.has(shopCode); + + if (duplicateOnLane || duplicateInBatch) { + errors.push({ + truckRowId, + messageKey: "schedule_err_duplicate_shop", + messageParams: { shop: shopCode }, + }); + } else { + seen.add(shopCode); + shopCodesInTargetBucket.set(bucket, seen); + } + } + } + + if (errors.length > 0) { + return { ok: false, errors }; + } + return { ok: true }; +} diff --git a/src/components/Shop/scheduleRouteExcelToMoves.ts b/src/components/Shop/scheduleRouteExcelToMoves.ts new file mode 100644 index 0000000..fdb689e --- /dev/null +++ b/src/components/Shop/scheduleRouteExcelToMoves.ts @@ -0,0 +1,99 @@ +import type { RouteLaneImportPreviewRow } from "@/app/api/shop/client"; +import type { TruckLaneMoveTargetRequest } from "@/app/api/shop/actions"; +import { normalizeStoreId } from "@/app/utils/formatUtil"; + +export type ScheduleRouteExcelRowError = { + shopCode: string; + shopName: string; + message: string; +}; + +export type ScheduleRouteExcelMovePreview = { + truckRowId: number; + shopCode: string; + shopName: string; + toTruckLanceCode: string; + toRemark: string | null; + toStoreId: string; + toLoadingSequence: number; +}; + +export type ScheduleRouteExcelToMovesResult = { + moves: TruckLaneMoveTargetRequest[]; + previews: ScheduleRouteExcelMovePreview[]; + rowErrors: ScheduleRouteExcelRowError[]; +}; + +export function formatScheduleTargetLaneLabel( + truckLanceCode: string, + remark: string | null | undefined, +): string { + const code = String(truckLanceCode || "").trim(); + const rem = remark != null && String(remark).trim() !== "" ? String(remark).trim() : ""; + return rem ? `${code} (${rem})` : code; +} + +/** + * Converts RouteBoard MTMS Excel parse rows into truck-lane schedule moves. + * Rows without an existing truck board row are reported as errors. + */ +export function scheduleRouteExcelToMoves( + rows: RouteLaneImportPreviewRow[], +): ScheduleRouteExcelToMovesResult { + const moves: TruckLaneMoveTargetRequest[] = []; + const previews: ScheduleRouteExcelMovePreview[] = []; + const rowErrors: ScheduleRouteExcelRowError[] = []; + + for (const row of rows) { + const shopCode = String(row.shopCode || "").trim() || "-"; + const shopName = String(row.shopName || "").trim() || "-"; + const truckRowId = row.truckRowId; + + if (truckRowId == null || !Number.isFinite(truckRowId) || truckRowId <= 0) { + rowErrors.push({ + shopCode, + shopName, + message: "no_truck_row", + }); + continue; + } + + const toTruckLanceCode = String(row.truckLanceCode || "").trim(); + if (!toTruckLanceCode) { + rowErrors.push({ + shopCode, + shopName, + message: "no_target_lane", + }); + continue; + } + + const toRemark = + row.remark != null && String(row.remark).trim() !== "" + ? String(row.remark).trim() + : null; + const toStoreId = normalizeStoreId(row.storeId); + const seq = Math.max(0, Number(row.loadingSequence ?? 0) || 0); + + const move: TruckLaneMoveTargetRequest = { + truckRowId, + toTruckLanceCode, + toRemark, + toStoreId, + toLoadingSequence: seq, + toDistrictReference: row.districtReference ?? null, + }; + moves.push(move); + previews.push({ + truckRowId, + shopCode, + shopName, + toTruckLanceCode, + toRemark, + toStoreId, + toLoadingSequence: seq, + }); + } + + return { moves, previews, rowErrors }; +} diff --git a/src/components/Shop/scheduleUiHelpers.ts b/src/components/Shop/scheduleUiHelpers.ts new file mode 100644 index 0000000..9753a2b --- /dev/null +++ b/src/components/Shop/scheduleUiHelpers.ts @@ -0,0 +1,21 @@ +import type { TFunction } from "i18next"; + +export type ScheduleValidationError = { + truckRowId: number; + messageKey: string; + messageParams?: Record; +}; + +export function formatScheduleValidationError( + t: TFunction<"shop">, + error: ScheduleValidationError, +): string { + return t(error.messageKey, (error.messageParams ?? {}) as Record); +} + +export function formatScheduleValidationErrors( + t: TFunction<"shop">, + errors: ScheduleValidationError[], +): string { + return errors.map((e) => formatScheduleValidationError(t, e)).join("\n"); +} diff --git a/src/components/Shop/truckLaneMovePlanner.ts b/src/components/Shop/truckLaneMovePlanner.ts new file mode 100644 index 0000000..fac137c --- /dev/null +++ b/src/components/Shop/truckLaneMovePlanner.ts @@ -0,0 +1,173 @@ +/** + * TruckLaneMovePlanner — single seam for planning schedule moves from the board UI. + */ +import type { + CreateTruckLaneScheduleRequest, + TruckLaneMoveTargetRequest, +} from "@/app/api/shop/actions"; +import type { + ScheduleChangePayload, + ScheduleLaneOption, + ScheduleShopRow, +} from "@/components/Shop/ScheduleChangeModal"; +import { + buildCreateScheduleRequest, + buildCreateScheduleRequestFromModifications, + buildScheduleLinesFromPlan, +} from "@/components/Shop/scheduleApiAdapter"; +import { + initPlannedLanes, + listModifications, + moveShop, + revertShop, + type ScheduleDragWorkspaceState, + type ScheduleModification, + type ScheduledDeleteSnapshot, +} from "@/components/Shop/scheduleDragWorkspace"; +import { buildExecuteAtIso } from "@/components/Shop/scheduleExecuteAt"; +import { + validateScheduleLines, + type ScheduleLineValidationError, +} from "@/components/Shop/scheduleLineValidation"; + +export type PlanScheduleSubmitInput = { + lanes: ScheduleLaneOption[]; + shops: ScheduleShopRow[]; + plannedLanes: ScheduleDragWorkspaceState; + pendingDeletes: ScheduledDeleteSnapshot[]; + date: string; + time: string; + pendingTruckRowIds: Set; + note?: string | null; +}; + +export type PlanScheduleSubmitResult = + | { + ok: true; + request: CreateTruckLaneScheduleRequest; + modifications: ScheduleModification[]; + } + | { ok: false; errors: ScheduleLineValidationError[] }; + +export function initPlannerState( + lanes: ScheduleLaneOption[], + shops: ScheduleShopRow[], +): ScheduleDragWorkspaceState { + return initPlannedLanes(lanes, shops); +} + +export function planMove( + state: ScheduleDragWorkspaceState, + args: Parameters[1], +): ScheduleDragWorkspaceState { + return moveShop(state, args); +} + +export function planRevert( + state: ScheduleDragWorkspaceState, + shopId: number, + currentLaneId: string, +): ScheduleDragWorkspaceState { + return revertShop(state, shopId, currentLaneId); +} + +export function listPlannedModifications( + state: ScheduleDragWorkspaceState, +): ScheduleModification[] { + return listModifications(state); +} + +export function buildShopCodeByTruckRowId( + shops: ScheduleShopRow[], +): Map { + return new Map(shops.map((s) => [s.truckRowId, s.shopCode])); +} + +export function validatePlannedSubmit( + input: PlanScheduleSubmitInput, +): PlanScheduleSubmitResult { + const executeAt = buildExecuteAtIso(input.date, input.time); + if (!executeAt) { + return { + ok: false, + errors: [{ truckRowId: 0, messageKey: "schedule_err_execute_at_past" }], + }; + } + const modifications = listModifications(input.plannedLanes); + const lines = buildScheduleLinesFromPlan({ + modifications, + pendingDeletes: input.pendingDeletes, + lanes: input.lanes, + plannedLanes: input.plannedLanes, + }); + const validation = validateScheduleLines({ + lines, + lanes: input.lanes, + plannedLanes: input.plannedLanes, + pendingTruckRowIds: input.pendingTruckRowIds, + executeAt, + shopCodeByTruckRowId: buildShopCodeByTruckRowId(input.shops), + }); + if (!validation.ok) { + return validation; + } + return { + ok: true, + modifications, + request: buildCreateScheduleRequestFromModifications({ + executeAt, + note: input.note, + modifications, + lanes: input.lanes, + plannedLanes: input.plannedLanes, + pendingDeletes: input.pendingDeletes, + }), + }; +} + +export function validatePayloadSubmit(input: { + payload: ScheduleChangePayload; + lanes: ScheduleLaneOption[]; + shops: ScheduleShopRow[]; + pendingTruckRowIds: Set; +}): { ok: true } | { ok: false; errors: ScheduleLineValidationError[] } { + const executeAt = buildExecuteAtIso(input.payload.date, input.payload.time); + if (!executeAt) { + return { + ok: false, + errors: [{ truckRowId: 0, messageKey: "schedule_err_execute_at_past" }], + }; + } + const lines = buildScheduleLinesFromPlan({ + modifications: listModifications(input.payload.plannedLanes), + pendingDeletes: input.payload.pendingDeletes, + lanes: input.lanes, + plannedLanes: input.payload.plannedLanes, + }); + return validateScheduleLines({ + lines, + lanes: input.lanes, + plannedLanes: input.payload.plannedLanes, + pendingTruckRowIds: input.pendingTruckRowIds, + executeAt, + shopCodeByTruckRowId: buildShopCodeByTruckRowId(input.shops), + }); +} + +export function buildRequestFromPayload( + payload: ScheduleChangePayload, + lanes: ScheduleLaneOption[], +): CreateTruckLaneScheduleRequest | null { + const executeAt = buildExecuteAtIso(payload.date, payload.time); + if (!executeAt) return null; + return buildCreateScheduleRequest({ + executeAt, + mappings: payload.mappings, + lanes, + plannedLanes: payload.plannedLanes, + pendingDeletes: payload.pendingDeletes, + modifications: listModifications(payload.plannedLanes), + }); +} + +export type { TruckLaneMoveTargetRequest, ScheduleModification }; diff --git a/src/components/Shop/useRouteBoardScheduleIndicators.ts b/src/components/Shop/useRouteBoardScheduleIndicators.ts new file mode 100644 index 0000000..dd6e606 --- /dev/null +++ b/src/components/Shop/useRouteBoardScheduleIndicators.ts @@ -0,0 +1,82 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { + getTruckLaneScheduleClient, + listTruckLaneSchedulesClient, + pendingTruckLaneScheduleShopIdsClient, +} from "@/app/api/shop/client"; +import { + collectFailedTruckRowIdsFromSchedules, + filterFailedSchedules, +} from "@/components/Shop/scheduleClientHelpers"; + +export function useRouteBoardScheduleIndicators(options?: { + pollMs?: number; + applyingPollMs?: number; + paused?: boolean; +}) { + const pollMs = options?.pollMs ?? 60_000; + const applyingPollMs = options?.applyingPollMs ?? 5_000; + const paused = options?.paused ?? false; + const [hasApplyingSchedule, setHasApplyingSchedule] = useState(false); + + const [pendingScheduleShopIds, setPendingScheduleShopIds] = useState< + Set + >(new Set()); + const [failedScheduleShopIds, setFailedScheduleShopIds] = useState< + Set + >(new Set()); + const [failedScheduleCount, setFailedScheduleCount] = useState(0); + + const refreshScheduleIndicators = useCallback(async () => { + try { + const [pendingIds, scheduleList] = await Promise.all([ + pendingTruckLaneScheduleShopIdsClient(), + listTruckLaneSchedulesClient( + ["PENDING", "APPLYING", "FAILED", "PARTIAL"], + 200, + ), + ]); + setPendingScheduleShopIds(new Set(pendingIds.truckRowIds ?? [])); + + const failedSchedules = filterFailedSchedules(scheduleList); + setFailedScheduleCount(failedSchedules.length); + const needsLineWarm = failedSchedules.filter( + (s) => !s.lineCounts || (s.lineCounts.failed ?? 0) > 0, + ); + const details = await Promise.all( + needsLineWarm.map((s) => + getTruckLaneScheduleClient(s.id).catch(() => null), + ), + ); + setFailedScheduleShopIds( + collectFailedTruckRowIdsFromSchedules(failedSchedules, details), + ); + setHasApplyingSchedule( + scheduleList.some((s) => String(s.status).toUpperCase() === "APPLYING"), + ); + } catch (e) { + console.warn("refreshScheduleIndicators failed", e); + } + }, []); + + const pausedRef = useRef(paused); + pausedRef.current = paused; + + useEffect(() => { + if (paused) return; + void refreshScheduleIndicators(); + const intervalMs = hasApplyingSchedule ? applyingPollMs : pollMs; + const timer = window.setInterval(() => { + if (pausedRef.current) return; + void refreshScheduleIndicators(); + }, intervalMs); + return () => window.clearInterval(timer); + }, [paused, pollMs, applyingPollMs, hasApplyingSchedule, refreshScheduleIndicators]); + + return { + pendingScheduleShopIds, + failedScheduleShopIds, + failedScheduleCount, + refreshScheduleIndicators, + }; +} diff --git a/src/components/StockIssue/BadItemHandleForm.tsx b/src/components/StockIssue/BadItemHandleForm.tsx index 4346528..4d16685 100644 --- a/src/components/StockIssue/BadItemHandleForm.tsx +++ b/src/components/StockIssue/BadItemHandleForm.tsx @@ -62,7 +62,7 @@ const normalizeStatus = (status: string | undefined | null): string => { }; const BadItemHandleForm: React.FC = () => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["stockIssue", "common"]); const { data: session } = useSession() as { data: SessionWithTokens | null }; const currentUserId = session?.id ? parseInt(session.id) : undefined; diff --git a/src/components/StockIssue/BadItemHandleModal.tsx b/src/components/StockIssue/BadItemHandleModal.tsx index 0744e36..b3ee2b5 100644 --- a/src/components/StockIssue/BadItemHandleModal.tsx +++ b/src/components/StockIssue/BadItemHandleModal.tsx @@ -36,7 +36,7 @@ const BadItemHandleModal: React.FC = ({ currentUserId, onSuccess, }) => { - const { t } = useTranslation("inventory"); + const { t } = useTranslation("stockIssue"); const inFlightRef = useRef(false); const [qty, setQty] = useState(""); const [remarks, setRemarks] = useState(""); diff --git a/src/components/StockIssue/ExpiryHandleTab.tsx b/src/components/StockIssue/ExpiryHandleTab.tsx index f4e7670..a582c4e 100644 --- a/src/components/StockIssue/ExpiryHandleTab.tsx +++ b/src/components/StockIssue/ExpiryHandleTab.tsx @@ -27,7 +27,7 @@ type SearchParamNames = keyof SearchQuery; const ExpiryHandleTab: React.FC = () => { const BATCH_CHUNK_SIZE = 20; - const { t } = useTranslation("inventory"); + const { t } = useTranslation("stockIssue"); const { data: session } = useSession() as { data: SessionWithTokens | null }; const currentUserId = session?.id ? parseInt(session.id) : undefined; diff --git a/src/components/StockIssue/SearchPage.tsx b/src/components/StockIssue/SearchPage.tsx index 8567eac..50e571c 100644 --- a/src/components/StockIssue/SearchPage.tsx +++ b/src/components/StockIssue/SearchPage.tsx @@ -10,7 +10,7 @@ import ExpiryHandleTab from "./ExpiryHandleTab"; type TabValue = "badHandle" | "badRecord" | "expiryHandle" | "expiryRecord"; const SearchPage: React.FC = () => { - const { t } = useTranslation("inventory"); + const { t } = useTranslation("stockIssue"); const [tab, setTab] = useState("badHandle"); const handleTabChange = useCallback((_: React.SyntheticEvent, value: string) => { diff --git a/src/components/StockIssue/StockIssueInventoryTable.tsx b/src/components/StockIssue/StockIssueInventoryTable.tsx index f3bc023..4112536 100644 --- a/src/components/StockIssue/StockIssueInventoryTable.tsx +++ b/src/components/StockIssue/StockIssueInventoryTable.tsx @@ -24,7 +24,7 @@ const StockIssueInventoryTable: React.FC = ({ totalCount, onRowClick, }) => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["stockIssue", "common"]); const columns = useMemo[]>( () => [ diff --git a/src/components/StockIssue/StockIssueLotLineTable.tsx b/src/components/StockIssue/StockIssueLotLineTable.tsx index 7433523..a195c38 100644 --- a/src/components/StockIssue/StockIssueLotLineTable.tsx +++ b/src/components/StockIssue/StockIssueLotLineTable.tsx @@ -50,7 +50,7 @@ const StockIssueLotLineTable: React.FC = ({ onBadItemHandleSuccess, onLotLinesChanged, }) => { - const { t } = useTranslation("inventory"); + const { t } = useTranslation(["stockIssue", "common"]); const [modalOpen, setModalOpen] = useState(false); const [selectedLotLine, setSelectedLotLine] = useState(null); @@ -202,7 +202,7 @@ const StockIssueLotLineTable: React.FC = ({ {inventory - ? `${t("Item selected")}: ${inventory.itemCode} | ${inventory.itemName} (${t(inventory.itemType)})` + ? `${t("Item selected")}: ${inventory.itemCode} | ${inventory.itemName} (${t(inventory.itemType, { ns: "common", defaultValue: inventory.itemType })})` : t("No items are selected yet.")} diff --git a/src/components/StockIssue/StockIssueRecordTab.tsx b/src/components/StockIssue/StockIssueRecordTab.tsx index 31332a9..145441a 100644 --- a/src/components/StockIssue/StockIssueRecordTab.tsx +++ b/src/components/StockIssue/StockIssueRecordTab.tsx @@ -30,7 +30,7 @@ interface Props { } const StockIssueRecordTab: React.FC = ({ kind }) => { - const { t } = useTranslation("inventory"); + const { t } = useTranslation("stockIssue"); const [items, setItems] = useState([]); const [totalCount, setTotalCount] = useState(0); const [paging, setPaging] = useState({ pageNum: 1, pageSize: 10 }); diff --git a/src/components/StockIssue/StockIssueSearchPanel.tsx b/src/components/StockIssue/StockIssueSearchPanel.tsx index a7355f8..c0a2998 100644 --- a/src/components/StockIssue/StockIssueSearchPanel.tsx +++ b/src/components/StockIssue/StockIssueSearchPanel.tsx @@ -53,7 +53,7 @@ function StockIssueSearchPanel({ extraActions, disabled = false, }: Props) { - const { t } = useTranslation("inventory"); + const { t } = useTranslation("stockIssue"); const { t: tCommon } = useTranslation("common"); // All const emptyValues = useMemo(() => { diff --git a/src/components/StockRecord/SearchPage.tsx b/src/components/StockRecord/SearchPage.tsx index 975c1b4..dc1161c 100644 --- a/src/components/StockRecord/SearchPage.tsx +++ b/src/components/StockRecord/SearchPage.tsx @@ -30,7 +30,7 @@ interface ExtendedStockTransaction extends StockTransactionResponse { } const SearchPage: React.FC = ({ dataList: initialDataList }) => { - const { t } = useTranslation("inventory"); + const { t } = useTranslation(["stockRecord", "common"]); // 添加数据状态 const [dataList, setDataList] = useState(initialDataList); diff --git a/src/components/StockTakeManagement/ApproverAllCardList.tsx b/src/components/StockTakeManagement/ApproverAllCardList.tsx index ac8ca62..29ab422 100644 --- a/src/components/StockTakeManagement/ApproverAllCardList.tsx +++ b/src/components/StockTakeManagement/ApproverAllCardList.tsx @@ -30,7 +30,7 @@ interface ApproverAllCardListProps { const ApproverAllCardList: React.FC = ({ onCardClick, }) => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["stockTake", "common"]); const [loading, setLoading] = useState(false); const [sessions, setSessions] = useState([]); const [page, setPage] = useState(0); diff --git a/src/components/StockTakeManagement/ApproverCardList.tsx b/src/components/StockTakeManagement/ApproverCardList.tsx index 44db58a..6b562d9 100644 --- a/src/components/StockTakeManagement/ApproverCardList.tsx +++ b/src/components/StockTakeManagement/ApproverCardList.tsx @@ -31,7 +31,7 @@ interface ApproverCardListProps { } const ApproverCardList: React.FC = ({ onCardClick }) => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["stockTake", "common"]); const [loading, setLoading] = useState(false); const [stockTakeSessions, setStockTakeSessions] = useState([]); diff --git a/src/components/StockTakeManagement/ApproverStockTake.tsx b/src/components/StockTakeManagement/ApproverStockTake.tsx index 700fd13..e2bdbb1 100644 --- a/src/components/StockTakeManagement/ApproverStockTake.tsx +++ b/src/components/StockTakeManagement/ApproverStockTake.tsx @@ -51,7 +51,7 @@ const ApproverStockTake: React.FC = ({ onBack, onSnackbar, }) => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["stockTake", "common"]); const { data: session } = useSession() as { data: SessionWithTokens | null }; const [inventoryLotDetails, setInventoryLotDetails] = useState([]); diff --git a/src/components/StockTakeManagement/ApproverStockTakeAll.tsx b/src/components/StockTakeManagement/ApproverStockTakeAll.tsx index 8bcf5d8..91f5748 100644 --- a/src/components/StockTakeManagement/ApproverStockTakeAll.tsx +++ b/src/components/StockTakeManagement/ApproverStockTakeAll.tsx @@ -225,7 +225,7 @@ const ApproverStockTakeAll: React.FC = ({ onBack, onSnackbar, }) => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["stockTake", "common"]); const { data: session } = useSession() as { data: SessionWithTokens | null }; const [inventoryLotDetails, setInventoryLotDetails] = useState([]); diff --git a/src/components/StockTakeManagement/InventoryAdjustmentsTab.tsx b/src/components/StockTakeManagement/InventoryAdjustmentsTab.tsx index 42ae4cb..44631f8 100644 --- a/src/components/StockTakeManagement/InventoryAdjustmentsTab.tsx +++ b/src/components/StockTakeManagement/InventoryAdjustmentsTab.tsx @@ -114,7 +114,7 @@ const fakeAdjustments: InventoryAdjustment[] = [ ]; const InventoryAdjustmentsTab: React.FC = () => { - const { t } = useTranslation(["inventory"]); + const { t } = useTranslation(["stockTake"]); // States const [adjustments] = useState(fakeAdjustments); diff --git a/src/components/StockTakeManagement/PickExecutionIssuesTab.tsx b/src/components/StockTakeManagement/PickExecutionIssuesTab.tsx index d98efba..c0b92cf 100644 --- a/src/components/StockTakeManagement/PickExecutionIssuesTab.tsx +++ b/src/components/StockTakeManagement/PickExecutionIssuesTab.tsx @@ -47,7 +47,7 @@ type SearchQuery = { type SearchParamNames = keyof SearchQuery; const PickExecutionIssuesTab: React.FC = () => { - const { t } = useTranslation(["inventory", "pickOrder"]); + const { t } = useTranslation(["stockTake", "pickOrder"]); // States const [issues, setIssues] = useState([]); @@ -81,14 +81,21 @@ const PickExecutionIssuesTab: React.FC = () => { { label: t("Type"), paramName: "type", - type: "select", - options: ["all", "jo", "do", "material"], + type: "select-labelled", + options: [ + { value: "jo", label: t("jo") }, + { value: "do", label: t("do") }, + { value: "material", label: t("material") }, + ], }, { label: t("Status"), paramName: "status", - type: "select", - options: ["all", "pending", "resolved"], + type: "select-labelled", + options: [ + { value: "pending", label: t("pending") }, + { value: "resolved", label: t("resolved") }, + ], }, ], [t], @@ -107,7 +114,7 @@ const PickExecutionIssuesTab: React.FC = () => { // Build API parameters with proper type casting let apiParams: FetchPickExecutionIssuesParams | undefined = undefined; - if (query.type && query.type !== "all") { + if (query.type && query.type !== "All") { // Type assertion to ensure the string is one of the valid types const validType = query.type as "jo" | "do" | "material"; apiParams = { type: validType }; @@ -143,7 +150,7 @@ const PickExecutionIssuesTab: React.FC = () => { ); } - if (query.status && query.status !== "all") { + if (query.status && query.status !== "All") { filteredResult = filteredResult.filter(issue => { if (query.status === "pending") { return issue.handleStatus.toLowerCase() === "pending"; diff --git a/src/components/StockTakeManagement/PickerCardList.tsx b/src/components/StockTakeManagement/PickerCardList.tsx index 6068e82..55b4933 100644 --- a/src/components/StockTakeManagement/PickerCardList.tsx +++ b/src/components/StockTakeManagement/PickerCardList.tsx @@ -126,7 +126,7 @@ const PickerCardList: React.FC = ({ onSearchFiltersChange, onAppliedFiltersChange, }) => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["stockTake", "common"]); const router = useRouter(); dayjs.extend(duration); diff --git a/src/components/StockTakeManagement/PickerReStockTake.tsx b/src/components/StockTakeManagement/PickerReStockTake.tsx index 199b593..1c8ebd3 100644 --- a/src/components/StockTakeManagement/PickerReStockTake.tsx +++ b/src/components/StockTakeManagement/PickerReStockTake.tsx @@ -51,7 +51,7 @@ const PickerReStockTake: React.FC = ({ onBack, onSnackbar, }) => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["stockTake", "common"]); const { data: session } = useSession() as { data: SessionWithTokens | null }; const [inventoryLotDetails, setInventoryLotDetails] = useState([]); diff --git a/src/components/StockTakeManagement/PickerStockTake.tsx b/src/components/StockTakeManagement/PickerStockTake.tsx index 76ca5d7..220c7b3 100644 --- a/src/components/StockTakeManagement/PickerStockTake.tsx +++ b/src/components/StockTakeManagement/PickerStockTake.tsx @@ -56,7 +56,7 @@ const PickerStockTake: React.FC = ({ onBack, onSnackbar, }) => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["stockTake", "common"]); const { data: session } = useSession() as { data: SessionWithTokens | null }; const [inventoryLotDetails, setInventoryLotDetails] = useState([]); diff --git a/src/components/StockTakeManagement/StockTakeManagement.tsx b/src/components/StockTakeManagement/StockTakeManagement.tsx index 9f9a543..c877ed2 100644 --- a/src/components/StockTakeManagement/StockTakeManagement.tsx +++ b/src/components/StockTakeManagement/StockTakeManagement.tsx @@ -30,7 +30,7 @@ function TabPanel(props: TabPanelProps) { } const StockTakeManagement: React.FC = () => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["stockTake", "common"]); const [currentTab, setCurrentTab] = useState(0); const handleTabChange = (event: React.SyntheticEvent, newValue: number) => { diff --git a/src/components/StockTakeManagement/StockTakeTab.tsx b/src/components/StockTakeManagement/StockTakeTab.tsx index c4ddfd7..ad4af35 100644 --- a/src/components/StockTakeManagement/StockTakeTab.tsx +++ b/src/components/StockTakeManagement/StockTakeTab.tsx @@ -20,7 +20,7 @@ const DEFAULT_PICKER_CARD_LIST_FILTERS: PickerCardListFilters = { }; const StockTakeTab: React.FC = () => { - const { t } = useTranslation(["inventory", "common"]); + const { t } = useTranslation(["stockTake", "common"]); const [tabValue, setTabValue] = useState(0); const [selectedSession, setSelectedSession] = useState(null); const [viewMode, setViewMode] = useState<"details" | "reStockTake">("details"); diff --git a/src/components/UpdateMaintenance/UpdateMaintenanceForm.tsx b/src/components/UpdateMaintenance/UpdateMaintenanceForm.tsx index 1d319f2..61a6256 100644 --- a/src/components/UpdateMaintenance/UpdateMaintenanceForm.tsx +++ b/src/components/UpdateMaintenance/UpdateMaintenanceForm.tsx @@ -34,7 +34,7 @@ type EquipmentDetailData = { }; const UpdateMaintenanceForm: React.FC = ({ id }) => { - const { t } = useTranslation("common"); + const { t } = useTranslation(["equipment", "common"]); const router = useRouter(); const [loading, setLoading] = useState(false); const [fetching, setFetching] = useState(true); diff --git a/src/components/qrCodeHandles/qrCodeHandleTabs.tsx b/src/components/qrCodeHandles/qrCodeHandleTabs.tsx index 08eb2a0..3d08d48 100644 --- a/src/components/qrCodeHandles/qrCodeHandleTabs.tsx +++ b/src/components/qrCodeHandles/qrCodeHandleTabs.tsx @@ -38,7 +38,7 @@ const QrCodeHandleTabs: React.FC = ({ equipmentTabContent, warehouseTabContent, }) => { - const { t } = useTranslation("common"); + const { t: tQrCodeHandle } = useTranslation("qrCodeHandle"); const { t: tUser } = useTranslation("user"); const { t: tWarehouse } = useTranslation("warehouse"); const searchParams = useSearchParams(); @@ -80,7 +80,7 @@ const QrCodeHandleTabs: React.FC = ({ - + diff --git a/src/i18n/I18nClientProvider.tsx b/src/i18n/I18nClientProvider.tsx index e28d972..6614e9f 100644 --- a/src/i18n/I18nClientProvider.tsx +++ b/src/i18n/I18nClientProvider.tsx @@ -25,7 +25,10 @@ const I18nProvider: React.FC = ({ resources: { [language]: resources, }, + lng: language, fallbackLng: language, + defaultNS: namespaces[0], + fallbackNS: namespaces, interpolation: { escapeValue: false, }, diff --git a/src/i18n/en/bagPrint.json b/src/i18n/en/bagPrint.json new file mode 100644 index 0000000..2baf2eb --- /dev/null +++ b/src/i18n/en/bagPrint.json @@ -0,0 +1,3 @@ +{ + "title": "Bag Printer" +} diff --git a/src/i18n/en/bagUsage.json b/src/i18n/en/bagUsage.json new file mode 100644 index 0000000..0d498ea --- /dev/null +++ b/src/i18n/en/bagUsage.json @@ -0,0 +1,12 @@ +{ + "Actions": "Actions", + "Back": "Back", + "Bag": "Bag", + "Bag Usage": "Bag Usage", + "Balance": "Balance", + "Consumed Qty": "Consumed Qty", + "Date": "Date", + "Job Order Code": "Job Order Code", + "Lot No": "Lot No", + "Scrap Qty": "Scrap Qty" +} diff --git a/src/i18n/en/bomWeighting.json b/src/i18n/en/bomWeighting.json new file mode 100644 index 0000000..1f8a21a --- /dev/null +++ b/src/i18n/en/bomWeighting.json @@ -0,0 +1,12 @@ +{ + "BOM Weighting Score List": "BOM Weighting Score List", + "Material Weighting": "Material Weighting", + "Material Score": "Material Score", + "Weighting": "Weighting", + "Base Score": "Base Score", + "Edit BOM Weighting Score": "Edit BOM Weighting Score", + "Scoring Item": "Scoring Item", + "Range": "Range", + "Item Code": "Item Code", + "Item Name": "Item Name" +} diff --git a/src/i18n/en/chart.json b/src/i18n/en/chart.json new file mode 100644 index 0000000..0eb11e4 --- /dev/null +++ b/src/i18n/en/chart.json @@ -0,0 +1,113 @@ +{ + "all": "All", + "autoRefresh": "Auto Refresh", + "board_equipmentUsage": "Equipment Usage Board", + "board_jobOrderChart": "Job Order Chart", + "board_jobOrderLive": "Job Order Live Board", + "board_processLive": "Process Live Board", + "dateRange_lastDays": "Last {{d}} days", + "delivery_colAvgMin": "Avg Min/Order", + "delivery_colPickCount": "Pick Count", + "delivery_colStaff": "Staff", + "delivery_colTotalMin": "Total Min", + "delivery_dailyByStaff": "Daily by Staff", + "delivery_endDate": "End Date", + "delivery_item": "Item", + "delivery_itemPlaceholder": "Leave empty for all", + "delivery_noDataDesc": "No delivery data available for the selected period.", + "delivery_orderCount": "Order Count", + "delivery_ordersByDate": "Delivery Orders by Date", + "delivery_ordersByDate_export": "Delivery_Orders_By_Date", + "delivery_staff": "Staff", + "delivery_staffPerfCaption": "Per-person pick count & total duration for period", + "delivery_staffPerfDateError": "Staff performance start date cannot be later than end date", + "delivery_staffPerformanceTitle": "Staff Delivery Performance (Daily Pick Count & Duration)", + "delivery_staffPlaceholder": "Leave empty for all", + "delivery_startDate": "Start Date", + "delivery_store": "Store", + "delivery_topItemsByCount": "Top Items by Delivery Count", + "equipment_boardTitle": "Equipment Usage Board", + "equipment_completed": "Completed", + "equipment_endTime": "End Time", + "equipment_equipment": "Equipment", + "equipment_infoDescription1": "Shows equipment status in real-time: working, idle, or under maintenance.", + "equipment_infoDescription2": "Each equipment card displays the current job order and process.", + "equipment_infoDescription3": "Equipment with unclosed hours or missing time entries will be flagged.", + "equipment_jobOrder": "Job Order", + "equipment_missingHours": "Missing equipment hours", + "equipment_notToday": "Not Today", + "equipment_operator": "Operator", + "equipment_planStart": "Plan Start", + "equipment_process": "Process", + "equipment_searchAndList": "Search & List", + "equipment_startTime": "Start Time", + "equipment_status": "Status", + "equipment_unclosedHours": "Equipment hours not closed", + "exportExcel": "Export Excel", + "forecast_itemCode": "Item Code", + "forecast_noScheduleData": "No scheduling data for this date range.", + "forecast_plannedOutputByDate": "Planned Daily Output by Item (Forecast)", + "forecast_plannedOutputByDate_export": "Planned_Daily_Output_By_Item", + "forecast_productionSchedule": "Production Schedule by Date (Estimated Output)", + "forecast_productionSchedule_export": "Production_Schedule_By_Date", + "intervalSeconds": "Interval (sec)", + "jo_byStatus": "Job Orders by Status", + "jo_byStatus_export": "Job_Orders_By_Status", + "jo_createdVsCompleted": "Job Orders Created vs Completed by Date", + "jo_createdVsCompleted_export": "Job_Orders_Created_vs_Completed", + "jo_datePlanStart": "Date (Plan Start)", + "jo_detailSection": "Job Order Material / Process / Equipment", + "jo_equipmentWorkingWorked": "Equipment In Use / Used (by Job Order)", + "jo_equipmentWorkingWorked_export": "Equipment_In_Use_vs_Used", + "jo_materialPendingPicked": "Material Pending / Picked (by Plan Date)", + "jo_materialPendingPicked_export": "Material_Pending_vs_Picked", + "jo_processPendingCompleted": "Process Pending / Completed (by Plan Date)", + "jo_processPendingCompleted_export": "Process_Pending_vs_Completed", + "laneX": "Lane X", + "minuteAbbr": "min", + "minutes": "minutes", + "minutesWithVal": "{{val}} min", + "noData": "No data", + "off": "Off", + "on": "On", + "otherBoards": "Other Boards", + "pageTitle_delivery": "Delivery & Dispatch", + "pageTitle_equipmentBoard": "Equipment Usage Board", + "pageTitle_forecast": "Forecast & Planning", + "pageTitle_jobOrder": "Job Orders", + "pageTitle_jobOrderBoard": "Job Order Real-time Board", + "pageTitle_processBoard": "Process Real-time Board", + "pageTitle_purchase": "Purchase", + "pageTitle_warehouse": "Inventory & Warehouse", + "process_completed": "Completed", + "process_inProgress": "In Progress", + "process_nonToday": "Not Today", + "process_notStarted": "Not Started", + "refresh": "Refresh", + "requestFailed": "Request failed", + "selectDate": "Select Date", + "series_balance": "Balance", + "series_completed": "Completed", + "series_consumption": "Consumption", + "series_created": "Created", + "series_inbound": "Inbound", + "series_month": "Month", + "series_outbound": "Outbound", + "series_total": "Total", + "show": "Show", + "today": "Today", + "warehouse_addItem": "Add item to split", + "warehouse_balanceTrend": "Stock Balance Trend", + "warehouse_balanceTrend_export": "Stock_Balance_Trend", + "warehouse_consumptionTrend": "Monthly Consumption Trend (Outbound)", + "warehouse_consumptionTrend_export": "Monthly_Consumption_Trend", + "warehouse_exportFail": "Master export failed", + "warehouse_optional": "Optional", + "warehouse_qty": "Qty", + "warehouse_stockInOutByDate": "Stock In vs Out by Date", + "warehouse_stockInOutByDate_export": "Stock_In_vs_Out", + "warehouse_stockTxnByDate": "Stock Transactions by Date (In / Out / Total)", + "warehouse_stockTxnByDate_export": "Stock_Transactions_By_Date", + "warehouse_sumAll": "Sum all if empty", + "yesterday": "Yesterday" +} diff --git a/src/i18n/en/clientMonitor.json b/src/i18n/en/clientMonitor.json new file mode 100644 index 0000000..4292a20 --- /dev/null +++ b/src/i18n/en/clientMonitor.json @@ -0,0 +1,3 @@ +{ + "title": "Device Connection Monitor" +} diff --git a/src/i18n/en/common.json b/src/i18n/en/common.json index 5e04af1..3c050f7 100644 --- a/src/i18n/en/common.json +++ b/src/i18n/en/common.json @@ -1,74 +1,125 @@ { - "Grade {{grade}}": "Grade {{grade}}", - "General Data": "General Data", - "Repair and Maintenance": "Repair and Maintenance", - "Repair and Maintenance Status": "Repair and Maintenance Status", - "Latest Repair and Maintenance Date": "Latest Repair and Maintenance Date", - "Last Repair and Maintenance Date": "Last Repair and Maintenance Date", - "Repair and Maintenance Remarks": "Repair and Maintenance Remarks", - "Update Equipment Maintenance and Repair": "Update Equipment Maintenance and Repair", - "Equipment Information": "Equipment Information", - "Loading": "Loading...", - "Equipment not found": "Equipment not found", - "Error saving data": "Error saving data", - "Cancel": "Cancel", - "Do you want to delete?": "Do you want to delete?", - "Save": "Save", - "Yes": "Yes", - "No": "No", - "Equipment Name": "Equipment Name", - "Equipment Code": "Equipment Code", - "ShopAndTruck": "Shop & route management", - "DO floor (supplier)": "DO floor (supplier)", - "Route Board": "Route board", - "TruckLance Code is required": "TruckLance Code is required", - "Truck shop details updated successfully": "Truck shop details updated successfully", - "Failed to save truck shop details": "Failed to save truck shop details", - "Truck lane information is required": "Truck lane information is required", - "Shop added to truck lane successfully": "Shop added to truck lane successfully", - "Failed to create shop in truck lane": "Failed to create shop in truck lane", - "Add Shop": "Add Shop", - "Shop Name": "Shop Name", - "Shop Branch": "Shop Branch", - "Shop Code": "Shop Code", - "Search or select shop name": "Search or select shop name", - "Search or select shop code": "Search or select shop code", - "Search or select remark": "Search or select remark", - "Edit shop details": "Edit shop details", - "Add Shop to Truck Lane": "Add Shop to Truck Lane", - "Truck lane code already exists. Please use a different code.": "Truck lane code already exists. Please use a different code.", - "No Purchase Order After 2026-01-01": "No Purchase Order After 2026-01-01", - "No Import Record": "No Import Record", - "Download Template": "Download Template", - "Upload": "Upload", - "Downloading...": "Downloading...", - "Uploading...": "Uploading...", - "Upload successful": "Upload successful", - "Upload failed": "Upload failed", - "Download failed": "Download failed", - "Upload completed with count": "{{count}} item(s) updated.", - "Upload row errors": "The following rows had issues:", - "item(s) updated": "item(s) updated.", - "Average unit price": "Average unit price", - "Latest market unit price": "Latest market unit price", - "Invalid departure time": "Invalid departure time", - "No trucks found for this truck lane": "No trucks found for this truck lane", - "Departure time updated for all shops on this truck lane": "Departure time updated for all shops on this truck lane", - "Applies to all shops using this truck lane": "Applies to all shops using this truck lane", - "Please fill in the following required fields:": "Please fill in the following required fields:", - "Departure Time": "Departure Time", - "Submitting...": "Submitting...", - "Failed to save truck data": "Failed to save truck data", - "Edit departure time": "Edit departure time", - "Cancel editing": "Cancel editing", - "Select a shop first": "Select a shop first", - "Search or select branch": "Search or select branch", - "Mass Edit": "Mass Edit", - "Save All": "Save All", - "All shops updated successfully": "All shops updated successfully", - "PO Workbench": "PO Workbench", - "masterDataIssue_nav": "Master Data Issues", - "masterDataIssue_pageTitle": "Master Data Issues", - "masterDataIssue_viewDetail": "View detail", - "masterDataIssue_group_count": "{{groups}} rows · {{issues}} issues" -} \ No newline at end of file + "Actions": "操作", + "Add Document": "新增文件", + "All": "全部", + "Allergic Substances": "過敏原", + "An error has occurred. Please try again later.": "An error has occurred. Please try again later.", + "Are you sure you want to delete this item?": "您確定要刪除此項目嗎?", + "Back": "返回", + "Basic Info": "基本資訊", + "Bom Required Qty": "BOM Required Qty", + "Bom UOM": "BOM UOM", + "Brand": "品牌", + "CMB": "消耗品", + "CO": "消耗品", + "Cancel": "取消", + "Column Name": "欄位名稱", + "Coming soon": "即將推出", + "Complexity": "複雜度", + "Confirm": "確認", + "Confirm Delete": "確認刪除", + "Cost (HKD)": "費用 (HKD)", + "Current total": "目前總和", + "Day Before Yesterday": "前天", + "Delete": "刪除", + "Delete Failed": "刪除失敗", + "Density": "濃淡", + "Depth": "顔色深淺度 深1淺5", + "Description": "描述", + "Details": "詳情", + "Duration (Minutes)": "時間(分)", + "Edit": "編輯", + "Enter any additional observations or notes...": "輸入其他觀察或備註...", + "Enter or select remark": "輸入或選擇備註", + "Error saving data": "Error saving data", + "Failed to fetch data": "無法取得資料", + "Filter": "過濾", + "Finished Good Detail": "成品出倉詳情", + "Finished Good Management": "成品出倉管理", + "Finished Good Order": "成品出倉", + "Float": "浮沉", + "General Data": "基本資料", + "Grade {{grade}}": "等級 {{grade}}", + "Invoice": "發票", + "Invoice Date": "發票日期", + "Item Code": "Item Code", + "Item Name": "Item Name", + "Loading": "載入中...", + "Loading order summary": "正在載入訂單摘要", + "Location": "位置", + "MA": "材料", + "MAT": "材料", + "MI": "雜項", + "Material Name": "材料清單", + "Min": "最小值", + "NM": "雜項及非消耗品", + "No": "否", + "No Lot": "沒有批號", + "No data available": "沒有資料", + "No options": "沒有選項", + "Order": "順序", + "Pending": "Pending", + "Please Select BOM": "請選擇 BOM", + "Please try again later.": "請稍後重試。", + "Project Code": "專案代碼", + "Project Code and Name": "專案代碼與名稱", + "QC Template not found": "找不到 QC 範本", + "Qty": "數量", + "RM": "原料", + "Range": "範圍", + "Refresh": "重新載入", + "Remarks": "備註", + "Remove Document": "移除文件", + "Report": "報告", + "Reset": "重置", + "Row per page": "每頁行數", + "Rows per page": "每頁行數", + "Sales Qty": "銷售數量", + "Sales UOM": "銷售單位", + "Save": "儲存", + "Saving": "儲存中", + "Search": "搜索", + "Search Criteria": "搜索條件", + "Select Date": "選擇日期", + "Session expired or unauthorized.": "工作階段已過期或未經授權。", + "Sign out": "Sign out", + "Status": "狀態", + "Stock Qty": "庫存數量", + "Supporting Document": "證明文件", + "Task": "任務", + "Time Sequence": "時段", + "Today": "今天", + "Total weighting must equal 1": "權重總和必須等於 1", + "Unauthorized: Please log in again": "未經授權:請重新登入", + "Uom": "單位", + "Update Failed": "更新失敗", + "Update Success": "更新成功", + "Weighting must be a number": "權重必須為數字", + "Yes": "是", + "Yesterday": "昨天", + "all": "全部", + "bomWeighting": "BOM Weighting Score", + "cmb": "消耗品", + "collapsible table": "可折疊表格", + "consumable": "消耗品", + "consumables": "消耗品", + "create": "新增", + "edit": "編輯", + "expand row": "展開行", + "group mode": "群組模式", + "item": "貨品", + "items": "物品", + "mat": "原料", + "menu": "選單", + "nm": "雜項及非消耗品", + "non-consumables": "非消耗品", + "other": "其他", + "profile": "個人資料", + "revert": "還原", + "settings": "設定", + "stockRecord": "盤點記錄", + "stocktakemanagement": "盤點管理", + "testing sections tabs": "測試區域分頁", + "warehouse": "倉庫", + "材料": "材料" +} diff --git a/src/i18n/en/dashboard.json b/src/i18n/en/dashboard.json index c45c20f..fc5c351 100644 --- a/src/i18n/en/dashboard.json +++ b/src/i18n/en/dashboard.json @@ -1,129 +1,137 @@ { - "Dashboard": "Dashboard", - "Order status": "Order status", - "pending": "pending", - "receiving": "receiving", - "total": "total", - "Warehouse temperature record": "Warehouse temperature record", - "Warehouse type": "Warehouse type", - "Last 6 hours": "Last 6 hours", "Add some entries!": "Add some entries!", - "Last 24 hours": "Last 24 hours", + "All": "All", + "All Stores": "All Stores", + "Allow to select Date to view history.": "Allow to select Date to view history.", + "Application completion": "Application completion", + "Auto-refresh every 1 minute": "Auto-refresh every 1 minute", + "Auto-refresh every 10 minutes": "Auto-refresh every 10 minutes", + "Auto-refresh every 15 minutes": "Auto-refresh every 15 minutes", + "Auto-refresh every 5 minutes": "Auto-refresh every 5 minutes", + "Based on Expected Delivery Date": "Based on Expected Delivery Date", "Cold storage": "Cold storage", - "Normal temperature storage": "Normal temperature storage", - "Temperature status": "Temperature status", + "Column 1": "Column 1", + "Column 2": "Column 2", + "Column 3": "Column 3", + "Completed QC Total": "Completed QC Total", + "Consumable": "Consumable", + "Count any item with IQC defect in any IQC criteria": "Count any item with IQC defect in any IQC criteria", + "DN Date": "DN Date", + "Dashboard": "Dashboard", + "dashboard": "Dashboard", + "dashboard tabs": "Dashboard tabs", + "Date": "Date", + "Day After Tomorrow": "Day After Tomorrow", + "Escalation Level": "Escalation Level", + "Escalation List": "Escalation List", + "Exit Screen": "Exit Screen", + "Expected No. of Delivery": "Expected No. of Delivery", + "Extracted order": "Extracted order", + "Filter": "Filter", + "First Ticket Start": "First Ticket Start", + "Goods Receipt Status": "Goods Receipt Status", + "Goods Receipt Status New": "Goods Receipt Status", + "Humidity": "Humidity", "Humidity status": "Humidity status", - "Warehouse status": "Warehouse status", - "Progress chart": "Progress chart", - "Purchase Order Code": "Purchase Order Code", "Item Name": "Item Name", - "Escalation Level": "Escalation Level", - "Reason": "Reason", - "escalated date": "escalated date", + "Last 24 hours": "Last 24 hours", + "Last 6 hours": "Last 6 hours", + "Last Ticket End": "Last Ticket End", + "Last updated": "Last updated", + "My Escalation List": "My Escalation List", + "No": "No", + "No data available": "No data available", + "No truck schedules available": "No truck schedules available", + "Overview": "Overview", + "No truck schedules available for today": "No truck schedules available for today", + "No. of Items Completed Put Away at Store": "No. of Items Completed Put Away at Store", + "No. of Items Inspected": "No. of Items Inspected", + "No. of Items with IQC Issue": "No. of Items with IQC Issue", + "No. of Orders Received at Dock": "No. of Orders Received at Dock", + "No. of Shops": "No. of Shops", + "Normal temperature storage": "Normal temperature storage", + "Now": "Now", "Order completion": "Order completion", - "Store Management": "Store Management", - "Consumable": "Consumable", - "Shipment": "Shipment", - "Extracted order": "Extracted order", + "Order status": "Order status", + "Pending application": "Pending application", "Pending order": "Pending order", - "Temperature": "Temperature", - "Humidity": "Humidity", "Pending storage": "Pending storage", - "Total storage": "Total storage", - "Application completion": "Application completion", - "Processed application": "Processed application", - "Pending application": "Pending application", - "pending inspection material": "pending inspection material", - "rejected": "rejected", - "accepted": "accepted", - "escalated": "escalated", - "inspected material": "inspected material", - "total material": "total material", - "stock in escalation list": "stock in escalation list", - "Responsible for handling colleagues": "Responsible for handling colleagues", - "Completed QC Total": "Completed QC Total", - "QC Fail Count": "QC Fail Count", - "DN Date": "DN Date", - "Received Qty": "Received Qty", + "Pick Time (min)": "Pick Time (min)", "Po Code": "Po Code", - "My Escalation List": "My Escalation List", - "Escalation List": "Escalation List", + "Po Code/Jo Code": "Po Code/Jo Code", + "Processed application": "Processed application", + "Progress chart": "Progress chart", + "Purchase Order Code": "Purchase Order Code", "Purchase UoM": "Purchase UoM", "QC Completed Count": "QC Completed Count", + "QC Fail Count": "QC Fail Count", "QC Fail-Total Count": "QC Fail-Total Count", - "escalationStatus": "escalationStatus", - "escalated datetime": "escalated datetime", - "escalateFrom": "escalateFrom", - "No": "No", + "Reason": "Reason", + "Received Qty": "Received Qty", "Responsible Escalation List": "Responsible Escalation List", - "show completed logs": "show completed logs", + "Responsible for handling colleagues": "Responsible for handling colleagues", + "Restore Screen": "Restore Screen", "Rows per page": "Rows per page", - "Truck Schedule Dashboard": "Truck Schedule Dashboard", + "Screen cleared": "Screen cleared", + "Select Date": "Select Date", + "Shipment": "Shipment", + "Show Purchase Order Codes": "Show Purchase Order Codes", + "Show Supplier Code": "Show Supplier Code", + "Show Supplier Name": "Show Supplier Name", + "Statistics": "Statistics", + "Status": "Status", "Store ID": "Store ID", - "All Stores": "All Stores", - "Auto-refresh every 5 minutes": "Auto-refresh every 5 minutes", - "Last updated": "Last updated", - "Truck Schedule": "Truck Schedule", - "Time Remaining": "Time Remaining", - "No. of Shops": "No. of Shops", - "Total Items": "Total Items", - "Tickets Released": "Tickets Released", - "First Ticket Start": "First Ticket Start", + "Store Management": "Store Management", + "Supplier": "Supplier", + "Supplier Code": "Supplier Code", + "Supplier Name": "Supplier Name", + "Temperature": "Temperature", + "Temperature status": "Temperature status", "Tickets Completed": "Tickets Completed", - "Last Ticket End": "Last Ticket End", - "Pick Time (min)": "Pick Time (min)", - "No truck schedules available for today": "No truck schedules available for today", - "No truck schedules available": "No truck schedules available", - "Select Date": "Select Date", + "Tickets Released": "Tickets Released", + "Time": "Time", + "Time Remaining": "Time Remaining", + "Two column navigable table": "Two column navigable table", "Today": "Today", "Tomorrow": "Tomorrow", - "Day After Tomorrow": "Day After Tomorrow", - "Goods Receipt Status": "Goods Receipt Status", - "Date": "Date", - "Time": "Time", - "Allow to select Date to view history.": "Allow to select Date to view history.", - "Auto-refresh every 15 minutes": "Auto-refresh every 15 minutes", - "Exit Screen": "Exit Screen", - "Restore Screen": "Restore Screen", - "Screen cleared": "Screen cleared", - "Supplier": "Supplier", - "Expected No. of Delivery": "Expected No. of Delivery", - "No. of Orders Received at Dock": "No. of Orders Received at Dock", - "No. of Items Inspected": "No. of Items Inspected", - "No. of Items with IQC Issue": "No. of Items with IQC Issue", - "No. of Items Completed Put Away at Store": "No. of Items Completed Put Away at Store", - "Show Supplier Name": "Show Supplier Name", - "Based on Expected Delivery Date": "Based on Expected Delivery Date", - "Upon entry of DN and Lot No. for all items of the order": "Upon entry of DN and Lot No. for all items of the order", + "Total Items": "Total Items", + "Total storage": "Total storage", + "Truck Schedule": "Truck Schedule", + "Truck Schedule Dashboard": "Truck Schedule Dashboard", "Upon any IQC decision received": "Upon any IQC decision received", - "Count any item with IQC defect in any IQC criteria": "Count any item with IQC defect in any IQC criteria", "Upon completion of put away for an material in order. Count no. of items being put away": "Upon completion of put away for an material in order. Count no. of items being put away", - "Filter": "Filter", - "All": "All", - "Column 1": "Column 1", - "Column 2": "Column 2", - "Column 3": "Column 3", - "No data available": "No data available", - "Supplier Code": "Supplier Code", - "Supplier Name": "Supplier Name", - "Purchase Order Code": "Purchase Order Code", - "Statistics": "Statistics", - "Show Supplier Code": "Show Supplier Code", - "Show Purchase Order Codes": "Show Purchase Order Codes", - "x/y orders received": "x/y orders received", - "Goods Receipt Status New": "Goods Receipt Status", + "Upon entry of DN and Lot No. for all items of the order": "Upon entry of DN and Lot No. for all items of the order", "Usage statistics": "Usage statistics", "Usage stats description": "Report management and truck routing summary: page views, downloads, and direct prints.", - "Usage stats report section": "Report management (報告管理)", - "Usage stats truck routing section": "Truck routing summary (送貨路線摘要)", - "Usage stats user": "User", - "Usage stats page views": "Page views", "Usage stats downloads": "Downloads", + "Usage stats end date": "End date", + "Usage stats invalid date range": "Start date must be earlier than or equal to end date.", + "Usage stats load error": "Could not load usage statistics.", + "Usage stats page views": "Page views", "Usage stats prints": "Direct prints", "Usage stats refresh": "Refresh", - "Usage stats load error": "Could not load usage statistics.", - "Usage stats start date": "Start date", - "Usage stats end date": "End date", + "Usage stats report section": "Usage stats report section", "Usage stats search": "Search", - "Usage stats invalid date range": "Start date must be earlier than or equal to end date." + "Usage stats start date": "Start date", + "Usage stats truck routing section": "Usage stats truck routing section", + "Usage stats user": "User", + "Warehouse status": "Warehouse status", + "Warehouse temperature record": "Warehouse temperature record", + "Warehouse type": "Warehouse type", + "accepted": "accepted", + "escalateFrom": "escalateFrom", + "escalated": "escalated", + "escalated date": "escalated date", + "escalated datetime": "escalated datetime", + "escalationStatus": "escalationStatus", + "inspected material": "inspected material", + "pending": "pending", + "pending inspection material": "pending inspection material", + "receiving": "receiving", + "rejected": "rejected", + "show completed logs": "show completed logs", + "stock in escalation list": "stock in escalation list", + "total": "total", + "total material": "total material", + "x/y orders received": "x/y orders received" } diff --git a/src/i18n/en/deliveryOrderFloor.json b/src/i18n/en/deliveryOrderFloor.json index addd841..3fd6f02 100644 --- a/src/i18n/en/deliveryOrderFloor.json +++ b/src/i18n/en/deliveryOrderFloor.json @@ -1,6 +1,6 @@ { "title": "Delivery order / workbench floor (supplier codes)", - "Intro": "Comma-separated supplier codes in system settings. Use edit to change; DO behavior depends on backend reading these keys.", + "Intro": "Manage supplier codes assigned to each floor. Use the edit button to add or remove suppliers from a floor.", "2F supplier": "2F supplier", "4F supplier": "4F supplier", "Edit 2F": "Edit 2F", @@ -29,5 +29,6 @@ "Comma separated hint": "Separate codes with commas, no spaces", "Save": "Save", "Saved": "Saved", - "Cancel": "Cancel" + "Cancel": "Cancel", + "DO floor (supplier)": "DO floor (supplier)" } diff --git a/src/i18n/en/demandForecast.json b/src/i18n/en/demandForecast.json new file mode 100644 index 0000000..988fe7b --- /dev/null +++ b/src/i18n/en/demandForecast.json @@ -0,0 +1,12 @@ +{ + "Demand Forecast Setting": "Demand Forecast Setting", + "Exclude Date": "Exclude Date", + "Finished Goods Name": "Finished Goods Name", + "Mon": "Mon", + "Tue": "Tue", + "Wed": "Wed", + "Thu": "Thu", + "Fri": "Fri", + "Sat": "Sat", + "Sun": "Sun" +} diff --git a/src/i18n/en/detailScheduling.json b/src/i18n/en/detailScheduling.json new file mode 100644 index 0000000..6abb2ab --- /dev/null +++ b/src/i18n/en/detailScheduling.json @@ -0,0 +1,21 @@ +{ + "Client Name": "Client Name", + "Demand Forecast Period": "Demand Forecast Period", + "Detail Scheduling": "Detail Scheduling", + "Details": "Details", + "Product": "Product", + "Product Count": "Product Count", + "Product Count(s)": "Product Count(s)", + "Project Category": "Project Category", + "Project Code": "Project Code", + "Project Name": "Project Name", + "Project Status": "Project Status", + "Project Type": "Project Type", + "Reset": "Reset", + "Schedule Period": "Schedule Period", + "Scheduled At": "Scheduled At", + "Search": "Search", + "Search Criteria": "Search Criteria", + "Search by Project Code or Project Name or Client Name or Project Category or Project Type or Project Status or Project Start Date or Project End Date": "Search by Project Code or Project Name or Client Name or Project Category or Project Type or Project Status or Project Start Date or Project End Date", + "types": "types" +} diff --git a/src/i18n/en/do.json b/src/i18n/en/do.json new file mode 100644 index 0000000..12467ab --- /dev/null +++ b/src/i18n/en/do.json @@ -0,0 +1,91 @@ +{ + "Action": "Action", + "An error has occurred. Please try again later.": "An error has occurred. Please try again later.", + "Assign 2/F": "Assign 2/F", + "Assign 4/F": "Assign 4/F", + "Available Orders": "Available Orders", + "Available Trucks": "Available Trucks", + "Back": "Back", + "Batch Release": "Batch Release", + "Batch release completed successfully.": "Batch release completed successfully.", + "Cancel": "Cancel", + "Code": "Code", + "Completed": "Completed", + "Confirm": "Confirm", + "Confirm Assignment": "Confirm Assignment", + "Create": "Create", + "DO Workbench": "DO Workbench", + "DO released successfully! Pick orders created.": "DO released successfully! Pick orders created.", + "Delete": "Delete", + "Delivery Order": "Delivery Order", + "Delivery Order Code": "Delivery Order Code", + "Delivery Order Date": "Delivery Order Date", + "Delivery Order No.": "Delivery Order No.", + "Delivery Order Status": "Delivery Order Status", + "Details": "Details", + "Do Workbench": "Do Workbench", + "Edit": "Edit", + "Edit Delivery Order Detail": "Edit Delivery Order Detail", + "Estimated Arrival": "Estimated Arrival", + "Estimated Arrival Date": "Estimated Arrival Date", + "Estimated Arrival From": "Estimated Arrival From", + "Estimated Arrival To": "Estimated Arrival To", + "Etra": "Etra", + "Expiry Date": "Expiry Date", + "Failed to assign pick orders. Please try again later.": "Failed to assign pick orders. Please try again later.", + "Failed to release pick orders. Please try again later.": "Failed to release pick orders. Please try again later.", + "Fetching all matching records...": "Fetching all matching records...", + "Floor": "Floor", + "Item Name": "Item Name", + "Item No.": "Item No.", + "Just Complete": "Just Complete", + "Just Completed": "Just Completed", + "Lane Code": "Lane Code", + "Loading...": "Loading...", + "Location": "Location", + "Lot No.": "Lot No.", + "No trucks available": "No trucks available", + "Order Date": "Order Date", + "Order Date From": "Order Date From", + "Order Date To": "Order Date To", + "Pending": "Pending", + "Pick Order Assignment": "Pick Order Assignment", + "Please wait": "Please wait", + "Price": "Price", + "Problem DO(s): ": "Problem DO(s): ", + "Progress": "Progress", + "Quantity": "Quantity", + "Receiving": "Receiving", + "Release": "Release", + "Release 2/F": "Release 2/F", + "Release 4/F": "Release 4/F", + "Releasing": "Releasing", + "Remark": "Remark", + "Required Date": "Required Date", + "Select Remark": "Select Remark", + "Selected Item(s): ": "Selected Item(s): ", + "Selected Shop(s): ": "Selected Shop(s): ", + "Shop Code": "Shop Code", + "Shop Name": "Shop Name", + "Status": "Status", + "Stock Available": "Stock Available", + "Stock Status": "Stock Status", + "Delivery": "Delivery", + "Store": "Store", + "Submit Miss Item": "Submit Miss Item", + "Submit Qty": "Submit Qty", + "Submit Quantity": "Submit Quantity", + "Supplier Code": "Supplier Code", + "Supplier Name": "Supplier Name", + "Truck Availability Warning": "Truck Availability Warning", + "Truck Lance Code": "Truck Lance Code", + "Truck X": "Truck X", + "Truck lane search requires date message": "Truck lane search requires date message", + "Truck lane search requires date title": "Truck lane search requires date title", + "Warning: Some delivery orders do not have matching trucks for the target date.": "Warning: Some delivery orders do not have matching trucks for the target date.", + "Workbench Batch Release": "Workbench Batch Release", + "code": "code", + "do workbench": "do workbench", + "row selected": "row selected", + "uom": "uom" +} diff --git a/src/i18n/en/doWorkbench.json b/src/i18n/en/doWorkbench.json new file mode 100644 index 0000000..321a91a --- /dev/null +++ b/src/i18n/en/doWorkbench.json @@ -0,0 +1,49 @@ +{ + "Actions": "Actions", + "All Pick Order Lots": "All Pick Order Lots", + "Auto-refresh every 5 minutes": "Auto-refresh every 5 minutes", + "Back to List": "Back to List", + "Cancel": "Cancel", + "Completed": "Completed", + "Confirm": "Confirm", + "DO Workbench": "DO Workbench", + "Delivery Order": "Delivery Order", + "Departure Time": "Departure Time", + "Edit": "Edit", + "Edit Delivery Order Detail": "Edit Delivery Order Detail", + "Floor": "Floor", + "Handler": "Handler", + "Issue": "Issue", + "Item Code": "Item Code", + "Item Name": "Item Name", + "Last updated": "Last updated", + "Loading Sequence": "Loading Sequence", + "Location": "Location", + "Lot No": "Lot No", + "Lot Required Pick Qty": "Lot Required Pick Qty", + "No data available": "No data available", + "Now": "Now", + "Pick Order": "Pick Order", + "Pick Order Code": "Pick Order Code", + "Required Qty": "Required Qty", + "Rows per page": "Rows per page", + "Scan Result": "Scan Result", + "Search date": "Search date", + "Select Date": "Select Date", + "Shop": "Shop", + "Shop Name": "Shop Name", + "Start QR Scan": "Start QR Scan", + "Status": "Status", + "Stop QR Scan": "Stop QR Scan", + "Store ID": "Store ID", + "Submitting...": "Submitting...", + "Target Date": "Target Date", + "This lot is rejected, please scan another lot.": "This lot is rejected, please scan another lot.", + "Today": "Today", + "Truck Information": "Truck Information", + "View Details": "View Details", + "DO Workbench Search": "DO Workbench Search", + "completed": "completed", + "items": "items", + "pending": "pending" +} diff --git a/src/i18n/en/equipment.json b/src/i18n/en/equipment.json new file mode 100644 index 0000000..fee9996 --- /dev/null +++ b/src/i18n/en/equipment.json @@ -0,0 +1,21 @@ +{ + "Create Material": "Create Material", + "Equipment": "Equipment", + "Equipment Type": "Equipment Type", + "Update Equipment Maintenance and Repair": "Update Equipment Maintenance and Repair", + "Create Equipment": "Create Equipment", + "Edit Equipment": "Edit Equipment", + "Create Equipment Type": "Create Equipment Type", + "Edit Equipment Type": "Edit Equipment Type", + "Equipment Information": "Equipment Information", + "Equipment Name": "Equipment Name", + "Equipment Code": "Equipment Code", + "Equipment Details": "Equipment Details", + "Equipment Type Details": "Equipment Type Details", + "Equipment not found": "Equipment not found", + "Last Repair and Maintenance Date": "Last Repair and Maintenance Date", + "Latest Repair and Maintenance Date": "Latest Repair and Maintenance Date", + "Repair and Maintenance": "Repair and Maintenance", + "Repair and Maintenance Remarks": "Repair and Maintenance Remarks", + "Repair and Maintenance Status": "Repair and Maintenance Status" +} diff --git a/src/i18n/en/finishedGood.json b/src/i18n/en/finishedGood.json new file mode 100644 index 0000000..6792d27 --- /dev/null +++ b/src/i18n/en/finishedGood.json @@ -0,0 +1,4 @@ +{ + "Finished Good Order": "Finished Good Order", + "Finished Good Detail": "Finished Good Detail" +} diff --git a/src/i18n/en/finishedgoodmanagement.json b/src/i18n/en/finishedgoodmanagement.json new file mode 100644 index 0000000..9d77f11 --- /dev/null +++ b/src/i18n/en/finishedgoodmanagement.json @@ -0,0 +1,38 @@ +{ + "Finished Good Management": "Finished Good Management", + "提料順序": "Pick Order Sequence", + "Item Code": "Item Code", + "Item Name": "Item Name", + "Search & Jump": "Search & Jump", + "Jump": "Jump", + "Move Up": "Move Up", + "Move Down": "Move Down", + "Move Top": "Move to Top", + "Move Bottom": "Move to Bottom", + "Add Item": "Add Item", + "Saving": "Saving", + "Save": "Save", + "Refresh": "Reload", + "Unsaved changes": "Unsaved changes", + "Order": "Order", + "Code": "Code", + "Name": "Name", + "Shipping Warehouse": "Shipping Warehouse", + "Receiving Warehouse": "Receiving Warehouse", + "Actions": "Actions", + "Loading": "Loading...", + "No data available": "No data available", + "Insert": "Insert", + "Search": "Search", + "Insert at": "Insert at", + "Insert position must be >= 1": "Insert position must be >= 1", + "Order number": "Order number", + "In front of": "In front of", + "Behind": "Behind", + "Only show FG items without order": "Enter a keyword to search (only items without sequence)", + "Cancel": "Cancel", + "Confirm": "Confirm", + "Move to order": "Move to order", + "Target order": "Target order", + "This will move the item to exact order": "This will move the item to the specified order and reorder the rest" +} diff --git a/src/i18n/en/home.json b/src/i18n/en/home.json new file mode 100644 index 0000000..5f5738d --- /dev/null +++ b/src/i18n/en/home.json @@ -0,0 +1,4 @@ +{ + "Add some entries!": "Add some entries!", + "Demand Qty": "Demand Qty" +} diff --git a/src/i18n/en/importBom.json b/src/i18n/en/importBom.json new file mode 100644 index 0000000..a570fc4 --- /dev/null +++ b/src/i18n/en/importBom.json @@ -0,0 +1,12 @@ +{ + "Create Material": "Create Material", + "Update Equipment Maintenance and Repair": "Update Equipment Maintenance and Repair", + "Correct BOM List (Can Import)": "Correct BOM List (Can Import)", + "Issue BOM List": "Issue BOM List", + "Loading BOM Detail...": "Loading BOM Detail...", + "Bom Material": "Bom Material", + "Is Drink": "Is Drink", + "Drink": "Drink", + "Powder_Mixture": "Powder Mixture", + "Base Score": "Base Score" +} diff --git a/src/i18n/en/importExcel.json b/src/i18n/en/importExcel.json new file mode 100644 index 0000000..14388f3 --- /dev/null +++ b/src/i18n/en/importExcel.json @@ -0,0 +1,15 @@ +{ + "title": "Excel File Import", + "Download Template": "Download Template", + "Download failed": "Download failed", + "Downloading...": "Downloading...", + "File Name": "File Name", + "No Import Record": "No Import Record", + "Upload": "Upload", + "Upload completed with count": "{{count}} item(s) updated", + "Upload failed": "Upload failed", + "Upload row errors": "Upload row errors", + "Upload successful": "Upload successful", + "Uploading...": "Uploading...", + "item(s) updated": "item(s) updated" +} diff --git a/src/i18n/en/inventory.json b/src/i18n/en/inventory.json index 81e3feb..1bb7e57 100644 --- a/src/i18n/en/inventory.json +++ b/src/i18n/en/inventory.json @@ -1,56 +1,206 @@ { - "Stock transfer successful": "Stock transfer completed", - "Stock transfer merged ambiguous": "Merged into the earliest available batch (multiple available lines for the same lot).", - "Stock transfer merged existing lot": "Stock transfer completed (merged into existing lot).", - "Stock transfer created new lot": "Stock transfer completed (created new lot line).", - "Average unit price": "Average unit price", - "Latest market unit price": "Latest market unit price", + "-{{Variance}}≤Variance Percentage ≤{{Variance}} will be filtered out": "-{{Variance}}≤Variance Percentage ≤{{Variance}} will be filtered out", + "AVAILABLE": "AVAILABLE", + "Action": "Action", + "Add": "Add", + "Add entry": "Add entry", "Add entry for items without inventory": "Add entry for items without inventory", - "Enter item code or name to search": "Enter item code or name to search", + "Adjusted Qty": "Adjusted Qty", + "Approve": "Approve", + "Approver All": "Approver All", + "Approver search empty hint": "Set search criteria and click Search", + "ApproverAll": "ApproverAll", + "Available": "Available", + "Available Qty": "Available Qty", + "Available Qty Per Smallest Unit": "Available Qty Per Smallest Unit", + "Average unit price": "Average unit price", + "Back": "Back", + "Bad Item Handle": "Bad Item Handle", + "Bad Item Qty": "Bad Item Qty", + "Bad Item Records": "Bad Item Records", + "Balance Qty": "Balance Qty", + "Base UoM": "Base UoM", + "Batch Save Completed": "Batch Save Completed", + "Batch Save Inputted": "Batch Save Inputted", + "Batch Submit All": "Batch Submit All", + "Batch approver save completed: {{success}} success, {{skipped}} skipped, {{errors}} errors": "Batch approver save completed: {{success}} success, {{skipped}} skipped, {{errors}} errors", + "Body is unavailable": "Body is unavailable", + "Bom Req. Qty": "Bom Req. Qty", + "CMB": "Consumables", + "CO": "Consumables", + "Clear selection": "Clear selection", + "Code": "Code", + "Collapse floor sections": "Collapse sections for this floor", + "Confirm Adjustment": "Confirm Adjustment", + "Confirm create stock take for all sections?": "Confirm create stock take for all sections?", + "Confirm remove": "Confirm remove", + "Counted Qty": "Counted Qty", + "Create Stock Take for All Sections": "Create Stock Take for All Sections", "Current Stock": "Current Stock", + "Delivery Order": "Delivery Order", + "Difference": "Difference", + "Download QR Code": "Download QR Code", + "Edit mode": "Edit mode", + "End Date": "End Date", + "Enter item code or name to search": "Enter item code or name to search", + "Expand floor sections": "Expand sections for this floor", + "Expiry Date": "Expiry Date", + "Expiry End Date": "Expiry End Date", + "Expiry Item Handle": "Expiry Item Handle", + "Expiry Item Qty": "Expiry Item Qty", + "Expiry Item Records": "Expiry Item Records", + "Expiry Qty": "Expiry Qty", + "Expiry Start Date": "Expiry Start Date", + "FG": "Finished good", + "Failed to transfer stock": "Failed to transfer stock", + "Failed to transfer stock. Please try again.": "Failed to transfer stock. Please try again.", + "Filtered out": "Filtered out", + "First Qty": "First Qty", + "Handled Date": "Handled Date", + "Handler": "Handler", + "Hide Search Filters": "Hide Search Filters", + "In Qty": "In Qty", + "Input": "Input", + "Invalid QTY": "Invalid quantity", + "Inventory": "Inventory", + "Inventory Exception Management": "Inventory Exception Management", + "Item Name": "Item Name", + "Item selected": "Item selected", + "Item-lotNo": "Item-lotNo", + "Job Order": "Job Order", + "Latest market unit price": "Latest market unit price", + "Loading": "Loading", + "Lot No": "Lot No", + "MA": "Material", + "MI": "Miscellaneous", + "Material": "Material", + "NM": "Miscellaneous / non-consumables", + "Name": "Name", + "No Data": "No Data", + "No changes to submit": "No changes to submit", + "No issues found": "No issues found", + "No items are selected yet.": "No items are selected yet.", + "No lot no entered, will be generated by system.": "No lot no entered, will be generated by system.", + "No record found": "No record found", + "Opening Inventory": "Opening Inventory", + "Optional - system will generate": "Optional - system will generate", + "Original Qty": "Original Qty", + "Out Qty": "Out Qty", + "Perform Stock Take": "Perform Stock Take", + "Pick Order, Issue No, Item, Lot...": "Pick Order, Issue No, Item, Lot...", + "Please enter QTY and Bad QTY": "Please enter QTY and Bad QTY", + "Please scan...": "Please scan...", "Please set at least one search criterion": "Please set at least one search criterion", - "Approver search empty hint": "Set search criteria and click Search", - "Saved successfully": "Saved successfully", - "Save failed": "Save failed", + "Print": "Print", + "Print QR Code": "Print QR Code", + "Print Qty": "Print Qty", + "Print failed": "Print failed", + "Print sent": "Print sent", + "Printer": "Printer", + "Qty": "Qty", + "Qty To Be Transferred": "Qty To Be Transferred", + "Quantity exceeds available quantity": "Quantity exceeds available quantity", + "RM": "Raw material", + "Reason for adjustment": "Reason for adjustment", + "Reason for removal": "Reason for removal", + "Refresh": "Refresh", + "Remaining Qty": "Remaining Qty", + "Remarks": "Remarks", "Remove": "Remove", - "Create Stock Take (Select Sections)": "Create stock take (select sections)", - "Select stock take sections to create hint": "Choose warehouse stock take sections to start a new round (selected sections share one new round id in this batch).", - "Stock take round name": "Stock take round name", - "Stock take round name placeholder": "Optional, e.g. May full-site stock take", - "Select sections placeholder": "Select one or more", + "Resolved": "Resolved", + "Review Variance": "Review Variance", + "Rows per page": "Rows per page", + "SFG": "Semi-finished good", + "Sales UoM": "Sales UoM", + "Save failed": "Save failed", + "Saved successfully": "Saved successfully", + "Search lot by QR code": "Search lot by QR code", + "Search to load lot lines": "Search to load lot lines", + "Second Qty": "Second Qty", + "Select": "Select", + "Select Section": "Select Section", + "Select Stock Take Section": "Select Stock Take Section", "Select all sections": "Select all sections", - "Clear selection": "Clear selection", + "Select sections placeholder": "Select one or more", + "Select stock take sections to create hint": "Choose warehouse stock take sections to start a new round (selected sections share one new round id in this batch).", "Selected section count": "Selected: {{count}} section(s)", - "Floor unassigned": "Unassigned floor", - "No stock take sections from warehouse": "No stock take sections returned from warehouse.", - "Expand floor sections": "Expand sections for this floor", - "Collapse floor sections": "Collapse sections for this floor", - "Select all on this floor": "Select all on this floor ({{floor}})", - "Deselect all on this floor": "Deselect all on this floor ({{floor}})", - "Creation date": "Creation date", - "Floor area selection header": "{{floor}} area selection ({{count}} areas)", - "Search section code or name": "Search code or name (e.g. ST-042 or drinks)", - "Select all sections all floors": "Select all areas (all floors)", - "Clear selection all floors": "Clear selection (all floors)", - "Total selected sections label": "Total selected:", - "sections unit": "area(s)", - "No sections match search": "No areas match your search", - "Variance filter inclusive only": "Show only rows within variance range", + "Show Search Filters": "Show Search Filters", + "Something went wrong fetching data in server.": "Something went wrong fetching data in server.", + "Start Date": "Start Date", + "Start Location": "Start Location", + "Start New Stock Take": "Start New Stock Take", + "Start Time": "Start Time", + "Stock Adjustment": "Stock Adjustment", + "Stock Record": "Stock Record", + "Stock Take Variance": "Stock Take Variance", + "Stock Transfer": "Stock Transfer", + "Stock UoM": "Stock UoM", + "Stock take adjustment confirmed! (Demo only)": "Stock take adjustment confirmed! (Demo only)", + "Stock take adjustment has been confirmed successfully!": "Stock take adjustment has been confirmed successfully!", + "Stock take qty exceeds maximum": "Stock take quantity cannot exceed 999,999,999,999", + "Stock transfer created new lot": "Stock transfer completed (created new lot line).", + "Stock transfer merged ambiguous": "Merged into the earliest available batch (multiple available lines for the same lot).", + "Stock transfer merged existing lot": "Stock transfer completed (merged into existing lot).", + "Stock transfer successful": "Stock transfer completed", + "Stop QR Scan": "Stop QR Scan", + "Submit": "Submit", + "Submit completed: {{success}} success, {{errors}} errors": "Submit completed: {{success}} success, {{errors}} errors", + "System Qty": "System Qty", + "Target Location": "Target Location", + "Total Approved": "Total Approved", + "Total Issues": "Total Issues", + "Total Item Number": "Total Item Number", + "Total Stock Takes": "Total Stock Takes", + "Total need stock take": "Total need stock take", + "Type": "Type", + "UNAVAILABLE": "UNAVAILABLE", "Variance filter strict bounds": "Exclude boundaries (use > <)", - "Variance filter exclusive range hint": "Show rows with -{{value}}% {{op}} variance % {{op}} {{value}}% (outside range, server-filtered)", - "Variance filter inclusive range hint": "Show rows with -{{value}}% {{op}} variance % {{op}} {{value}}% (within range)", - "Confirm batch save approver": "Confirm batch save", - "Batch save confirm message": "Batch save {{count}} stock take record(s) in the current list. Continue?", - "Stock take sections in current list": "{{count}} stock take section(s) in current list", - "Confirm create stock take": "Confirm create stock take", - "Not filled": "(not filled)", - "Warehouse missing stock take section warn title": "Warehouses without stock take section", - "Warehouse missing stock take section tooltip has": "{{count}} warehouse location(s) missing stock take section — click to view", - "Warehouse missing stock take section tooltip none": "All warehouses have a stock take section", - "Warehouse missing stock take section drawer hint": "These locations have no stock take section (ST-xxx) and cannot be included in stock take. Fix in warehouse settings.", - "Warehouse missing stock take section go settings": "Go to warehouse settings", - "Warehouse missing stock take section showing": "Showing {{shown}} of {{count}}", - "Warehouse missing stock take section empty": "No warehouses missing stock take section", - "Invalid QTY": "Invalid quantity", - "Stock take qty exceeds maximum": "Stock take quantity cannot exceed 999,999,999,999" -} \ No newline at end of file + "View": "View", + "WIP": "Work in progress", + "Waiting for Approver": "Waiting for Approver", + "Warehouse": "Warehouse", + "adj": "adj", + "approver": "approver", + "approving": "approving", + "available": "available", + "bad": "bad", + "cmb": "Consumables", + "complete": "complete", + "completed": "completed", + "completed by": "completed by", + "completed date": "completed date", + "completed remarks": "completed remarks", + "completed status": "completed status", + "consumable": "Consumable", + "consumables": "Consumables", + "dnNo": "dnNo", + "expiry": "expiry", + "fg": "Finished good", + "item": "Item", + "mat": "Raw material", + "miss": "miss", + "nm": "Miscellaneous / non-consumables", + "non-consumables": "Non-consumables", + "nor": "nor", + "not available": "not available", + "not match": "not match", + "not pass": "not pass", + "notMatch": "notMatch", + "notmatch": "notmatch", + "open": "open", + "pass": "pass", + "pending": "pending", + "productLotNo": "productLotNo", + "rejected": "rejected", + "save": "save", + "section": "section", + "sfg": "Semi-finished good", + "stockTaking": "stockTaking", + "tke": "tke", + "to": "to", + "trf": "trf", + "unavailable": "unavailable", + "variance": "variance", + "wip": "Work in progress", + "材料": "Material" +} diff --git a/src/i18n/en/itemPrice.json b/src/i18n/en/itemPrice.json new file mode 100644 index 0000000..d036b8f --- /dev/null +++ b/src/i18n/en/itemPrice.json @@ -0,0 +1,30 @@ +{ + "Price Inquiry": "Price Inquiry", + "No Purchase Order After 2026-01-01": "No Purchase Order After 2026-01-01", + "Code": "Code", + "Name": "Name", + "Searching": "Searching", + "No item selected": "No item selected", + "Average unit price": "Average unit price", + "Latest market unit price": "Latest market unit price", + "No Import Record": "No Import Record", + "Download Template": "Download Template", + "Downloading...": "Downloading...", + "Upload": "Upload", + "Uploading...": "Uploading...", + "Download failed": "Download failed", + "Upload failed": "Upload failed", + "Upload successful": "Upload successful", + "item(s) updated": "item(s) updated", + "Upload row errors": "Upload row errors", + "FG": "Finished good", + "RM": "Raw material", + "SFG": "Semi-finished good", + "WIP": "Work in progress", + "CMB": "Consumables", + "NM": "Miscellaneous / non-consumables", + "MA": "Material", + "MI": "Miscellaneous", + "CO": "Consumables", + "MAT": "Material" +} diff --git a/src/i18n/en/items.json b/src/i18n/en/items.json index 1acc8c5..859f7ef 100644 --- a/src/i18n/en/items.json +++ b/src/i18n/en/items.json @@ -1,22 +1,70 @@ { - "LocationCode": "Location Code", - "DefaultLocationCode": "Default Location Code", - "Special Type": "Special Type", - "None": "None", - "isEgg": "Egg", - "isFee": "Fee", - "isBag": "Bag", + "Actions": "Actions", "Back": "Back", - "Status": "Status", + "Cancel": "Cancel", + "Code": "Code", + "Code or name": "Code or name", "Complete": "Complete", - "Missing Data": "Missing Data", + "Confirm": "Confirm", + "Create Material": "Create Material", + "DefaultLocationCode": "Default Location Code", + "Demand Forecast Period": "Demand Forecast Period", + "Details": "Details", + "EPQC": "EPQC", + "Edit Product / Material": "Edit Product / Material", + "Finished Goods Name": "Finished Goods Name", + "IPQC": "IPQC", + "Item": "Item", "Loading QC items...": "Loading QC items...", - "Select a QC template to view items": "Select a QC template to view items", + "LocationCode": "Location Code", + "Missing Data": "Missing Data", + "Name": "Name", "No QC items in this template": "No QC items in this template", + "None": "None", + "Product": "Product", + "Product / Material": "Product / Material", + "Product / Material Details": "Product / Material Details", + "Product Count": "Product Count", + "Product Count(s)": "Product Count(s)", + "Product Details": "Product Details", "QC Checklist": "QC Checklist", "QC Type": "QC Type", - "IPQC": "IPQC", - "EPQC": "EPQC", - "Item": "Item", - "Code or name": "Code or name" -} \ No newline at end of file + "Qc Category": "Qc Category", + "Qc items": "Qc items", + "Release": "Release", + "Reset": "Reset", + "Save": "Save", + "Schedule Period": "Schedule Period", + "Scheduled At": "Scheduled At", + "Search": "Search", + "Select a QC template to view items": "Select a QC template to view items", + "Special Type": "Special Type", + "Status": "Status", + "Type": "Type", + "fg": "FG", + "wip": "WIP", + "mat": "MAT", + "cmb": "CMB", + "nm": "NM", + "code": "code", + "countryOfOrigin": "countryOfOrigin", + "description": "description", + "instruction": "instruction", + "isBag": "Bag", + "isEgg": "Egg", + "isFee": "Fee", + "lowerLimit": "lowerLimit", + "maxQty": "maxQty", + "name": "name", + "no rows": "no rows", + "remarks": "remarks", + "shelfLife": "shelfLife", + "upperLimit": "upperLimit", + "Item Code": "Item Code", + "Item Name": "Item Name", + "Sales Qty": "Sales Qty", + "Sales UOM": "Sales UOM", + "Stock Qty": "Stock Qty", + "Uom": "Uom", + "Cost (HKD)": "Cost (HKD)" +} diff --git a/src/i18n/en/jo.json b/src/i18n/en/jo.json new file mode 100644 index 0000000..0d94138 --- /dev/null +++ b/src/i18n/en/jo.json @@ -0,0 +1,645 @@ +{ + "2/F plastic box carton Qty": "2/F plastic box carton Qty", + "2/F plastic box carton qty count": "2/F plastic box carton qty count", + "3/F plastic box carton Qty": "3/F plastic box carton Qty", + "3/F plastic box carton qty count": "3/F plastic box carton qty count", + "4/F plastic box carton Qty": "4/F plastic box carton Qty", + "4/F plastic box carton qty count": "4/F plastic box carton qty count", + "Action": "Action", + "Actions": "Actions", + "Actual Pick Qty": "Actual Pick Qty", + "Add Bag": "Add Bag", + "Add Record": "Add Record", + "Add Selected Items to Created Items": "Add Selected Items to Created Items", + "Add some entries!": "Add some entries!", + "All": "All", + "All Pick Order Lots": "All Pick Order Lots", + "All floors": "All floors", + "All pick orders created successfully": "All pick orders created successfully", + "Are you sure you want to delete this procoess?": "Are you sure you want to delete this procoess?", + "Assignment failed: ": "Assignment failed: ", + "Assignment successful": "Assignment successful", + "Assume End Time": "Assume End Time", + "At least one issue must be reported": "At least one issue must be reported", + "Auto-refresh every 1 minute": "Auto-refresh every 1 minute", + "Auto-refresh every 10 minutes": "Auto-refresh every 10 minutes", + "Auto-refresh every 15 minutes": "Auto-refresh every 15 minutes", + "Auto-refresh every 5 minutes": "Auto-refresh every 5 minutes", + "Available Qty": "Available Qty", + "Available in warehouse": "Available in warehouse", + "BOM Description": "BOM Description", + "BOM Status": "BOM Status", + "BOM Type": "BOM Type", + "Back": "Back", + "Back to List": "Back to List", + "Bad Item Qty": "Bad Item Qty", + "Bad Package Qty": "Bad Package Qty", + "Bag": "Bag", + "Bag Code": "Bag Code", + "Bag Consumption": "Bag Consumption", + "Bag Consumption Records": "Bag Consumption Records", + "Bag List": "Bag List", + "Bag Lot Lines": "Bag Lot Lines", + "Bag Name": "Bag Name", + "Bag Usage": "Bag Usage", + "Balance": "Balance", + "Balance Qty": "Balance Qty", + "Base Qty": "Base Qty", + "Base UOM": "Base UOM", + "Batch Count": "Batch Count", + "BoM Material": "BoM Material", + "Bom": "Bom", + "Bom Req. Qty": "Bom Req. Qty", + "Bom Uom": "Bom Uom", + "By-product": "By-product", + "COMPLETED": "COMPLETED", + "Cancel": "Cancel", + "Cancel Job Order": "Cancel Job Order", + "Cancel job order confirm message": "Cancel job order confirm message", + "Changeover Time": "Changeover Time", + "Changeover Time (mins)": "Changeover Time (mins)", + "Changeover Time (mins)": "Changeover Time (mins)", + "Clean Record": "Clean Record", + "Code": "Code", + "Code / Lot No": "Code / Lot No", + "Complete Job Order Record": "Complete Job Order Record", + "Complete Step": "Complete Step", + "Completed": "Completed", + "Completed Step": "Completed Step", + "Confirm": "Confirm", + "Confirm All": "Confirm All", + "Confirm Lot Substitution": "Confirm Lot Substitution", + "Confirm cancel job order": "Confirm cancel job order", + "Confirm delete job order": "Confirm delete job order", + "Consolidate": "Consolidate", + "Consolidated Code": "Consolidated Code", + "Consumable": "Consumable", + "Consumed Qty": "Consumed Qty", + "Continue": "Continue", + "Count of Job Orders": "Count of Job Orders", + "Create": "Create", + "Create Job Order": "Create Job Order", + "Create New Group": "Create New Group", + "Create Pick Order": "Create Pick Order", + "Create Project": "Create Project", + "Create Task Template": "Create Task Template", + "Created Items": "Created Items", + "Current Stock": "Current Stock", + "Customer": "Customer", + "Date": "Date", + "Default Warehouse": "Default Warehouse", + "Defect": "Defect", + "Delete Job Order": "Delete Job Order", + "Delete job order confirm message": "Delete job order confirm message", + "Deliver Order": "Deliver Order", + "Delivery Code": "Delivery Code", + "Delivery Date": "Delivery Date", + "Delivery Note Code": "Delivery Note Code", + "Delivery Order": "Delivery Order", + "Demand Forecast": "Demand Forecast", + "Demand Forecast Setting": "Demand Forecast Setting", + "Departure Time": "Departure Time", + "Describe the issue with bad items": "Describe the issue with bad items", + "Description": "Description", + "Detail Scheduling": "Detail Scheduling", + "Details": "Details", + "Do you want to start job order": "Do you want to start job order", + "Download Excel": "Download Excel", + "Drink": "Drink", + "Duration hours": "Duration hours", + "Duration minutes": "Duration minutes", + "Duration seconds": "Duration seconds", + "Edit": "Edit", + "Edit Equipment": "Edit Equipment", + "Edit Equipment Type": "Edit Equipment Type", + "Edit Job Order": "Edit Job Order", + "Edit Job Order Detail": "Edit Job Order Detail", + "End Product": "End Product", + "End Qty": "End Qty", + "Enter all floors plastic box carton qty": "Enter all floors plastic box carton qty", + "Enter bad item quantity (required if no missing items)": "Enter bad item quantity (required if no missing items)", + "Enter missing quantity (required if no bad items)": "Enter missing quantity (required if no bad items)", + "Enter plastic box carton qty": "Enter plastic box carton qty", + "Enter print quantity": "Enter print quantity", + "Enter the number of cartons": "Enter the number of cartons", + "Enter the number of cartons: ": "Enter the number of cartons: ", + "Equipment": "Equipment", + "Equipment Code": "Equipment Code", + "Equipment Details": "Equipment Details", + "Equipment Name and Code": "Equipment Name and Code", + "Equipment Type Details": "Equipment Type Details", + "EquipmentType-EquipmentName-Code": "EquipmentType-EquipmentName-Code", + "Escalation History": "Escalation History", + "Escalation Info": "Escalation Info", + "Escalation Result": "Escalation Result", + "Estimated Production Date": "Estimated Production Date", + "Exclude Date": "Exclude Date", + "Executing": "Executing", + "Expected Lot:": "Expected Lot:", + "Expiry Date": "Expiry Date", + "Exporting...": "Exporting...", + "FG": "FG", + "FG & Material Demand Forecast Detail": "FG & Material Demand Forecast Detail", + "FG / WIP Item": "FG / WIP Item", + "FG Production Schedule": "FG Production Schedule", + "Failed to create group": "Failed to create group", + "Failed to load plastic box carton qty dashboard": "Failed to load plastic box carton qty dashboard", + "Failed to submit scan data. Please try again.": "Failed to submit scan data. Please try again.", + "Finish": "Finish", + "Finished Good Order": "Finished Good Order", + "Finished Goods Name": "Finished Goods Name", + "Finished Job Order Record": "Finished Job Order Record", + "Finished lines": "Finished lines", + "First created group": "First created group", + "Floor": "Floor", + "Group": "Group", + "Handled By": "Handled By", + "Handler": "Handler", + "Idle": "Idle", + "If you confirm, the system will:": "If you confirm, the system will:", + "Import Testing": "Import Testing", + "In Progress": "In Progress", + "In progress": "In progress", + "In_Progress": "In_Progress", + "Index": "Index", + "Input Equipment is not match with process": "Input Equipment is not match with process", + "Insufficient available quantity on lot (may have been picked by another user)": "Insufficient available quantity on lot (may have been picked by another user)", + "Invalid Stock In Line Id": "Invalid Stock In Line Id", + "Invalid date format": "Invalid date format", + "Inventory": "Inventory", + "Is Dark": "Is Dark", + "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity", + "Is Dense": "Is Dense", + "Is Float": "Is Float", + "Issue": "Issue", + "Issue Remark": "Issue Remark", + "Item": "Item", + "Item Code": "Item Code", + "Item Name": "Item Name", + "Item already exists in created items": "Item already exists in created items", + "Items": "Items", + "Jo Pick Order Detail": "Jo Pick Order Detail", + "Job Order": "Job Order", + "Job Order Code": "Job Order Code", + "Job Order Info": "Job Order Info", + "Job Order Item Name": "Job Order Item Name", + "Job Order Match": "Job Order Match", + "Job Order No.": "Job Order No.", + "Job Order Pick Execution": "Job Order Pick Execution", + "Job Order Pick Order Details": "Job Order Pick Order Details", + "Job Order Pickexcution": "Job Order Pickexcution", + "Job Order Qty": "Job Order Qty", + "Job Order Type": "Job Order Type", + "Job Order not found or has no items": "Job Order not found or has no items", + "Job Process Status Dashboard": "Job Process Status Dashboard", + "Job Type": "Job Type", + "Job dashboard PP status: cancelled": "Job dashboard PP status: cancelled", + "Job dashboard PP status: completed": "Job dashboard PP status: completed", + "Job dashboard PP status: in_progress": "Job dashboard PP status: in_progress", + "Job dashboard PP status: pending": "Job dashboard PP status: pending", + "Job dashboard PP status: stopped": "Job dashboard PP status: stopped", + "Job process detail mode label": "Job process detail mode label", + "Job process detail: equipment": "Job process detail: equipment", + "Job process detail: handler": "Job process detail: handler", + "Job process detail: process name": "Job process detail: process name", + "Job process detail: time": "Job process detail: time", + "Just Complete": "Just Complete", + "Just Completed (workbench): requires valid quantity; expired rows must not use this button.": "Just Completed (workbench): requires valid quantity; expired rows must not use this button.", + "Last 7 days": "Last 7 days", + "Last updated": "Last updated", + "Latest created group": "Latest created group", + "Lines with insufficient stock: ": "Lines with insufficient stock: ", + "Lines with sufficient stock: ": "Lines with sufficient stock: ", + "Loading...": "Loading...", + "Location": "Location", + "Lot Actual Pick Qty": "Lot Actual Pick Qty", + "Lot Availability": "Lot Availability", + "Lot Details": "Lot Details", + "Lot No": "Lot No", + "Lot No.": "Lot No.", + "Lot Number Mismatch": "Lot Number Mismatch", + "Lot Required Pick Qty": "Lot Required Pick Qty", + "Lot has been rejected and marked as unavailable.": "Lot has been rejected and marked as unavailable.", + "LotNo": "LotNo", + "Mail": "Mail", + "Management Job Order": "Management Job Order", + "Manual Input": "Manual Input", + "Matching Stock": "Matching Stock", + "Material": "Material", + "Material Code": "Material Code", + "Material Pick Status": "Material Pick Status", + "Max": "Max", + "Missing item Qty": "Missing item Qty", + "Missing items": "Missing items", + "Multiplier": "Multiplier", + "N/A": "N/A", + "Name": "Name", + "Next": "Next", + "Next page": "Next page", + "No": "No", + "No Group": "No Group", + "No Item": "No Item", + "No Qc": "No Qc", + "No Uom": "No Uom", + "No Warehouse": "No Warehouse", + "No chart data": "No chart data", + "No completed Job Order pick orders with matching found": "No completed Job Order pick orders with matching found", + "No created items": "No created items", + "No data available": "No data available", + "No data found": "No data found", + "No lot details available": "No lot details available", + "No results found": "No results found", + "No. of Items to be Picked": "No. of Items to be Picked", + "No. of Items with Issue During Pick": "No. of Items with Issue During Pick", + "None": "None", + "Note:": "Note:", + "Now": "Now", + "Number must be at least 1": "Number must be at least 1", + "Number of cartons": "Number of cartons", + "Open 挑號 QR 碼 on a pick line first, then scan {2fic} to use manual lot substitution.": "Open 挑號 QR 碼 on a pick line first, then scan {2fic} to use manual lot substitution.", + "Operator": "Operator", + "Operator KPI": "Operator KPI", + "Operator KPI Dashboard": "Operator KPI Dashboard", + "Operator Name & No.": "Operator Name & No.", + "Order Complete": "Order Complete", + "Order Quantity": "Order Quantity", + "Other": "Other", + "Output Qty": "Output Qty", + "Output from Process": "Output from Process", + "Over Time": "Over Time", + "Overall Time Remaining": "Overall Time Remaining", + "Overview": "Overview", + "Packaging": "Packaging", + "Partial quantity submitted. Please submit more or complete the order.": "Partial quantity submitted. Please submit more or complete the order.", + "Pass": "Pass", + "Passed Step": "Passed Step", + "Pause": "Pause", + "Pause Reason": "Pause Reason", + "Paused": "Paused", + "Pending": "Pending", + "Pending for pick": "Pending for pick", + "Pick End Time": "Pick End Time", + "Pick Execution Issue Form": "Pick Execution Issue Form", + "Pick Order": "Pick Order", + "Pick Order Code": "Pick Order Code", + "Pick Order Conso Code": "Pick Order Conso Code", + "Pick Order Detail": "Pick Order Detail", + "Pick Order Id": "Pick Order Id", + "Pick Order No.- Job Order No.- Item": "Pick Order No.- Job Order No.- Item", + "Pick Order Status": "Pick Order Status", + "Pick Order Target Date": "Pick Order Target Date", + "Pick Start Time": "Pick Start Time", + "Pick Time Taken (minutes)": "Pick Time Taken (minutes)", + "Pick order completed successfully!": "Pick order completed successfully!", + "Picked Qty": "Picked Qty", + "Plan Start": "Plan Start", + "Plan Start From": "Plan Start From", + "Plan Start To": "Plan Start To", + "Planning": "Planning", + "Plastic box carton Qty": "Plastic box carton Qty", + "Plastic box carton qty dashboard": "Plastic box carton qty dashboard", + "Plastic box carton qty multi period report": "Plastic box carton qty multi period report", + "Plastic box carton qty report last 7 days": "Plastic box carton qty report last 7 days", + "Plastic box carton qty report this month": "Plastic box carton qty report this month", + "Plastic box carton qty report this year": "Plastic box carton qty report this year", + "Plastic box carton qty usage": "Plastic box carton qty usage", + "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.": "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.", + "Please enter at least code or name": "Please enter at least code or name", + "Please enter quantity for all selected items": "Please enter quantity for all selected items", + "Please finish QR code scan and pick order.": "Please finish QR code scan and pick order.", + "Please make sure all required items are picked": "Please make sure all required items are picked", + "Please make sure the qty is enough": "Please make sure the qty is enough", + "Please scan equipment code": "Please scan equipment code", + "Please scan equipment code (optional if not required)": "Please scan equipment code (optional if not required)", + "Please scan operator code": "Please scan operator code", + "Please scan operator code first": "Please scan operator code first", + "Please scan staff no": "Please scan staff no", + "Please scan the item qr code": "Please scan the item qr code", + "Please scan warehouse qr code.": "Please scan warehouse qr code.", + "Please select": "Please select", + "Please select a printer": "Please select a printer", + "Please select a printer first": "Please select a printer first", + "Please select at least one item to submit": "Please select at least one item to submit", + "Please select group and enter quantity for all selected items": "Please select group and enter quantity for all selected items", + "Please select group for all selected items": "Please select group for all selected items", + "Please select item": "Please select item", + "Please select product type": "Please select product type", + "Please select target date": "Please select target date", + "Please select type": "Please select type", + "Please submit the pick order.": "Please submit the pick order.", + "Po Code": "Po Code", + "Powder Mixture": "Powder Mixture", + "Powder_Mixture": "Powder_Mixture", + "Previous page": "Previous page", + "Print Pick Record": "Print Pick Record", + "Print Quantity": "Print Quantity", + "Printed Successfully.": "Printed Successfully.", + "Printer": "Printer", + "Process": "Process", + "Process page summary": "Process page summary", + "Processing": "Processing", + "Processing QR code...": "Processing QR code...", + "Processing Status": "Processing Status", + "Processing Time": "Processing Time", + "Processing Time (mins)": "Processing Time (mins)", + "Processing...": "Processing...", + "Product": "Product", + "Product Type": "Product Type", + "Product process status": "Product process status", + "Production": "Production", + "Production Date": "Production Date", + "Production Equipment Status Dashboard": "Production Equipment Status Dashboard", + "Production Output Data": "Production Output Data", + "Production Output Data Entry": "Production Output Data Entry", + "Production Priority": "Production Priority", + "Production Priority required!": "Production Priority required!", + "Production Process": "Production Process", + "Production Process Information": "Production Process Information", + "Production Process Line Remark": "Production Process Line Remark", + "Production Process Steps": "Production Process Steps", + "Production Time Remaining": "Production Time Remaining", + "Production date": "Production date", + "Progress": "Progress", + "Project": "Project", + "Projects": "Projects", + "Purchase Order": "Purchase Order", + "Put Away": "Put Away", + "Put Away Scan": "Put Away Scan", + "Put Awayed": "Put Awayed", + "Putaway Detail": "Putaway Detail", + "QC Category": "QC Category", + "QC Check Item": "QC Check Item", + "QC Check Template": "QC Check Template", + "QC Info": "QC Info", + "QR Code Handle": "QR Code Handle", + "QR Code Scan for Lot": "QR Code Scan for Lot", + "QR Scan Result:": "QR Scan Result:", + "QR code does not match any item in current orders.": "QR code does not match any item in current orders.", + "QR code verified.": "QR code verified.", + "Qc Item": "Qc Item", + "Qty": "Qty", + "Qty Already Picked": "Qty Already Picked", + "Qty cannot be negative": "Qty cannot be negative", + "Qty is not allowed to be greater than required qty": "Qty is not allowed to be greater than required qty", + "Qty is required": "Qty is required", + "Qty must be a whole number": "Qty must be a whole number", + "Qty will submit": "Qty will submit", + "Quality Check": "Quality Check", + "Quantity": "Quantity", + "R&D": "R&D", + "RELEASED": "RELEASED", + "Reason": "Reason", + "Received Qty": "Received Qty", + "Reject": "Reject", + "Release": "Release", + "Released": "Released", + "Released By": "Released By", + "Remaining Available Qty": "Remaining Available Qty", + "Remaining Time (min)": "Remaining Time (min)", + "Remark": "Remark", + "Remember plan start as default": "Remember plan start as default", + "Repair": "Repair", + "Req. Qty": "Req. Qty", + "Required Qty": "Required Qty", + "Reset": "Reset", + "Route": "Route", + "Router": "Router", + "Row per page": "Row per page", + "Rows per page": "Rows per page", + "SEQ": "SEQ", + "STF": "STF", + "Save": "Save", + "Scan Operator & Equipment": "Scan Operator & Equipment", + "Scan Result": "Scan Result", + "Scan Status": "Scan Status", + "Scanned": "Scanned", + "Scanned Lot:": "Scanned Lot:", + "Scanning...": "Scanning...", + "Scrap": "Scrap", + "Scrap Qty": "Scrap Qty", + "Search": "Search", + "Search Criteria": "Search Criteria", + "Search Items": "Search Items", + "Search Job Order/ Create Job Order": "Search Job Order/ Create Job Order", + "Search Results": "Search Results", + "Second Scan Completed": "Second Scan Completed", + "Second Scan Pending": "Second Scan Pending", + "Second Scan Status": "Second Scan Status", + "Select Another Bag Lot": "Select Another Bag Lot", + "Select Bag": "Select Bag", + "Select Date": "Select Date", + "Select Printer": "Select Printer", + "Select Unit": "Select Unit", + "Select a printer": "Select a printer", + "Select warehouse": "Select warehouse", + "Selected": "Selected", + "Selected items will join above created group": "Selected items will join above created group", + "Seq": "Seq", + "Seq No": "Seq No", + "Seq No Remark": "Seq No Remark", + "Seq:": "Seq:", + "Sequence": "Sequence", + "Setup Time": "Setup Time", + "Setup Time (mins)": "Setup Time (mins)", + "Shop Address": "Shop Address", + "Shop ID": "Shop ID", + "Shop Name": "Shop Name", + "Shop PO Code": "Shop PO Code", + "Sign out": "Sign out", + "Skip": "Skip", + "Staff No": "Staff No", + "Staff No Required": "Staff No Required", + "Staff No:": "Staff No:", + "Start": "Start", + "Start Job Order": "Start Job Order", + "Start QR Scan": "Start QR Scan", + "Start Qty": "Start Qty", + "Start Scan": "Start Scan", + "Start Time": "Start Time", + "Status": "Status", + "Step": "Step", + "Step Information": "Step Information", + "Step Name": "Step Name", + "Still need to pick": "Still need to pick", + "Stock Available": "Stock Available", + "Stock Req. Qty": "Stock Req. Qty", + "Stock Status": "Stock Status", + "Stock UOM": "Stock UOM", + "Stock Unit": "Stock Unit", + "Stop": "Stop", + "Stop QR Scan": "Stop QR Scan", + "Stop Scan": "Stop Scan", + "Store ID": "Store ID", + "Storing": "Storing", + "Submit": "Submit", + "Submit & Start": "Submit & Start", + "Submit All Scanned": "Submit All Scanned", + "Submit Bag Consumption": "Submit Bag Consumption", + "Submit Required Pick Qty": "Submit Required Pick Qty", + "Submitting...": "Submitting...", + "Summary": "Summary", + "Supplier": "Supplier", + "Target Date": "Target Date", + "Target Date From": "Target Date From", + "Target Date To": "Target Date To", + "Target Production Date": "Target Production Date", + "Task Template": "Task Template", + "The input is not the same as the expected lot number.": "The input is not the same as the expected lot number.", + "The scanned item matches the expected item, but the lot number is different. Do you want to proceed with this different lot?": "The scanned item matches the expected item, but the lot number is different. Do you want to proceed with this different lot?", + "This form is for reporting issues only. You must report either missing items or bad items.": "This form is for reporting issues only. You must report either missing items or bad items.", + "This is last lot, so no available lot.": "This is last lot, so no available lot.", + "This lot is rejected, please scan another lot.": "This lot is rejected, please scan another lot.", + "This month": "This month", + "This order is insufficient, please pick another lot.": "This order is insufficient, please pick another lot.", + "This year": "This year", + "Ticket No.": "Ticket No.", + "Time": "Time", + "Time Information(mins)": "Time Information(mins)", + "Time Remaining": "Time Remaining", + "Time used": "Time used", + "Timer Paused": "Timer Paused", + "Today": "Today", + "Total": "Total", + "Total (Verified + Bad + Missing) must equal Required quantity": "Total (Verified + Bad + Missing) must equal Required quantity", + "Total Processing Time": "Total Processing Time", + "Total Steps": "Total Steps", + "Total lines: ": "Total lines: ", + "Total pick orders": "Total pick orders", + "Total plastic box carton qty": "Total plastic box carton qty", + "Total processes": "Total processes", + "Truck No.": "Truck No.", + "Two Days Ago": "Two Days Ago", + "Type": "Type", + "Unable to get user ID": "Unable to get user ID", + "Unassigned Job Orders": "Unassigned Job Orders", + "Unit": "Unit", + "Unknown": "Unknown", + "Unknown error": "Unknown error", + "Unknown error: ": "Unknown error: ", + "UoM": "UoM", + "Update Estimated Production Date": "Update Estimated Production Date", + "Update Job Order": "Update Job Order", + "Update Production Priority": "Update Production Priority", + "Update Time Information": "Update Time Information", + "Update your suggested lot to the this scanned lot": "Update your suggested lot to the this scanned lot", + "User not found with staffNo:": "User not found with staffNo:", + "Validation failed. Please check operator and equipment.": "Validation failed. Please check operator and equipment.", + "Verified Qty": "Verified Qty", + "Verified quantity cannot exceed received quantity": "Verified quantity cannot exceed received quantity", + "Verified successfully!": "Verified successfully!", + "View": "View", + "View Details": "View Details", + "View item In-out And inventory Ledger": "View item In-out And inventory Ledger", + "WIP": "WIP", + "Wait Time [minutes]": "Wait Time [minutes]", + "Warehouse": "Warehouse", + "Yesterday": "Yesterday", + "You need to enter a number": "You need to enter a number", + "acceptQty": "acceptQty", + "acceptQty must not greater than": "acceptQty must not greater than", + "acceptedQty": "acceptedQty", + "acceptedQty must not greater than": "acceptedQty must not greater than", + "acceptedWeight": "acceptedWeight", + "bind": "bind", + "bomWarn_close": "bomWarn_close", + "bomWarn_copyAll": "bomWarn_copyAll", + "bomWarn_empty": "bomWarn_empty", + "bomWarn_issue_MISSING_BOM_CODE": "bomWarn_issue_MISSING_BOM_CODE", + "bomWarn_issue_MISSING_BOM_NAME": "bomWarn_issue_MISSING_BOM_NAME", + "bomWarn_issue_MISSING_ITEM": "bomWarn_issue_MISSING_ITEM", + "bomWarn_issue_MISSING_SALES_UOM": "bomWarn_issue_MISSING_SALES_UOM", + "bomWarn_issue_MISSING_STOCK_UOM": "bomWarn_issue_MISSING_STOCK_UOM", + "bomWarn_issue_MISSING_STOCK_UOM_CONVERSION": "bomWarn_issue_MISSING_STOCK_UOM_CONVERSION", + "bomWarn_issue_MISSING_UOM_CONVERSION": "bomWarn_issue_MISSING_UOM_CONVERSION", + "bomWarn_loadFailed": "bomWarn_loadFailed", + "bomWarn_refresh": "bomWarn_refresh", + "bomWarn_refreshing": "bomWarn_refreshing", + "bomWarn_rowBomId": "bomWarn_rowBomId", + "bomWarn_rowItemId": "bomWarn_rowItemId", + "bomWarn_title": "bomWarn_title", + "bomWarn_tooltipHas": "bomWarn_tooltipHas", + "bomWarn_tooltipNone": "bomWarn_tooltipNone", + "cancel": "cancel", + "cancelled": "cancelled", + "complete jo": "complete jo", + "completed": "completed", + "completed Job Order pick orders with Matching": "completed Job Order pick orders with Matching", + "completed Job Order pick orders with matching": "completed Job Order pick orders with matching", + "consumables": "consumables", + "create": "create", + "edit": "edit", + "enter a qty": "enter a qty", + "equipmentType": "equipmentType", + "escalation": "escalation", + "escalation processing": "escalation processing", + "expiryDate": "expiryDate", + "failedQty": "failedQty", + "fg": "fg", + "finishedGood": "finishedGood", + "handler": "handler", + "hr": "hr", + "hrs": "hrs", + "id": "id", + "inProgress": "inProgress", + "in_progress": "in_progress", + "is expired. Please check around have available QR code or not.": "is expired. Please check around have available QR code or not.", + "is unavable. Please check around have available QR code or not.": "is unavable. Please check around have available QR code or not.", + "issue": "issue", + "issue remark": "issue remark", + "item": "item", + "itemName": "itemName", + "itemNo": "itemNo", + "items": "items", + "items completed": "items completed", + "jodetail": "jodetail", + "mat": "mat", + "min": "min", + "minimal value is 1": "minimal value is 1", + "mins": "mins", + "minutes": "minutes", + "non-consumables": "non-consumables", + "not default warehosue": "not default warehosue", + "packaging": "packaging", + "paused": "paused", + "pending": "pending", + "printQty": "printQty", + "process epqc": "process epqc", + "process stockIn": "process stockIn", + "productionDate": "productionDate", + "qcCategory": "qcCategory", + "qcItem": "qcItem", + "qcResult": "qcResult", + "qty": "qty", + "qty is not allowed to be greater than picked qty": "qty is not allowed to be greater than picked qty", + "qty is not allowed to be greater than remaining available qty": "qty is not allowed to be greater than remaining available qty", + "qty is not allowed to be greater than required qty": "qty is not allowed to be greater than required qty", + "qty is required": "qty is required", + "receivedQty": "receivedQty", + "release jo": "release jo", + "remarks": "remarks", + "reset": "reset", + "scan picked material": "scan picked material", + "scheduling": "scheduling", + "second Scan Status": "second Scan Status", + "select qc": "select qc", + "seq": "seq", + "settings": "settings", + "sfg": "sfg", + "stock in information": "stock in information", + "stopped": "stopped", + "storing": "storing", + "submit": "submit", + "submitting": "submitting", + "success": "success", + "supervisor": "supervisor", + "targetDate": "targetDate", + "type": "type", + "uom": "uom", + "update production priority": "update production priority", + "update qc info": "update qc info", + "update success": "update success", + "value must be a number": "value must be a number", + "view putaway": "view putaway", + "view stockin": "view stockin", + "warehouse": "warehouse" +} diff --git a/src/i18n/en/laserPrint.json b/src/i18n/en/laserPrint.json new file mode 100644 index 0000000..5cf6a96 --- /dev/null +++ b/src/i18n/en/laserPrint.json @@ -0,0 +1,3 @@ +{ + "title": "Laser Printer" +} diff --git a/src/i18n/en/login.json b/src/i18n/en/login.json index 9e26dfe..4b0d314 100644 --- a/src/i18n/en/login.json +++ b/src/i18n/en/login.json @@ -1 +1,11 @@ -{} \ No newline at end of file +{ + "Invalid username or password.": "Invalid username or password.", + "Login": "Login", + "Password": "Password", + "Please enter a password": "Please enter a password", + "Please enter a username": "Please enter a username", + "Sign In": "Sign In", + "Something went wrong. Please try again later.": "Something went wrong. Please try again later.", + "Username": "Username", + "toggle password visibility": "Toggle password visibility" +} diff --git a/src/i18n/en/m18ImportTesting.json b/src/i18n/en/m18ImportTesting.json new file mode 100644 index 0000000..1df1011 --- /dev/null +++ b/src/i18n/en/m18ImportTesting.json @@ -0,0 +1,16 @@ +{ + "testing sections tabs": "testing sections tabs", + "Import Delivery Order": "Import Delivery Order", + "Import Do": "Import Do", + "Import Master Data": "Import Master Data", + "Import Po": "Import Po", + "Import Pq": "Import Pq", + "Import Purchase Order": "Import Purchase Order", + "Import Purchase Quotation": "Import Purchase Quotation", + "Modified Date From": "Modified Date From", + "Modified Date From *": "Modified Date From *", + "Modified Date To": "Modified Date To", + "Modified Date To *": "Modified Date To *", + "Ready to import": "Ready to import", + "Status": "Status" +} diff --git a/src/i18n/en/m18Sync.json b/src/i18n/en/m18Sync.json new file mode 100644 index 0000000..f718dcb --- /dev/null +++ b/src/i18n/en/m18Sync.json @@ -0,0 +1,3 @@ +{ + "title": "M18 Sync" +} diff --git a/src/i18n/en/mail.json b/src/i18n/en/mail.json new file mode 100644 index 0000000..ffcf818 --- /dev/null +++ b/src/i18n/en/mail.json @@ -0,0 +1,25 @@ +{ + "Code": "Code", + "Description": "Description", + "MAIL.smtp.auth": "MAIL.smtp.auth", + "MAIL.smtp.host": "MAIL.smtp.host", + "MAIL.smtp.password": "MAIL.smtp.password", + "MAIL.smtp.port": "MAIL.smtp.port", + "MAIL.smtp.ssl": "MAIL.smtp.ssl", + "MAIL.smtp.username": "MAIL.smtp.username", + "Mail": "Mail", + "Mail Created At": "Mail Created At", + "Mail Description": "Mail Description", + "Mail List": "Mail List", + "Mail Name": "Mail Name", + "Mail Status": "Mail Status", + "Mail Updated At": "Mail Updated At", + "Save Fail": "Save Fail", + "Save Success": "Save Success", + "Select Template (View By Code - Description)": "Select Template (View By Code - Description)", + "Setting": "Setting", + "Settings": "Settings", + "Subject CHT": "Subject CHT", + "Template": "Template", + "send to everyone": "send to everyone" +} diff --git a/src/i18n/en/masterDataIssue.json b/src/i18n/en/masterDataIssue.json new file mode 100644 index 0000000..54374a1 --- /dev/null +++ b/src/i18n/en/masterDataIssue.json @@ -0,0 +1,101 @@ +{ + "Current Stock": "Current Stock", + "masterDataIssue": "masterDataIssue", + "masterDataIssue_BOM_MATERIAL_BASE_UOM_MISMATCH": "masterDataIssue_BOM_MATERIAL_BASE_UOM_MISMATCH", + "masterDataIssue_BOM_MATERIAL_MISSING_ITEM": "masterDataIssue_BOM_MATERIAL_MISSING_ITEM", + "masterDataIssue_BOM_MATERIAL_SALES_UOM_MISMATCH": "masterDataIssue_BOM_MATERIAL_SALES_UOM_MISMATCH", + "masterDataIssue_BOM_MATERIAL_STOCK_UOM_MISMATCH": "masterDataIssue_BOM_MATERIAL_STOCK_UOM_MISMATCH", + "masterDataIssue_BOM_MATERIAL_UOM_FK_INVALID": "masterDataIssue_BOM_MATERIAL_UOM_FK_INVALID", + "masterDataIssue_BOM_OUTPUT_UOM_MISMATCH_SALES": "masterDataIssue_BOM_OUTPUT_UOM_MISMATCH_SALES", + "masterDataIssue_BOM_OUTPUT_UOM_TEXT_DRIFT": "masterDataIssue_BOM_OUTPUT_UOM_TEXT_DRIFT", + "masterDataIssue_DELETED_BASE_UOM": "masterDataIssue_DELETED_BASE_UOM", + "masterDataIssue_DELETED_PURCHASE_UOM": "masterDataIssue_DELETED_PURCHASE_UOM", + "masterDataIssue_DELETED_SALES_UOM": "masterDataIssue_DELETED_SALES_UOM", + "masterDataIssue_DELETED_STOCK_UOM": "masterDataIssue_DELETED_STOCK_UOM", + "masterDataIssue_MISSING_BASE_UOM": "masterDataIssue_MISSING_BASE_UOM", + "masterDataIssue_MISSING_BASE_UOM_CONVERSION": "masterDataIssue_MISSING_BASE_UOM_CONVERSION", + "masterDataIssue_MISSING_BOM_CODE": "masterDataIssue_MISSING_BOM_CODE", + "masterDataIssue_MISSING_BOM_NAME": "masterDataIssue_MISSING_BOM_NAME", + "masterDataIssue_MISSING_ITEM": "masterDataIssue_MISSING_ITEM", + "masterDataIssue_MISSING_PICKING_UOM": "masterDataIssue_MISSING_PICKING_UOM", + "masterDataIssue_MISSING_PICKING_UOM_CONVERSION": "masterDataIssue_MISSING_PICKING_UOM_CONVERSION", + "masterDataIssue_MISSING_PURCHASE_UOM": "masterDataIssue_MISSING_PURCHASE_UOM", + "masterDataIssue_MISSING_PURCHASE_UOM_CONVERSION": "masterDataIssue_MISSING_PURCHASE_UOM_CONVERSION", + "masterDataIssue_MISSING_SALES_UOM": "masterDataIssue_MISSING_SALES_UOM", + "masterDataIssue_MISSING_SALES_UOM_CONVERSION": "masterDataIssue_MISSING_SALES_UOM_CONVERSION", + "masterDataIssue_MISSING_STOCK_UOM": "masterDataIssue_MISSING_STOCK_UOM", + "masterDataIssue_MISSING_STOCK_UOM_CONVERSION": "masterDataIssue_MISSING_STOCK_UOM_CONVERSION", + "masterDataIssue_MISSING_UOM_CONVERSION": "masterDataIssue_MISSING_UOM_CONVERSION", + "masterDataIssue_MULTIPLE_BASE_UOM": "masterDataIssue_MULTIPLE_BASE_UOM", + "masterDataIssue_MULTIPLE_PICKING_UOM": "masterDataIssue_MULTIPLE_PICKING_UOM", + "masterDataIssue_MULTIPLE_PURCHASE_UOM": "masterDataIssue_MULTIPLE_PURCHASE_UOM", + "masterDataIssue_MULTIPLE_SALES_UOM": "masterDataIssue_MULTIPLE_SALES_UOM", + "masterDataIssue_MULTIPLE_STOCK_UOM": "masterDataIssue_MULTIPLE_STOCK_UOM", + "masterDataIssue_bomMore": "masterDataIssue_bomMore", + "masterDataIssue_close": "masterDataIssue_close", + "masterDataIssue_col_actions": "masterDataIssue_col_actions", + "masterDataIssue_col_actual": "masterDataIssue_col_actual", + "masterDataIssue_col_bom": "BOM", + "masterDataIssue_col_bom_uom": "BOM UOM", + "masterDataIssue_col_expected": "masterDataIssue_col_expected", + "masterDataIssue_col_issue": "masterDataIssue_col_issue", + "masterDataIssue_col_item": "masterDataIssue_col_item", + "masterDataIssue_col_item_uom": "M18 UOM", + "masterDataIssue_col_problem": "masterDataIssue_col_problem", + "masterDataIssue_col_scope": "masterDataIssue_col_scope", + "masterDataIssue_col_subject": "masterDataIssue_col_subject", + "masterDataIssue_col_summary": "masterDataIssue_col_summary", + "masterDataIssue_copy": "masterDataIssue_copy", + "masterDataIssue_count": "masterDataIssue_count", + "masterDataIssue_detail_subtitle": "masterDataIssue_detail_subtitle", + "masterDataIssue_detail_uomBase": "masterDataIssue_detail_uomBase", + "masterDataIssue_detail_uomBoth": "masterDataIssue_detail_uomBoth", + "masterDataIssue_detail_uomSales": "masterDataIssue_detail_uomSales", + "masterDataIssue_detail_uomStock": "masterDataIssue_detail_uomStock", + "masterDataIssue_detail_usedInBom": "masterDataIssue_detail_usedInBom", + "masterDataIssue_empty": "masterDataIssue_empty", + "masterDataIssue_fgItem": "masterDataIssue_fgItem", + "masterDataIssue_filter_all": "masterDataIssue_filter_all", + "masterDataIssue_filter_type": "masterDataIssue_filter_type", + "masterDataIssue_group_count": "masterDataIssue_group_count", + "masterDataIssue_issueCount": "masterDataIssue_issueCount", + "masterDataIssue_line_generic": "masterDataIssue_line_generic", + "masterDataIssue_line_itemGeneric": "masterDataIssue_line_itemGeneric", + "masterDataIssue_line_itemMissing": "masterDataIssue_line_itemMissing", + "masterDataIssue_line_missingUnits": "masterDataIssue_line_missingUnits", + "masterDataIssue_line_outputText": "masterDataIssue_line_outputText", + "masterDataIssue_line_outputUom": "masterDataIssue_line_outputUom", + "masterDataIssue_line_pairBase": "masterDataIssue_line_pairBase", + "masterDataIssue_line_pairBoth": "masterDataIssue_line_pairBoth", + "masterDataIssue_line_pairSales": "masterDataIssue_line_pairSales", + "masterDataIssue_line_pairStock": "masterDataIssue_line_pairStock", + "masterDataIssue_line_problemOnly": "{{bom}}:{{problem}}", + "masterDataIssue_line_uomBoth": "masterDataIssue_line_uomBoth", + "masterDataIssue_line_uomSales": "masterDataIssue_line_uomSales", + "masterDataIssue_line_uomStock": "masterDataIssue_line_uomStock", + "masterDataIssue_loadFailed": "masterDataIssue_loadFailed", + "masterDataIssue_material": "masterDataIssue_material", + "masterDataIssue_materialUsedInBom": "masterDataIssue_materialUsedInBom", + "masterDataIssue_modifiedAt": "masterDataIssue_modifiedAt", + "masterDataIssue_nav": "masterDataIssue_nav", + "masterDataIssue_pageTitle": "masterDataIssue_pageTitle", + "masterDataIssue_refresh": "masterDataIssue_refresh", + "masterDataIssue_refreshing": "masterDataIssue_refreshing", + "masterDataIssue_scope_BOM": "masterDataIssue_scope_BOM", + "masterDataIssue_scope_BOM_MATERIAL": "masterDataIssue_scope_BOM_MATERIAL", + "masterDataIssue_scope_ITEM": "masterDataIssue_scope_ITEM", + "masterDataIssue_search": "masterDataIssue_search", + "masterDataIssue_tab_bom": "BOM", + "masterDataIssue_tab_item": "masterDataIssue_tab_item", + "masterDataIssue_unit_active": "masterDataIssue_unit_active", + "masterDataIssue_unit_base": "masterDataIssue_unit_base", + "masterDataIssue_unit_inactive": "masterDataIssue_unit_inactive", + "masterDataIssue_unit_missing": "masterDataIssue_unit_missing", + "masterDataIssue_unit_output": "masterDataIssue_unit_output", + "masterDataIssue_unit_picking": "masterDataIssue_unit_picking", + "masterDataIssue_unit_purchase": "masterDataIssue_unit_purchase", + "masterDataIssue_unit_sales": "masterDataIssue_unit_sales", + "masterDataIssue_unit_stock": "masterDataIssue_unit_stock", + "masterDataIssue_usedInBom": "masterDataIssue_usedInBom", + "masterDataIssue_viewDetail": "masterDataIssue_viewDetail" +} diff --git a/src/i18n/en/material.json b/src/i18n/en/material.json new file mode 100644 index 0000000..f10f621 --- /dev/null +++ b/src/i18n/en/material.json @@ -0,0 +1,4 @@ +{ + "Material": "Material", + "Create Claim": "Create Claim" +} diff --git a/src/i18n/en/navigation.json b/src/i18n/en/navigation.json new file mode 100644 index 0000000..8b6cbe0 --- /dev/null +++ b/src/i18n/en/navigation.json @@ -0,0 +1,99 @@ +{ + "menu": "menu", + "nav.dashboard": "Dashboard", + "nav.storeManagement": "Store Management", + "nav.store.purchaseOrder": "Purchase Order", + "nav.store.pickOrder": "Pick Order", + "nav.store.inventoryLedger": "View item In-out And inventory Ledger", + "nav.store.stockTake": "Stock Take Management", + "nav.store.stockIssue": "Stock Issue", + "nav.store.putAwayScan": "Put Away Scan", + "nav.store.finishedGoodManagement": "Finished Good Management", + "nav.store.stockRecord": "Stock Record", + "nav.store.doWorkbench": "DO Workbench", + "nav.deliveryOrder": "Delivery Order", + "nav.scheduling": "Scheduling", + "nav.jobOrderManagement": "Management Job Order", + "nav.jobOrder.searchCreate": "Search Job Order/ Create Job Order", + "nav.jobOrder.pickExecution": "Job Order Pick Execution", + "nav.jobOrder.productionProcess": "Job Order Production Process", + "nav.jobOrder.bagUsage": "Bag Usage", + "nav.bagPrint": "Bag Printer", + "nav.laserPrint": "Laser Printer", + "nav.report": "Report Management", + "nav.m18Sync": "M18 Sync", + "nav.chartReports": "Chart Reports", + "nav.chart.purchase": "Purchase", + "nav.chart.jobOrder": "Job Order", + "nav.chart.jobOrderBoard": "Job Order Live Board", + "nav.chart.delivery": "Delivery & Distribution", + "nav.chart.warehouse": "Inventory & Warehouse", + "nav.chart.forecast": "Forecast & Planning", + "nav.settings": "Settings", + "nav.settings.user": "User", + "nav.settings.clientMonitor": "Device Connection Monitor", + "nav.settings.items": "Items", + "nav.settings.equipment": "Equipment", + "nav.settings.warehouse": "Warehouse", + "nav.settings.printer": "Printer", + "nav.settings.priceInquiry": "Price Inquiry", + "nav.settings.qcItem": "QC Check Item", + "nav.settings.qcCategory": "QC Category", + "nav.settings.qcItemAll": "QC Item All", + "nav.settings.shopAndTruck": "Shop And Truck", + "nav.settings.deliveryOrderFloor": "DO floor (supplier)", + "nav.settings.demandForecast": "Demand Forecast Setting", + "nav.settings.bomWeighting": "BOM Weighting Score List", + "nav.settings.masterDataIssues": "BOM / Item UOM Issues", + "nav.settings.qrCodeHandle": "QR Code Handle", + "nav.settings.importTesting": "Import Testing", + "nav.settings.importExcel": "Import Excel", + "nav.settings.importBom": "Import BOM", + "nav.breadcrumb.home": "Overview", + "nav.breadcrumb.chart": "Chart Reports", + "nav.breadcrumb.chartWarehouse": "Inventory & Warehouse", + "nav.breadcrumb.chartPurchase": "Purchase", + "nav.breadcrumb.chartDelivery": "Delivery & Distribution", + "nav.breadcrumb.chartJobOrder": "Job Order", + "nav.breadcrumb.chartJobOrderBoard": "Job Order Live Board", + "nav.breadcrumb.chartForecast": "Forecast & Planning", + "nav.breadcrumb.projects": "Projects", + "nav.breadcrumb.projectsCreate": "Create Project", + "nav.breadcrumb.tasks": "Task Template", + "nav.breadcrumb.tasksCreate": "Create Task Template", + "nav.breadcrumb.qcItem": "QC Item", + "nav.breadcrumb.qcItemAll": "QC Item All", + "nav.breadcrumb.qrCodeHandle": "QR Code Handle", + "nav.breadcrumb.demandForecast": "Demand Forecast Setting", + "nav.breadcrumb.deliveryOrderFloor": "Delivery Order Floor", + "nav.breadcrumb.masterDataIssues": "BOM / Item UOM Issues", + "nav.breadcrumb.equipment": "Equipment", + "nav.breadcrumb.equipmentMaintenanceEdit": "Maintenance Edit", + "nav.breadcrumb.shop": "Shop And Truck", + "nav.breadcrumb.routeBoard": "Route Board", + "nav.breadcrumb.shopDetail": "Shop Detail", + "nav.breadcrumb.truckLaneDetail": "Truck Lane Detail", + "nav.breadcrumb.printer": "Printer", + "nav.breadcrumb.schedulingRough": "Demand Forecast", + "nav.breadcrumb.schedulingRoughEdit": "FG & Material Demand Forecast Detail", + "nav.breadcrumb.schedulingDetailed": "Detail Scheduling", + "nav.breadcrumb.schedulingDetailedEdit": "FG Production Schedule", + "nav.breadcrumb.inventory": "Inventory", + "nav.breadcrumb.importTesting": "Import Testing", + "nav.breadcrumb.doWorkbenchSearch": "DO Workbench Search", + "nav.breadcrumb.doWorkbenchPick": "DO Workbench Pick", + "nav.breadcrumb.doWorkbenchEdit": "DO Workbench Detail", + "nav.breadcrumb.poEdit": "Edit", + "nav.breadcrumb.poWorkbench": "PO Workbench", + "nav.breadcrumb.joEdit": "Edit Job Order", + "nav.breadcrumb.joTesting": "Job Order Testing", + "nav.breadcrumb.joWorkbench": "Job Order Workbench", + "nav.breadcrumb.report": "Report", + "nav.breadcrumb.m18Sync": "M18 Sync", + "nav.breadcrumb.bagPrint": "Bag Printer", + "nav.breadcrumb.laserPrint": "Laser Printer", + "nav.breadcrumb.priceInquiry": "Price Inquiry", + "nav.breadcrumb.finishedGood": "Finished Good Order", + "nav.breadcrumb.finishedGoodManagement": "Finished Good Management", + "nav.productionServer": "Production Server" +} diff --git a/src/i18n/en/pickOrder.json b/src/i18n/en/pickOrder.json new file mode 100644 index 0000000..4e6026c --- /dev/null +++ b/src/i18n/en/pickOrder.json @@ -0,0 +1,521 @@ +{ + "Purchase Order": "Purchase Order", + "Code": "Code", + "Pick Order Code": "Pick Order Code", + "Item Code": "Item Code", + "OrderDate": "OrderDate", + "Details": "Details", + "Supplier": "Supplier", + "Status": "Status", + "N/A": "N/A", + "Release Pick Orders": "Release Pick Orders", + "released": "released", + "Loading...": "Loading...", + "Suggestion success": "Suggestion success", + "Scan pick success": "Scan pick success", + "Remark": "Remark", + "Available Qty": "Available Qty", + "Picked Qty": "Picked Qty", + "Escalated": "Escalated", + "NotEscalated": "NotEscalated", + "Assigned To": "Assigned To", + "Progress": "Progress", + "Select Remark": "Select Remark", + "Just Complete": "Just Complete", + "Skip": "Skip", + "if need just edit number, please scan the lot again": "if need just edit number, please scan the lot again", + "Total qty (actual pick + miss + bad) cannot exceed available qty: {available}": "Total qty (actual pick + miss + bad) cannot exceed available qty: {available}", + "Confirm Assignment": "Confirm Assignment", + "Required Date": "Required Date", + "Store": "Store", + "Available Orders": "Available Orders", + "This lot is rejected, please scan another lot.": "This lot is rejected, please scan another lot.", + "Lane Code": "Lane Code", + "Fetching all matching records...": "Fetching all matching records...", + "Edit": "Edit", + "Submit Qty": "Submit Qty", + "Just Completed": "Just Completed", + "Just Completed (workbench): requires a valid lot number and quantity; expired rows must not use this button.": "Just Completed (workbench): requires a valid lot number and quantity; expired rows must not use this button.", + "Do you want to start?": "Do you want to start?", + "Start": "Start", + "Pick Order Code(s)": "Pick Order Code(s)", + "Delivery Order Code(s)": "Delivery Order Code(s)", + "Start Success": "Start Success", + "Qty will submit": "Qty will submit", + "Truck Lance Code": "Truck Lance Code", + "Pick Order Codes": "Pick Order Codes", + "Pick Order Lines": "Pick Order Lines", + "Delivery Order Codes": "Delivery Order Codes", + "Delivery Order Lines": "Delivery Order Lines", + "Lines Per Pick Order": "Lines Per Pick Order", + "Pick Orders Details": "Pick Orders Details", + "Lines": "Lines", + "Before Today": "Before Today", + "Truck X": "Truck X", + "Finsihed good items": "Finsihed good items", + "kinds": "kinds", + "Completed Date": "Completed Date", + "Completed Time": "Completed Time", + "Delivery Order": "Delivery Order", + "items": "items", + "Select Pick Order:": "Select Pick Order:", + "No Stock Available": "No Stock Available", + "is expired. Please check around have available QR code or not.": "is expired. Please check around have available QR code or not.", + "Start Fail": "Start Fail", + "Start PO": "Start PO", + "Do you want to complete?": "Do you want to complete?", + "Complete": "Complete", + "Complete Success": "Complete Success", + "Complete Fail": "Complete Fail", + "Complete Pick Order": "Complete Pick Order", + "General": "General", + "Bind Storage": "Bind Storage", + "itemNo": "itemNo", + "itemName": "itemName", + "qty": "qty", + "Require Qty": "Require Qty", + "uom": "uom", + "total weight": "total weight", + "weight unit": "weight unit", + "price": "price", + "processed": "processed", + "expiryDate": "expiryDate", + "acceptedQty": "acceptedQty", + "weight": "weight", + "start": "start", + "qc": "qc", + "escalation": "escalation", + "stock in": "stock in", + "putaway": "putaway", + "delete": "delete", + "qty cannot be greater than remaining qty": "qty cannot be greater than remaining qty", + "Record pol": "Record pol", + "Add some entries!": "Add some entries!", + "draft": "draft", + "pending": "pending", + "determine1": "determine1", + "determine2": "determine2", + "determine3": "determine3", + "receiving": "receiving", + "received": "received", + "completed": "completed", + "rejected": "rejected", + "success": "success", + "acceptedQty must not greater than": "acceptedQty must not greater than", + "minimal value is 1": "minimal value is 1", + "value must be a number": "value must be a number", + "qc Check": "qc Check", + "Please select QC": "Please select QC", + "failQty": "failQty", + "select qc": "select qc", + "enter a failQty": "enter a failQty", + "qty too big": "qty too big", + "sampleRate": "sampleRate", + "sampleWeight": "sampleWeight", + "totalWeight": "totalWeight", + "Escalation": "Escalation", + "to be processed": "to be processed", + "Stock In Detail": "Stock In Detail", + "productLotNo": "productLotNo", + "receiptDate": "receiptDate", + "acceptedWeight": "acceptedWeight", + "productionDate": "productionDate", + "reportQty": "reportQty", + "Default Warehouse": "Default Warehouse", + "Select warehouse": "Select warehouse", + "Putaway Detail": "Putaway Detail", + "LotNo": "LotNo", + "Po Code": "Po Code", + "No Warehouse": "No Warehouse", + "Please scan warehouse qr code.": "Please scan warehouse qr code.", + "Reject": "Reject", + "submit": "submit", + "print": "print", + "bind": "bind", + "Total must equal Required Qty. Missing: {diff}": "Total must equal Required Qty. Missing: {diff}", + "Total must equal Required Qty. Exceeds by: {diff}": "Total must equal Required Qty. Exceeds by: {diff}", + "Batch": "Batch", + "Single": "Single", + "Release Type": "Release Type", + "isExtra order": "isExtra order", + "Etra": "Etra", + "Exit Etra view": "Exit Etra view", + "Etra Pick Order Detail": "Etra Pick Order Detail", + "Etra incomplete badge tooltip": "Etra incomplete badge tooltip", + "Etra incomplete badge tooltip none": "Etra incomplete badge tooltip none", + "Back to normal assign tab": "Back to normal assign tab", + "Enter isExtra workbench view?": "Enter isExtra workbench view?", + "Etra view groups all add-on tickets by shop and lane for the selected date.": "Etra view groups all add-on tickets by shop and lane for the selected date.", + "Etra Ticket Notice": "Etra Ticket Notice", + "Pick Order": "Pick Order", + "Type": "Type", + "Product Type": "Product Type", + "Reset": "Reset", + "Search": "Search", + "Pick Orders": "Pick Orders", + "Consolidated Pick Orders": "Consolidated Pick Orders", + "Pick Order No.": "Pick Order No.", + "Pick Order Date": "Pick Order Date", + "Pick Order Status": "Pick Order Status", + "Pick Order Type": "Pick Order Type", + "Consolidated Code": "Consolidated Code", + "type": "type", + "Items": "Items", + "Target Date": "Target Date", + "Released By": "Released By", + "Target Date From": "Target Date From", + "Target Date To": "Target Date To", + "Consolidate": "Consolidate", + "Stock Unit": "Stock Unit", + "create": "create", + "detail": "detail", + "Pick Order Detail": "Pick Order Detail", + "item": "item", + "Unit": "Unit", + "reset": "reset", + "targetDate": "targetDate", + "remove": "remove", + "release": "release", + "location": "location", + "suggestedLotNo": "suggestedLotNo", + "lotNo": "lotNo", + "item name": "item name", + "Item Name": "Item Name", + "approval": "approval", + "lot change": "lot change", + "checkout": "checkout", + "Search Items": "Search Items", + "Search Results": "Search Results", + "Second Search Results": "Second Search Results", + "Second Search Items": "Second Search Items", + "Second Search": "Second Search", + "Item": "Item", + "Order Quantity": "Order Quantity", + "Current Stock": "Current Stock", + "Selected": "Selected", + "Select Items": "Select Items", + "Assign": "Assign", + "Release": "Release", + "Pick Execution": "Pick Execution", + "Create Pick Order": "Create Pick Order", + "Consumable": "Consumable", + "Material": "Material", + "Job Order": "Job Order", + "End Product": "End Product", + "Lot Expiry Date": "Lot Expiry Date", + "Lot Location": "Lot Location", + "Available Lot": "Available Lot", + "Lot Required Pick Qty": "Lot Required Pick Qty", + "Lot Actual Pick Qty": "Lot Actual Pick Qty", + "Lot#": "Lot#", + "Submit": "Submit", + "Created Items": "Created Items", + "Create New Group": "Create New Group", + "Group": "Group", + "Qty Already Picked": "Qty Already Picked", + "Select Job Order Items": "Select Job Order Items", + "failedQty": "failedQty", + "remarks": "remarks", + "Qc items": "Qc items", + "qcItem": "qcItem", + "QC Info": "QC Info", + "qcResult": "qcResult", + "acceptQty": "acceptQty", + "Escalation History": "Escalation History", + "Group Code": "Group Code", + "Job Order Code": "Job Order Code", + "QC Check": "QC Check", + "QR Code Scan": "QR Code Scan", + "Pick Order Details": "Pick Order Details", + "Partial quantity submitted. Please submit more or complete the order.": "Partial quantity submitted. Please submit more or complete the order.", + "Pick order completed successfully!": "Pick order completed successfully!", + "Lot has been rejected and marked as unavailable.": "Lot has been rejected and marked as unavailable.", + "This order is insufficient, please pick another lot.": "This order is insufficient, please pick another lot.", + "Please finish QR code scan, QC check and pick order.": "Please finish QR code scan, QC check and pick order.", + "No data available": "No data available", + "Please submit the pick order.": "Please submit the pick order.", + "Item lot to be Pick:": "Item lot to be Pick:", + "Report and Pick another lot": "Report and Pick another lot", + "Accept Stock Out": "Accept Stock Out", + "Pick Another Lot": "Pick Another Lot", + "Delivery Note Code": "Delivery Note Code", + "A4 Printer": "A4 Printer", + "Label Printer": "Label Printer", + "Please select a printer first": "Please select a printer first", + "Please select a label printer first": "Please select a label printer first", + "Lot No": "Lot No", + "Expiry Date": "Expiry Date", + "Location": "Location", + "All Pick Order Lots": "All Pick Order Lots", + "Completed": "Completed", + "Finished Good Order": "Finished Good Order", + "Assign and Release": "Assign and Release", + "Original Available Qty": "Original Available Qty", + "Remaining Available Qty": "Remaining Available Qty", + "Please submit pick order.": "Please submit pick order.", + "Please finish QR code scan and pick order.": "Please finish QR code scan and pick order.", + "Please finish QR code scanand pick order.": "Please finish QR code scanand pick order.", + "First created group": "First created group", + "Latest created group": "Latest created group", + "Manual Input": "Manual Input", + "QR Code Scan for Lot": "QR Code Scan for Lot", + "Processing QR code...": "Processing QR code...", + "The input is not the same as the expected lot number.": "The input is not the same as the expected lot number.", + "Verified successfully!": "Verified successfully!", + "Cancel": "Cancel", + "storing": "storing", + "pick successful": "pick successful", + "Insufficient available quantity on lot (may have been picked by another user)": "Insufficient available quantity on lot (may have been picked by another user)", + "Scan": "Scan", + "Before today": "Before today", + "Scanned": "Scanned", + "Loading data...": "Loading data...", + "No available stock for this item": "No available stock for this item", + "No lot details available for this item": "No lot details available for this item", + "Current stock is insufficient or unavailable": "Current stock is insufficient or unavailable", + "Please check inventory status": "Please check inventory status", + "Rows per page": "Rows per page", + "QR Scan Result:": "QR Scan Result:", + "Action": "Action", + "Please finish pick order.": "Please finish pick order.", + "Lot": "Lot", + "Assign Pick Orders": "Assign Pick Orders", + "Selected Pick Orders": "Selected Pick Orders", + "Please assgin/release the pickorders to picker": "Please assgin/release the pickorders to picker", + "Assign To": "Assign To", + "No Group": "No Group", + "Selected items will join above created group": "Selected items will join above created group", + "Issue": "Issue", + "Pick Execution Issue Form": "Pick Execution Issue Form", + "Lot line is unavailable": "Lot line is unavailable", + "This form is for reporting issues only. You must report either missing items or bad items.": "This form is for reporting issues only. You must report either missing items or bad items.", + "Bad item Qty": "Bad item Qty", + "Missing item Qty": "Missing item Qty", + "Missing Item Qty": "Missing Item Qty", + "Bad Item Qty": "Bad Item Qty", + "Bad Package Qty": "Bad Package Qty", + "Lot line is not available (status=UNAVAILABLE)": "Lot line is not available (status=UNAVAILABLE)", + "Actual Pick Qty": "Actual Pick Qty", + "Required Qty": "Required Qty", + "Issue Remark": "Issue Remark", + "Handler": "Handler", + "Qty is required": "Qty is required", + "Qty is not allowed to be greater than remaining available qty": "Qty is not allowed to be greater than remaining available qty", + "Qty is not allowed to be greater than required qty": "Qty is not allowed to be greater than required qty", + "At least one issue must be reported": "At least one issue must be reported", + "issueRemark": "issueRemark", + "handler": "handler", + "Max": "Max", + "Route": "Route", + "Index": "Index", + "No FG pick orders found": "No FG pick orders found", + "Finish Scan?": "Finish Scan?", + "Delivery Code": "Delivery Code", + "Shop PO Code": "Shop PO Code", + "Shop ID": "Shop ID", + "Truck No.": "Truck No.", + "Departure Time": "Departure Time", + "Shop Name": "Shop Name", + "Shop Address": "Shop Address", + "Delivery Date": "Delivery Date", + "Pick Execution 2/F": "Pick Execution 2/F", + "Pick Execution 4/F": "Pick Execution 4/F", + "Pick Execution Detail": "Pick Execution Detail", + "Submit Required Pick Qty": "Submit Required Pick Qty", + "Scan Result": "Scan Result", + "Ticket No.": "Ticket No.", + "Start QR Scan": "Start QR Scan", + "Stop QR Scan": "Stop QR Scan", + "Scanning...": "Scanning...", + "Print DN/Label": "Print DN/Label", + "Store ID": "Store ID", + "QR code does not match any item in current orders.": "QR code does not match any item in current orders.", + "Lot Number Mismatch": "Lot Number Mismatch", + "The scanned item matches the expected item, but the lot number is different. Do you want to proceed with this different lot?": "The scanned item matches the expected item, but the lot number is different. Do you want to proceed with this different lot?", + "The scanned item matches the expected item, but the lot number is different. Scan again to confirm: scan the expected lot QR to keep the suggested lot, or scan the other lot QR again to switch.": "The scanned item matches the expected item, but the lot number is different. Scan again to confirm: scan the expected lot QR to keep the suggested lot, or scan the other lot QR again to switch.", + "Expected Lot:": "Expected Lot:", + "Scanned Lot:": "Scanned Lot:", + "Confirm": "Confirm", + "Update your suggested lot to the this scanned lot": "Update your suggested lot to the this scanned lot", + "Print Draft": "Print Draft", + "Print Pick Order and DN Label": "Print Pick Order and DN Label", + "Print Pick Order": "Print Pick Order", + "Print DN Label": "Print DN Label", + "Print All Draft": "Print All Draft", + "If you confirm, the system will:": "If you confirm, the system will:", + "After you scan to choose, the system will update the pick line to the lot you confirmed.": "After you scan to choose, the system will update the pick line to the lot you confirmed.", + "Or use the Confirm button below if you cannot scan again (same as scanning the other lot again).": "Or use the Confirm button below if you cannot scan again (same as scanning the other lot again).", + "Lot switch failed": "Lot switch failed", + "The system could not switch to the scanned lot. Review the lots below, then tap Confirm to retry.": "The system could not switch to the scanned lot. Review the lots below, then tap Confirm to retry.", + "You can also scan again: expected lot QR keeps the suggested line; scanned lot QR retries the switch.": "You can also scan again: expected lot QR keeps the suggested line; scanned lot QR retries the switch.", + "QR code verified.": "QR code verified.", + "Order Finished": "Order Finished", + "Submitted Status": "Submitted Status", + "Pick Execution Record": "Pick Execution Record", + "Delivery No.": "Delivery No.", + "Total": "Total", + "Completed DO pick orders: ": "Completed DO pick orders: ", + "No completed DO pick orders found": "No completed DO pick orders found", + "Enter the number of cartons: ": "Enter the number of cartons: ", + "Number of cartons": "Number of cartons", + "Select an action for the assigned pick orders.": "Select an action for the assigned pick orders.", + "Detail": "Detail", + "consoCode": "consoCode", + "status": "status", + "Items Included": "Items Included", + "Pick Order Included": "Pick Order Included", + "No created items": "No created items", + "Please select item": "Please select item", + "enter a qty": "enter a qty", + "update qc info": "update qc info", + "All lots must be completed before printing": "All lots must be completed before printing", + "Assigning pick order...": "Assigning pick order...", + "Enter the number of cartons:": "Enter the number of cartons:", + "Finished Good Detail": "Finished Good Detail", + "Finished Good Record": "Finished Good Record", + "Finished Good Record (All)": "Finished Good Record (All)", + "All dates": "All dates", + "Search date": "Search date", + "Hide Completed: OFF": "Hide Completed: OFF", + "Hide Completed: ON": "Hide Completed: ON", + "Number must be at least 1": "Number must be at least 1", + "Printed Successfully.": "Printed Successfully.", + "Product": "Product", + "You need to enter a number": "You need to enter a number", + "Available in warehouse": "Available in warehouse", + "Describe the issue with bad items": "Describe the issue with bad items", + "Enter pick qty or issue qty": "Enter pick qty or issue qty", + "Invalid qty": "Invalid qty", + "Note:": "Note:", + "Qty is not allowed to be greater than required/available qty": "Qty is not allowed to be greater than required/available qty", + "Still need to pick": "Still need to pick", + "Total exceeds required qty": "Total exceeds required qty", + "submitting": "submitting", + "Back to List": "Back to List", + "Delivery No": "Delivery No", + "FG orders": "FG orders", + "View Details": "View Details", + "No Item": "No Item", + "None": "None", + "Add Selected Items to Created Items": "Add Selected Items to Created Items", + "All pick orders created successfully": "All pick orders created successfully", + "Failed to create group": "Failed to create group", + "Invalid date format": "Invalid date format", + "Item already exists in created items": "Item already exists in created items", + "Job Order not found or has no items": "Job Order not found or has no items", + "No results found": "No results found", + "Please enter at least code or name": "Please enter at least code or name", + "Please enter quantity for all selected items": "Please enter quantity for all selected items", + "Please select at least one item to submit": "Please select at least one item to submit", + "Please select group and enter quantity for all selected items": "Please select group and enter quantity for all selected items", + "Please select group for all selected items": "Please select group for all selected items", + "Please select product type": "Please select product type", + "Please select target date": "Please select target date", + "Please select type": "Please select type", + "Search Criteria": "Search Criteria", + "Processing...": "Processing...", + "Failed items must have failed quantity": "Failed items must have failed quantity", + "QC items without result": "QC items without result", + "confirm putaway": "confirm putaway", + "email supplier": "email supplier", + "qc processing": "qc processing", + "submitStockIn": "submitStockIn", + "not default warehosue": "not default warehosue", + "printQty": "printQty", + "Shop": "Shop", + "warehouse": "warehouse", + "Add Record": "Add Record", + "Clean Record": "Clean Record", + "Select": "Select", + "Close": "Close", + "Truck": "Truck", + "Date": "Date", + "Delivery Order Code": "Delivery Order Code", + "Escalation Info": "Escalation Info", + "Escalation Result": "Escalation Result", + "acceptQty must not greater than": "acceptQty must not greater than", + "supervisor": "supervisor", + "No Qc": "No Qc", + "receivedQty": "receivedQty", + "stock in information": "stock in information", + "No Uom": "No Uom", + "Input quantity cannot exceed": "Input quantity cannot exceed", + "Quantity cannot be negative": "Quantity cannot be negative", + "Enter bad item quantity (required if no missing items)": "Enter bad item quantity (required if no missing items)", + "Enter missing quantity (required if no bad items)": "Enter missing quantity (required if no bad items)", + "Submit All Scanned": "Submit All Scanned", + "Submitting...": "Submitting...", + "COMPLETED": "COMPLETED", + "Confirm print: (": "Confirm print: (", + "piece(s))": "piece(s))", + "Printing...": "Printing...", + "Please wait...": "Please wait...", + "No available pick order(s) for this floor.": "No available pick order(s) for this floor.", + "You already have a pick order in progess. Please complete it first before taking next pick order.": "You already have a pick order in progess. Please complete it first before taking next pick order.", + "Error occurred during assignment.": "Error occurred during assignment.", + "Info": "Info", + "Warning": "Warning", + "Error": "Error", + "Batch Print": "Batch Print", + "No entries available": "No entries available", + "Today": "Today", + "Tomorrow": "Tomorrow", + "packaging": "packaging", + "This lot is not available, please scan another lot.": "This lot is not available, please scan another lot.", + "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.": "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.", + "Lot is expired (expiry={{expiry}})": "Lot is expired (expiry={{expiry}})", + "Day After Tomorrow": "Day After Tomorrow", + "Select Date": "Select Date", + "Suggest Lot No.": "Suggest Lot No.", + "Search by Shop": "Search by Shop", + "Search by Truck": "Search by Truck", + "Print DN & Label": "Print DN & Label", + "Print Label": "Print Label", + "Reprint Label(s)": "Reprint Label(s)", + "Reprint DN Label": "Reprint DN Label", + "From carton": "From carton", + "To carton": "To carton", + "Total cartons on shipment": "Total cartons on shipment", + "From carton must be at least 1": "From carton must be at least 1", + "To carton must be greater than or equal to from carton": "To carton must be greater than or equal to from carton", + "Total cartons on shipment must be at least 1": "Total cartons on shipment must be at least 1", + "To carton cannot be greater than total cartons on shipment": "To carton cannot be greater than total cartons on shipment", + "Not Yet Finished Released Do Pick Orders": "Not Yet Finished Released Do Pick Orders", + "Not yet finished released do pick orders": "Not yet finished released do pick orders", + "Released orders not yet completed - click lane to select and assign": "Released orders not yet completed - click lane to select and assign", + "Ticket Release Table": "Ticket Release Table", + "Please take one pick order before printing the draft.": "Please take one pick order before printing the draft.", + "No released pick order records found.": "No released pick order records found.", + "EDT - Lane Code (Unassigned/Total)": "EDT - Lane Code (Unassigned/Total)", + "Floor ticket": "Floor ticket", + "2F ticket": "2F ticket", + "4F ticket": "4F ticket", + "4F lane panel legend": "4F lane panel legend", + "Loading sequence n": "Loading sequence n", + "lot QR code": "lot QR code", + "label Printer": "label Printer", + "Loading Sequence": "Loading Sequence", + "Ticket No": "Ticket No", + "The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated.": "The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated.", + "is unavable. Please check around have available QR code or not.": "is unavable. Please check around have available QR code or not.", + "Lot switch failed; pick line was not marked as checked.": "Lot switch failed; pick line was not marked as checked.", + "Lot confirmation failed. Please try again.": "Lot confirmation failed. Please try again.", + "Powder Mixture": "Powder Mixture", + "This lot is not yet putaway": "This lot is not yet putaway", + "Cannot resolve new inventory lot line": "Cannot resolve new inventory lot line", + "Pick order line item is null": "Pick order line item is null", + "New lot line item does not match pick order line item": "New lot line item does not match pick order line item", + "Pick order line {{id}} not found": "Pick order line {{id}} not found", + "SuggestedPickLot not found for pickOrderLineId {{polId}}": "SuggestedPickLot not found for pickOrderLineId {{polId}}", + "SuggestedPickLot qty is invalid: {{qty}}": "SuggestedPickLot qty is invalid: {{qty}}", + "Reject switch lot: available {{available}} less than required {{required}}": "Reject switch lot: available {{available}} less than required {{required}}", + "Reject switch lot: picked {{picked}} already greater or equal required {{required}}": "Reject switch lot: picked {{picked}} already greater or equal required {{required}}", + "Lot status is unavailable. Cannot switch or bind; pick line was not updated.": "Lot status is unavailable. Cannot switch or bind; pick line was not updated.", + "No lot rows. Select a line in the table above.": "No lot rows. Select a line in the table above.", + "No stock out line for this lot": "No stock out line for this lot", + "No data available for this pick order.": "No data available for this pick order.", + "Report missing or bad items": "Report missing or bad items", + "passed": "Passed", + "failed": "Failed", + "confirm_accept_with_fail": "There are failed QC items. Confirm to accept stock out?" +} diff --git a/src/i18n/en/po.json b/src/i18n/en/po.json new file mode 100644 index 0000000..abb99cf --- /dev/null +++ b/src/i18n/en/po.json @@ -0,0 +1,16 @@ +{ + "code": "code", + "status": "status", + "escalated": "escalated", + "notEscalated": "notEscalated", + "All": "All", + "Pending": "Pending", + "Receiving": "Receiving", + "Completed": "Completed", + "Purchase Order": "Purchase Order", + "Details": "Details", + "OrderDate": "OrderDate", + "Supplier": "Supplier", + "Escalated": "Escalated", + "NotEscalated": "NotEscalated" +} diff --git a/src/i18n/en/poWorkbench.json b/src/i18n/en/poWorkbench.json index d054a2f..5612607 100644 --- a/src/i18n/en/poWorkbench.json +++ b/src/i18n/en/poWorkbench.json @@ -1,4 +1,5 @@ { + "PO Workbench": "PO Workbench", "searchCriteria": { "poPlaceholder": "Scan PO QR code or enter PO number", "ariaPoSearch": "PO number search", diff --git a/src/i18n/en/printer.json b/src/i18n/en/printer.json new file mode 100644 index 0000000..0dd667f --- /dev/null +++ b/src/i18n/en/printer.json @@ -0,0 +1,8 @@ +{ + "Create Printer": "Create Printer", + "Edit": "Edit", + "PDF Preview": "PDF Preview", + "Print failed": "Print failed", + "Print job sent successfully": "Print job sent successfully", + "Printer": "Printer" +} diff --git a/src/i18n/en/production.json b/src/i18n/en/production.json new file mode 100644 index 0000000..1dc2b2b --- /dev/null +++ b/src/i18n/en/production.json @@ -0,0 +1,3 @@ +{ + "Production Process": "Production Process" +} diff --git a/src/i18n/en/productionProcess.json b/src/i18n/en/productionProcess.json new file mode 100644 index 0000000..3e68fe4 --- /dev/null +++ b/src/i18n/en/productionProcess.json @@ -0,0 +1,221 @@ +{ + "Action": "Action", + "All": "All", + "An error has occurred. Please try again later.": "An error has occurred. Please try again later.", + "Are you sure you want to delete this process?": "Are you sure you want to delete this process?", + "Assignment successful": "Assignment successful", + "Assume End Time": "Assume End Time", + "Assume Time Need": "Assume Time Need", + "Auto-refresh every 1 minute": "Auto-refresh every 1 minute", + "Auto-refresh every 10 minutes": "Auto-refresh every 10 minutes", + "Back": "Back", + "Back to List": "Back to List", + "Bag": "Bag", + "Bag Consumption": "Bag Consumption", + "Balance": "Balance", + "Base Qty": "Base Qty", + "Base UOM": "Base UOM", + "Batch Count": "Batch Count", + "BoM Material": "BoM Material", + "Bom Req. Qty": "Bom Req. Qty", + "Bom Uom": "Bom Uom", + "By-product": "By-product", + "Cancel": "Cancel", + "Cancel Job Order": "Cancel Job Order", + "Cancel job order confirm message": "Cancel job order confirm message", + "Changeover Time": "Changeover Time", + "Changeover Time (mins)": "Changeover Time (mins)", + "Code": "Code", + "Complete Step": "Complete Step", + "Completed": "Completed", + "Completed Step": "Completed Step", + "Confirm": "Confirm", + "Confirm cancel job order": "Confirm cancel job order", + "Confirm delete job order": "Confirm delete job order", + "Confirm to Pass this Process?": "Confirm to Pass this Process?", + "Confirm to update this Job Order?": "Confirm to update this Job Order?", + "Consumed Qty": "Consumed Qty", + "Continue": "Continue", + "Count of Job Orders": "Count of Job Orders", + "Date": "Date", + "Defect": "Defect", + "Delete job order confirm message": "Delete job order confirm message", + "Description": "Description", + "Duration hours": "{{count}} hour(s)", + "Duration minutes": "{{count}} minute(s)", + "Duration seconds": "{{count}} second(s)", + "End Time": "End Time", + "Enter any additional production notes...": "Enter any additional production notes...", + "Equipment": "Equipment", + "Equipment Code": "Equipment Code", + "Equipment Name and Code": "Equipment Name and Code", + "Equipment Type": "Equipment Type", + "EquipmentType-EquipmentName-Code": "EquipmentType-EquipmentName-Code", + "Estimated Completion Time": "Estimated Completion Time", + "Executing": "Executing", + "FG / WIP Item": "FG / WIP Item", + "Filtered": "Filtered", + "Finished Time": "Finished Time", + "Finished lines": "Finished lines", + "In Progress": "In Progress", + "In progress": "In progress", + "Invalid Job Order Id": "Invalid Job Order Id", + "Invalid Stock In Line Id": "Invalid Stock In Line Id", + "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity", + "Item": "Item", + "Item Code": "Item Code", + "Item Name": "Item Name", + "Job Details": "Job Details", + "Job Order": "Job Order", + "Job Order Code": "Job Order Code", + "Job Order Info": "Job Order Info", + "Job Order No.": "Job Order No.", + "Job Order and Product": "Job Order and Product", + "Job Order Production Process": "Job Order Production Process", + "Job Process Status Dashboard": "Job Process Status Dashboard", + "Job Type": "Job Type", + "Job process detail mode label": "Job process detail mode label", + "Job process detail: equipment": "Equipment", + "Job process detail: handler": "Handler", + "Job process detail: process name": "Process Name", + "Job process detail: time": "Time", + "Just Pass": "Just Pass", + "Last updated": "Last updated", + "Lines with insufficient stock: ": "Lines with insufficient stock: ", + "Lines with sufficient stock: ": "Lines with sufficient stock: ", + "Lot No": "Lot No", + "Matching Stock": "Matching Stock", + "Material Code": "Material Code", + "N/A": "N/A", + "Name": "Name", + "Next page": "Next page", + "No data available": "No data available", + "No data found": "No data found", + "No.": "No.", + "Now": "Now", + "Operator": "Operator", + "Operator KPI Dashboard": "Operator KPI Dashboard", + "Operator Name & No.": "Operator Name & No.", + "Order Complete": "Order Complete", + "Output from Process": "Output from Process", + "Output Quantity": "Output Quantity", + "Over Time": "Over Time", + "Overall Time Remaining": "Overall Time Remaining", + "Passed Step": "Passed Step", + "Pause": "Pause", + "Pause Reason": "Pause Reason", + "Paused": "Paused", + "Pending": "Pending", + "Plan start (from)": "Plan start (from)", + "Plan start (to)": "Plan start (to)", + "Please scan equipment code": "Please scan equipment code", + "Please scan operator code first": "Please scan operator code first", + "Please scan staff no": "Please scan staff no", + "Post Prod Time (Minutes)": "Post Prod Time (Minutes)", + "Prep Time (Minutes)": "Prep Time (Minutes)", + "Previous page": "Previous page", + "Printer": "Printer", + "Process": "Process", + "Process & Equipment": "Process & Equipment", + "Process Description": "Process Description", + "Process Name": "Process Name", + "Process Start Time": "Process Start Time", + "Process Type": "Process Type", + "Process page summary": "Process {{from}}–{{to}} / {{total}} total", + "Processing Time": "Processing Time", + "Processing Time (mins)": "Processing Time (mins)", + "Product process status": "Product process status", + "Production Date": "Production Date", + "Production Equipment Status Dashboard": "Production Equipment Status Dashboard", + "Production Output Data": "Production Output Data", + "Production Output Data Entry": "Production Output Data Entry", + "Production Priority": "Production Priority", + "Production Process": "Production Process", + "Production Process Line Remark": "Production Process Line Remark", + "Production Process Steps": "Production Process Steps", + "Production Time Remaining": "Production Time Remaining", + "Production date": "Production date", + "Put Awayed Job Orders": "Put Awayed Job Orders", + "Qty": "Qty", + "Quality Check": "Quality Check", + "Quantity": "Quantity", + "Reason": "Reason", + "Release": "Release", + "Remaining Time": "Remaining Time", + "Remaining Time (min)": "Remaining Time (min)", + "Remark": "Remark", + "Req. Qty": "Req. Qty", + "Required Qty": "Required Qty", + "Required Time": "Required Time", + "SEQ": "SEQ", + "Save": "Save", + "Scan Operator & Equipment": "Scan Operator & Equipment", + "Scrap": "Scrap", + "Scrap Qty": "Scrap Qty", + "Search date": "Search date", + "Searched Item": "Searched Item", + "Select Another Bag Lot": "Select Another Bag Lot", + "Select Bag": "Select Bag", + "Select Date": "Select Date", + "Select Printer": "Select Printer", + "Select Unit": "Select Unit", + "Seq": "Seq", + "Seq No": "Seq No", + "Setup Time": "Setup Time", + "Setup Time (mins)": "Setup Time (mins)", + "Staff No": "Staff No", + "Start": "Start", + "Start QR Scan": "Start QR Scan", + "Start Time": "Start Time", + "Status": "Status", + "Step": "Step", + "Step Information": "Step Information", + "Step Name": "Step Name", + "Step Start Time": "Step Start Time", + "Stock Available": "Stock Available", + "Stock Req. Qty": "Stock Req. Qty", + "Stock Status": "Stock Status", + "Stock UOM": "Stock UOM", + "Stop QR Scan": "Stop QR Scan", + "Submit & Start": "Submit & Start", + "Submit Bag Consumption": "Submit Bag Consumption", + "Submitting...": "Submitting...", + "Target Production Date": "Target Production Date", + "Time Information(mins)": "Time Information (mins)", + "Time Remaining": "Time Remaining", + "Timer Paused": "Timer Paused", + "Total Processing Time": "Total Processing Time", + "Total Time": "Total Time", + "Total finished QC job orders": "Total finished QC job orders", + "Total job orders": "Total job orders", + "Total lines: ": "Total lines: ", + "Type": "Type", + "Unable to get user ID": "Unable to get user ID", + "Unit": "Unit", + "Unknown": "Unknown", + "Update Job Order": "Update Job Order", + "Update Production Priority": "Update Production Priority", + "Update Target Production Date": "Update Target Production Date", + "Update Required Quantity": "Update Required Quantity", + "Update Time Information": "Update Time Information", + "View": "View", + "View Details": "View Details", + "Wait Time": "Wait Time", + "Waiting QC Put Away Job Orders": "Waiting QC Put Away Job Orders", + "all": "All", + "drink": "Drink", + "id": "ID", + "mins": "mins", + "minutes": "minutes", + "FG": "FG", + "No processes found for this job order": "No processes found for this job order", + "SFG": "SFG", + "WIP": "WIP", + "fg": "fg", + "other": "Other", + "processing": "Processing", + "productionProcess": "Production Process", + "sfg": "sfg", + "view stockin": "View Stock In", + "wip": "wip" +} diff --git a/src/i18n/en/project.json b/src/i18n/en/project.json index 45de18e..47f8b14 100644 --- a/src/i18n/en/project.json +++ b/src/i18n/en/project.json @@ -1,6 +1,16 @@ { - - "FG & Material Demand Forecast Detail": "FG & Material Demand Forecast Detail", - "Release": "Release", - "Actions": "Actions" -} \ No newline at end of file + "Actions": "Actions", + "Create Project": "Create Project", + "Details": "Details", + "FG & Material Demand Forecast Detail": "FG & Material Demand Forecast Detail", + "Product": "Product", + "Projects": "Projects", + "Project Code": "Project Code", + "Project Code and Name": "Project Code and Name", + "Release": "Release", + "View BoM": "View BoM", + "code": "code", + "Task": "Task", + "description": "description", + "details": "details" +} diff --git a/src/i18n/en/purchaseOrder.json b/src/i18n/en/purchaseOrder.json index 9e26dfe..96f848a 100644 --- a/src/i18n/en/purchaseOrder.json +++ b/src/i18n/en/purchaseOrder.json @@ -1 +1,185 @@ -{} \ No newline at end of file +{ + "Purchase Order": "Purchase Order", + "Purchase Receipt": "Purchase Receipt", + "Code": "Code", + "row per page": "Rows per page", + "Rows per page": "Rows per page", + "OrderDate": "Order Date", + "Order Date": "Order Date", + "Order Date To": "Order Date To", + "ETA": "ETA", + "ETA To": "ETA To", + "Details": "Details", + "Supplier": "Supplier", + "Status": "Receipt Status", + "escalateFrom": "Escalation Source", + "Escalated": "Escalated", + "NotEscalated": "Not Escalated", + "Do you want to start?": "Do you want to start?", + "Start": "Start", + "Start Success": "Start Success", + "Start Fail": "Start Fail", + "Start PO": "Start PO", + "Do you want to complete?": "Do you want to complete?", + "Cancel": "Cancel", + "Complete": "Complete", + "Complete Success": "Complete Success", + "Complete Fail": "Complete Fail", + "Complete PO": "Complete PO", + "General": "General", + "Bind Storage": "Bind Storage", + "PO No.": "PO No.", + "itemNo": "Item No.", + "itemName": "Item Name", + "Item Detail": "Item Detail", + "Item Code": "Item Code", + "Item Name": "Item Name", + "Item Qty": "Item Qty", + "Item": "Item", + "Item Accepted Qty": "Item Accepted Qty", + "Item Purchase UoM": "Item Purchase UoM", + "qty": "Order Qty", + "uom": "Purchase UoM", + "Stock UoM": "Stock UoM", + "Stock In Qty": "Stock In Qty", + "total weight": "Total Weight", + "weight unit": "Weight Unit", + "price": "Order Value", + "processedQty": "Put Away Qty", + "expiryDate": "Expiry Date", + "acceptedQty": "Accepted Qty (This Batch)", + "acceptedPutawayQty": "Put Away Qty (This Batch)", + "putawayQty": "Put Away Qty", + "Confirm submit": "Confirm Submit", + "This batch quantity exceeds order quantity. Do you still want to submit?": "This batch quantity exceeds order quantity. Do you still want to submit?", + "acceptQty": "Accept Qty", + "printQty": "Print Qty", + "qcResult": "QC Result", + "weight": "Weight", + "start": "Start", + "qc": "Quality Control", + "escalation": "Escalation", + "stock in": "Stock In", + "putaway": "Put Away", + "delete": "Delete", + "Accept quantity must be greater than 0": "Accept quantity must be greater than 0", + "QC items without result": "QC items without result", + "Failed items must have failed quantity": "Failed items must have failed quantity", + "qty cannot be greater than remaining qty": "Qty cannot be greater than remaining qty", + "acceptQty must not greater than": "Accept qty must not be greater than", + "Record pol": "Record PO", + "Add some entries!": "Add some entries!", + "draft": "Draft", + "pending": "Pending", + "determine1": "Escalation 1", + "determine2": "Escalation 2", + "determine3": "Escalation 3", + "receiving": "Receiving", + "received": "Awaiting Put Away", + "completed": "Put Away Completed", + "partially_completed": "Partially Put Away", + "rejected": "Rejected", + "escalated": "Escalated", + "status": "Status", + "acceptedQty must not greater than": "Accepted qty must not be greater than", + "minimal value is 1": "Minimum value is 1", + "value must be a number": "Value must be a number", + "qc Check": "QC Check", + "Please select QC": "Please select QC", + "failQty": "Fail Qty", + "select qc": "Select QC", + "enter a failQty": "Enter fail qty", + "qty too big": "Qty too big", + "sampleRate": "Sample Rate", + "sampleWeight": "Sample Weight", + "totalWeight": "Total Weight", + "Escalation": "Escalation", + "to be processed": "To Be Processed", + "supervisor": "Supervisor", + "Stock In Detail": "Stock In Detail", + "productLotNo": "Delivery Lot No.", + "receiptDate": "Receipt Date", + "acceptedWeight": "Accepted Weight", + "productionDate": "Production Date", + "reportQty": "Report Qty", + "Default Warehouse": "Suggested Warehouse", + "Select warehouse": "Select Warehouse", + "Putaway Detail": "Put Away Detail", + "Delivery Detail": "Delivery Detail", + "stockLotNo": "Stock Lot No.", + "Po Code": "PO No.", + "No Warehouse": "No Warehouse", + "Please scan warehouse qr code.": "Please scan warehouse QR code.", + "receivedQty": "Received Qty", + "dnQty": "Delivery Qty (This Batch)", + "Accept submit": "Accept Delivery", + "qc processing": "Delivery & QC Processing", + "putaway processing": "Delivery & Put Away Processing", + "view stockin": "View Stock In Detail", + "view putaway": "View Put Away Detail", + "putawayBtn": "Put Away", + "dnNo": "Delivery Note No.", + "dnDate": "Delivery Note Date", + "submitStockIn": "Update Delivery Info", + "QC Info": "QC Info", + "Escalation History": "Escalation History", + "Escalation Info": "Escalation Info", + "Escalation Result": "Escalation Result", + "update qc info": "Update QC Info", + "email supplier": "Email Supplier", + "confirm putaway": "Confirm & Complete Put Away", + "confirm qc result": "Confirm QC Result", + "warehouse": "Warehouse", + "qcItem": "QC Item", + "passed": "Passed", + "failed": "Failed", + "failedQty": "Failed Qty", + "remarks": "Remarks", + "Reject": "Reject", + "submit": "Submit", + "printQrCode": "Print QR Code", + "print": "Print", + "bind": "Bind", + "Search": "Search", + "Found": "Found", + "escalation processing": "Escalation Processing", + "Printer": "Printer", + "Printing": "Printing", + "rejectQty": "Reject Qty", + "QC decision is required": "QC decision is required", + "Select All": "Select All", + "View Selected": "View Selected", + "No Option": "No Option", + "receivedTotal": "Total Received", + "QC Record": "QC Record", + "value must be integer": "Value must be an integer", + "dn and qc info": "Delivery & QC Detail", + "Qc Decision": "QC Decision", + "Print Qty": "Print Qty", + "putawayDatetime": "Put Away Time", + "putawayUser": "Put Away User", + "joCode": "Job Order No.", + "salesUnit": "Sales Unit", + "download Qr Code": "Download QR Code", + "downloading": "Downloading", + "&": "&", + "Calculate Expiry Date": "Calculate Expiry Date", + "shelfLife": "Shelf Life", + "Fill in Expiry Date": "Fill in production date and shelf life to calculate expiry date", + "Expiry Date cannot be earlier than Production Date": "Expiry date cannot be earlier than production date", + "Production Date must be earlier than Expiry Date": "Production date must be earlier than expiry date", + "confirm expiry date": "Confirm Expiry Date", + "Invalid Date": "Invalid Date", + "Missing QC Template, please contact administrator": "Missing QC template, please contact administrator", + "submitting": "Submitting...", + "Submit": "Submit", + "Total must equal Required Qty. Missing": "Total must equal required qty. Missing: {{diff}}", + "Total must equal Required Qty. Exceeds by": "Total must equal required qty. Exceeds by: {{diff}}", + "Add Record": "Add Record", + "Clean Record": "Reset", + "Create Material": "Create Material", + "Will start binding procedure after scanning item qr code.": "Will start binding procedure after scanning item qr code.", + "Quantity": "Quantity", + "Enter quantity": "Enter quantity", + "Enter your remark": "Enter your remark" +} diff --git a/src/i18n/en/putAway.json b/src/i18n/en/putAway.json new file mode 100644 index 0000000..806ee3c --- /dev/null +++ b/src/i18n/en/putAway.json @@ -0,0 +1,26 @@ +{ + "Pending scan": "Pending scan", + "Please scan warehouse qr code": "Please scan warehouse qr code", + "PoCode/JoCode": "PoCode/JoCode", + "Put Away": "Put Away", + "Put Away Scan": "Put Away Scan", + "Rescan": "Rescan", + "Scanning": "Scanning", + "acceptedQty": "acceptedQty", + "confirm putaway": "confirm putaway", + "itemCode": "itemCode", + "itemName": "itemName", + "lotNo": "lotNo", + "minimal value is 1": "minimal value is 1", + "poCode": "poCode", + "putAwayHistory": "putAwayHistory", + "putQty": "putQty", + "putQty must not greater than": "putQty must not greater than", + "putawayQty": "putawayQty", + "scan loading": "scan loading", + "scan warehouse": "scan warehouse", + "uom": "uom", + "value must be a number": "value must be a number", + "value must be integer": "value must be integer", + "warehouse": "warehouse" +} diff --git a/src/i18n/en/qcCategory.json b/src/i18n/en/qcCategory.json new file mode 100644 index 0000000..b25c2c6 --- /dev/null +++ b/src/i18n/en/qcCategory.json @@ -0,0 +1,21 @@ +{ + "Cancel": "Cancel", + "Code": "Code", + "Create Qc Category": "Create Qc Category", + "Delete": "Delete", + "Description": "Description", + "Details": "Details", + "Edit Qc Category": "Edit Qc Category", + "Edit Qc Item": "Edit Qc Item", + "Name": "Name", + "Qc Category": "Qc Category", + "Qc Category Created At": "Qc Category Created At", + "Qc Category Description": "Qc Category Description", + "Qc Category List": "Qc Category List", + "Qc Category Name": "Qc Category Name", + "Qc Category Status": "Qc Category Status", + "Qc Category Updated At": "Qc Category Updated At", + "Qc Item": "Qc Item", + "Qc Item Details": "Qc Item Details", + "Submit": "Submit" +} diff --git a/src/i18n/en/qcItem.json b/src/i18n/en/qcItem.json new file mode 100644 index 0000000..c85af2d --- /dev/null +++ b/src/i18n/en/qcItem.json @@ -0,0 +1,13 @@ +{ + "Cancel": "Cancel", + "Code": "Code", + "Create Qc Item": "Create Qc Item", + "Delete": "Delete", + "Description": "Description", + "Details": "Details", + "Edit Qc Item": "Edit Qc Item", + "Name": "Name", + "Qc Item": "Qc Item", + "Qc Item Details": "Qc Item Details", + "Submit": "Submit" +} diff --git a/src/i18n/en/qcItemAll.json b/src/i18n/en/qcItemAll.json index f587f48..3cc2e26 100644 --- a/src/i18n/en/qcItemAll.json +++ b/src/i18n/en/qcItemAll.json @@ -1,58 +1,66 @@ { - "Qc Item All": "QC Management", - "Item and Qc Category Mapping": "Item and Qc Category Mapping", - "Qc Category and Qc Item Mapping": "Qc Category and Qc Item Mapping", - "Qc Category Management": "Qc Category Management", - "Qc Item Management": "Qc Item Management", - "Qc Category": "Qc Category", - "Qc Item": "Qc Item", - "Item": "Item", - "Code": "Code", - "Name": "Name", - "Description": "Description", - "Type": "Type", - "Order": "Order", - "Item Count": "Item Count", - "Qc Item Count": "Qc Item Count", - "Qc Category Count": "Qc Category Count", "Actions": "Actions", - "View": "View", - "Edit": "Edit", - "Delete": "Delete", "Add": "Add", - "Add Mapping": "Add Mapping", "Add Association": "Add Association", - "Save": "Save", + "Add Mapping": "Add Mapping", + "Are you sure you want to delete this item?": "Are you sure you want to delete this item?", + "Association Details": "Association Details", "Cancel": "Cancel", - "Submit": "Submit", - "Details": "Details", - "Create Qc Category": "Create Qc Category", - "Edit Qc Category": "Edit Qc Category", - "Create Qc Item": "Create Qc Item", - "Edit Qc Item": "Edit Qc Item", - "Delete Success": "Delete Success", - "Delete Error": "Delete Error", - "Submit Success": "Submit Success", - "Submit Error": "Submit Error", "Cannot Delete": "Cannot Delete", "Cannot delete QcCategory. It has {itemCount} item(s) and {qcItemCount} qc item(s) linked to it.": "Cannot delete QcCategory. It has {itemCount} item(s) and {qcItemCount} qc item(s) linked to it.", "Cannot delete QcItem. It is linked to one or more QcCategories.": "Cannot delete QcItem. It is linked to one or more QcCategories.", - "Select Item": "Select Item", - "Select Qc Category": "Select Qc Category", - "Select Qc Item": "Select Qc Item", - "Select Type": "Select Type", + "Category": "Category", + "Category Type": "Category Type", + "Code": "Code", + "Confirm": "Confirm", + "Confirm Delete": "Confirm Delete", + "Create Qc Category": "Create Qc Category", + "Create Qc Item": "Create Qc Item", + "Delete": "Delete", + "Delete Error": "Delete Error", + "Delete Success": "Delete Success", + "Description": "Description", + "Details": "Details", + "Do you want to delete?": "Do you want to delete?", + "Do you want to submit?": "Do you want to submit?", + "Edit": "Edit", + "Edit Qc Category": "Edit Qc Category", + "Edit Qc Item": "Edit Qc Item", + "Enter item code to validate": "Enter item code to validate", + "Error validating item code": "Error validating item code", + "Item": "Item", "Item Code": "Item Code", + "Item Count": "Item Count", "Item Name": "Item Name", + "Item already has type \"{{type}}\" in QcCategory \"{{category}}\". One item can only have each type in one QcCategory.": "Item already has type \"{{type}}\" in QcCategory \"{{category}}\". One item can only have each type in one QcCategory.", + "Item and Qc Category Mapping": "Item and Qc Category Mapping", + "Item code not found": "Item code not found", + "Mapping Details": "Mapping Details", + "Name": "Name", + "No associations found": "No associations found", + "No data available": "No data available", + "No mappings found": "No mappings found", + "Order": "Order", + "Qc Category": "Qc Category", "Qc Category Code": "Qc Category Code", + "Qc Category Count": "Qc Category Count", + "Qc Category Management": "Qc Category Management", "Qc Category Name": "Qc Category Name", + "Qc Category and Qc Item Mapping": "Qc Category and Qc Item Mapping", + "Qc Item": "Qc Item", + "Qc Item All": "QC Management", "Qc Item Code": "Qc Item Code", + "Qc Item Count": "Qc Item Count", + "Qc Item Management": "Qc Item Management", "Qc Item Name": "Qc Item Name", - "Mapping Details": "Mapping Details", - "Association Details": "Association Details", - "No mappings found": "No mappings found", - "No associations found": "No associations found", - "No data available": "No data available", - "Confirm Delete": "Confirm Delete", - "Are you sure you want to delete this item?": "Are you sure you want to delete this item?" + "Save": "Save", + "Select Item": "Select Item", + "Select Qc Category": "Select Qc Category", + "Select Qc Item": "Select Qc Item", + "Select Type": "Select Type", + "Submit": "Submit", + "Submit Error": "Submit Error", + "Submit Success": "Submit Success", + "Type": "Type", + "View": "View" } - diff --git a/src/i18n/en/qrCodeHandle.json b/src/i18n/en/qrCodeHandle.json new file mode 100644 index 0000000..b5c8133 --- /dev/null +++ b/src/i18n/en/qrCodeHandle.json @@ -0,0 +1,5 @@ +{ + "PDF Preview": "PDF Preview", + "QR Code Handle": "QR Code Handle", + "Equipment": "Equipment" +} diff --git a/src/i18n/en/report.json b/src/i18n/en/report.json new file mode 100644 index 0000000..c13c611 --- /dev/null +++ b/src/i18n/en/report.json @@ -0,0 +1,13 @@ +{ + "Report": "Report", + "title": "Report Management", + "selectReport": "Select Report", + "reportList": "Report List", + "selectReportHelper": "Select a report", + "searchCriteria": "Search Criteria", + "downloadPdf": "Download Report (PDF)", + "downloadExcel": "Download Report (Excel)", + "generatingPdf": "Generating PDF...", + "generatingExcel": "Generating Excel...", + "generatingReport": "Generating report..." +} diff --git a/src/i18n/en/schedule.json b/src/i18n/en/schedule.json index 75a38bf..6d1ae64 100644 --- a/src/i18n/en/schedule.json +++ b/src/i18n/en/schedule.json @@ -1,10 +1,121 @@ { - "Total Demand Qty": "Total Demand Qty", - "Demand Qty (Day1)": "Demand Qty (Day1)", - "Demand Qty (Day2)": "Demand Qty (Day2)", - "Demand Qty (Day3)": "Demand Qty (Day3)", - "Demand Qty (Day4)": "Demand Qty (Day4)", - "Demand Qty (Day5)": "Demand Qty (Day5)", - "Demand Qty (Day6)": "Demand Qty (Day6)", - "Demand Qty (Day7)": "Demand Qty (Day7)" -} \ No newline at end of file + " (Selected)": " (Selected)", + "Actions": "Actions", + "Add": "Add", + "Available Qty": "Available Qty", + "Avg Qty Last Month": "Avg Qty Last Month", + "Back": "Back", + "Batch Need": "Batch Need", + "CODE": "CODE", + "Cancel": "Cancel", + "Close": "Close", + "Code": "Code", + "Confirm": "Confirm", + "Date": "Date", + "Days Left": "Days Left", + "Delete": "Delete", + "Demand Forecast": "Demand Forecast", + "Demand Forecast Detail": "Demand Forecast Detail", + "Demand Forecast Period": "Demand Forecast Period", + "Demand Qty": "Demand Qty", + "Demand Qty (7 Days)": "Demand Qty (7 Days)", + "Demand Qty (Day1)": "Demand Qty (Day1)", + "Demand Qty (Day2)": "Demand Qty (Day2)", + "Demand Qty (Day3)": "Demand Qty (Day3)", + "Demand Qty (Day4)": "Demand Qty (Day4)", + "Demand Qty (Day5)": "Demand Qty (Day5)", + "Demand Qty (Day6)": "Demand Qty (Day6)", + "Demand Qty (Day7)": "Demand Qty (Day7)", + "Detail Scheduling": "Detail Scheduling", + "Detailed Schedule": "Detailed Schedule", + "Detailed": "Detailed", + "Details": "Details", + "Edit": "Edit", + "Estimated Production Time": "Estimated Production Time", + "Export Schedule": "Export Schedule", + "FG & Material Demand Forecast": "FG & Material Demand Forecast", + "FG & Material Demand Forecast Detail": "FG & Material Demand Forecast Detail", + "FG Demand Date": "FG Demand Date", + "FG Demand List (7 Days)": "FG Demand List (7 Days)", + "FG Demand Qty": "FG Demand Qty", + "FG Production Schedule": "FG Production Schedule", + "Fri": "Fri", + "Job Date": "Job Date", + "Job Name": "Job Name", + "Job No.": "Job No.", + "Job Priority": "Job Priority", + "Job Qty": "Job Qty", + "Job Status": "Job Status", + "Job Type": "Job Type", + "Last Month Average Sales": "Last Month Average Sales", + "Last Month Average Stock": "Last Month Average Stock", + "Material Demand Date": "Material Demand Date", + "Material Demand List": "Material Demand List", + "Material Demand List (7 Days)": "Material Demand List (7 Days)", + "Mon": "Mon", + "Name": "Name", + "Output Qty": "Output Qty", + "Overall": "Overall", + "Product Count": "Product Count", + "Product Count(s)": "Product Count(s)", + "Production Date": "Production Date", + "Production Priority": "Production Priority", + "Release": "Release", + "Reset": "Reset", + "Safety Stock": "Safety Stock", + "Sales Qty": "Sales Qty", + "Sales UOM": "Sales UOM", + "Sat": "Sat", + "Save": "Save", + "Schedule": "Schedule", + "Schedule At": "Schedule At", + "Schedule Detail": "Schedule Detail", + "Schedule Period": "Schedule Period", + "Schedule Period To": "Schedule Period To", + "Scheduled At": "Scheduled At", + "Search": "Search", + "Selected": "Selected", + "Status": "Status", + "Stock Qty": "Stock Qty", + "Sun": "Sun", + "Test Detailed Schedule": "Test Detailed Schedule", + "Test Rough Schedule": "Test Rough Schedule", + "Thu": "Thu", + "Total Demand Qty": "Total Demand Qty", + "Total Estimated Demand Qty": "Total Estimated Demand Qty", + "Total FG Item": "Total FG Item", + "Total Job Order": "Total Job Order", + "Total Job Orders": "Total Job Orders", + "Total Production Qty": "Total Production Qty", + "Tue": "Tue", + "Type": "Type", + "Unselected": "Unselected", + "UoM": "UoM", + "View BoM": "View BoM", + "View By FG": "View By FG", + "View By Material": "View By Material", + "Wed": "Wed", + "code": "code", + "consumables": "consumables", + "detailed": "detailed", + "fg": "fg", + "item": "item", + "Item Name": "Item Name", + "Priority": "Priority", + "Mat Code": "Mat Code", + "Mat Name": "Mat Name", + "Required Qty": "Required Qty", + "Total Qty Need": "Total Qty Need", + "Purchased Qty": "Purchased Qty", + "On Hand Qty": "On Hand Qty", + "Unavailable Qty": "Unavailable Qty", + "Related Item Code": "Related Item Code", + "Related Item Name": "Related Item Name", + "Material Summary": "Material Summary", + "mat": "mat", + "name": "name", + "non-consumables": "non-consumables", + "sfg": "sfg", + "type": "type", + "Generate Job Order": "Generate Job Order" +} diff --git a/src/i18n/en/scheduling.json b/src/i18n/en/scheduling.json new file mode 100644 index 0000000..3531f16 --- /dev/null +++ b/src/i18n/en/scheduling.json @@ -0,0 +1,5 @@ +{ + "title": "Scheduling", + "Scheduling": "Scheduling", + "ps": "Scheduling" +} diff --git a/src/i18n/en/settings.json b/src/i18n/en/settings.json new file mode 100644 index 0000000..427d1c8 --- /dev/null +++ b/src/i18n/en/settings.json @@ -0,0 +1,14 @@ +{ + "settings": "settings", + "Demand Forecast Setting": "Demand Forecast Setting", + "Import Master Data": "Import Master Data", + "Import Po": "Import Po", + "QC Check Template": "QC Check Template", + "QC Check Template Created At": "QC Check Template Created At", + "QC Check Template Description": "QC Check Template Description", + "QC Check Template Details": "QC Check Template Details", + "QC Check Template Name": "QC Check Template Name", + "QC Check Template Status": "QC Check Template Status", + "QC Check Template Updated At": "QC Check Template Updated At", + "Ready to import": "Ready to import" +} diff --git a/src/i18n/en/shop.json b/src/i18n/en/shop.json new file mode 100644 index 0000000..6b864aa --- /dev/null +++ b/src/i18n/en/shop.json @@ -0,0 +1,471 @@ +{ + "Actions": "Actions", + "Add New Truck Lane": "Add New Truck Lane", + "Add Shop": "Add Shop", + "Add Shop to Truck Lane": "Add Shop to Truck Lane", + "Add Truck Lane": "Add Truck Lane", + "Addr1": "Addr1", + "Addr2": "Addr2", + "Addr3": "Addr3", + "All": "All", + "All shops updated successfully": "All shops updated successfully", + "Back": "Back", + "Back to Truck Lane List": "Back to Truck Lane List", + "Cancel": "Cancel", + "Cancel editing": "Cancel editing", + "Changed": "Changed", + "Code": "Code", + "Complete": "Complete", + "Contact Email": "Contact Email", + "Contact Name": "Contact Name", + "Contact No": "Contact No", + "Current version": "Current version", + "Delete truck lane": "Delete truck lane", + "Departure": "Departure", + "Departure Time": "Departure Time", + "Departure time updated for all shops on this truck lane": "Departure time updated for all shops on this truck lane", + "District Reference": "District Reference", + "Driver": "Driver", + "Edit departure time": "Edit departure time", + "Edit shop details": "Edit shop details", + "Failed to create shop in truck lane": "Failed to create shop in truck lane", + "Failed to create truck": "Failed to create truck", + "Failed to delete truck lane": "Failed to delete truck lane", + "Failed to load shop details": "Failed to load shop details", + "Failed to load shops": "Failed to load shops", + "Failed to load truck lane detail": "Failed to load truck lane detail", + "Failed to load truck lanes": "Failed to load truck lanes", + "Failed to save": "Failed to save", + "Failed to save truck data": "Failed to save truck data", + "Failed to save truck shop details": "Failed to save truck shop details", + "Filter by Status": "Filter by Status", + "Invalid Shop ID": "Invalid Shop ID", + "Invalid departure time": "Invalid departure time", + "Invalid shop data": "Invalid shop data", + "Loading Sequence": "Loading Sequence", + "Logistic": "Logistic", + "Mass Edit": "Mass Edit", + "Missing Data": "Missing Data", + "Name": "Name", + "No Truck Lane data available": "No Truck Lane data available", + "No Truck data available": "No Truck data available", + "No TruckLance": "No TruckLance", + "No changes": "No changes", + "No shops found using this truck lane": "No shops found using this truck lane", + "No truck lane data available": "No truck lane data available", + "No trucks found for this truck lane": "No trucks found for this truck lane", + "Not editable for this Store ID": "Not editable for this Store ID", + "Plate": "Plate", + "Please fill in the following required fields:": "Please fill in the following required fields:", + "Please log in to view shop details": "Please log in to view shop details", + "Remark": "Remark", + "Save": "Save", + "Save All": "Save All", + "Save changes": "Save changes", + "Saved": "Saved", + "Search or select remark": "Search or select remark", + "Search or select shop name": "Search or select shop name", + "Shop": "Shop", + "Shop Branch": "Shop Branch", + "Shop Code": "Shop Code", + "Shop ID is required": "Shop ID is required", + "Shop Information": "Shop Information", + "Shop Name": "Shop Name", + "Shop added to truck lane successfully": "Shop added to truck lane successfully", + "Shop not found": "Shop not found", + "ShopAndTruck": "Lane & shop management", + "Shops": "Shops", + "Shops Using This Truck Lane": "Shops Using This Truck Lane", + "Store ID": "Store ID", + "Submitting...": "Submitting…", + "Truck Information": "Truck Information", + "Truck Lane": "Truck Lane", + "Truck Lane Detail": "Truck Lane Detail", + "Truck lane code already exists. Please use a different code.": "Truck lane code already exists. Please use a different code.", + "Truck lane deleted successfully": "Truck lane deleted successfully", + "Truck lane information is required": "Truck lane information is required", + "Truck lane not found": "Truck lane not found", + "Truck shop details updated successfully": "Truck shop details updated successfully", + "TruckLance Code": "TruckLance Code", + "TruckLance Code is required": "TruckLance Code is required", + "TruckLance Status": "TruckLance Status", + "View Detail": "View Detail", + "addRoute_confirm": "Confirm add lane", + "addRoute_dialogTitle": "Add delivery lane", + "addRoute_hint": "After confirm, the lane is staged on the board; press \"Save changes\" in the header to create it on the server (no dummy shop rows).", + "addRoute_submitting": "Adding…", + "addShop_confirm": "Confirm", + "addShop_dialogTitle": "Add shop to lane", + "addShop_listHint": "Shop codes already on this lane are hidden from the list. After adding, reorder by drag; like other edits, press \"Save changes\" to persist to truck rows.", + "api_fail_addShop": "Failed to add shop", + "api_fail_createLane": "Failed to create lane", + "api_fail_deleteShop": "Failed to delete shop", + "api_fail_updateLane": "Failed to update lane", + "api_fail_updateLogistics": "Failed to update logistics", + "aria_deleteLogistics": "Delete logistics company", + "aria_editDistrict": "Edit district", + "aria_editLogistics": "Edit logistics company", + "aria_editSeq": "Edit load sequence", + "aria_openLaneBoard": "Open lane on route board", + "aria_pickLane": "Pick lane", + "aria_removeEmptyDistrict": "Remove empty district block", + "aria_searchLanes": "Search lanes", + "btn_addDistrict": "Add district", + "btn_addLane": "Add lane", + "btn_addLogistics": "Add logistics", + "btn_addShopToLane": "Add shop", + "btn_apply": "Apply", + "btn_cancelBack": "Cancel and go back", + "btn_loading": "Loading…", + "btn_openVersionLog": "Version log", + "btn_refresh": "Refresh", + "btn_scheduleChange": "Schedule changes", + "btn_scheduleHistory": "Schedule change log", + "cancel": "Cancel", + "code": "code", + "confirm_addShopConflict": "Detected {{count}} potential conflict(s) with other lanes (Rules 1/2; see bell). It will be added to the board first; press \"Save changes\" to persist. Continue?", + "confirm_clearLane": "Clear all {{count}} shop(s) from lane \"{{laneLabel}}\"? (Press \"Save changes\" to delete on server)", + "confirm_deleteLogistic": "Delete logistics company \"{{name}}\"? Press \"Save changes\" to persist.", + "confirm_departureConflict": "After changing departure time, {{count}} potential conflict(s) detected (Rules 1/2; see bell). Apply anyway?", + "confirm_discardDraftShop": "Discard unsaved \"new shop\" draft?", + "confirm_importDiscardEdits": "Import will replace unsaved board edits. Continue?", + "confirm_removeShop": "Remove this shop from the lane? (Press \"Save changes\" to persist)", + "confirm_restoreDiscardsEdits": "Scheduling a version restore will discard other unsaved board changes (drags, deletes, pending shops/lanes, logistics fields, etc.). Continue?", + "confirm_restoreSaveWillDropStaging": "Save will apply the snapshot restore first; other staged edits in this save will be skipped. Continue?", + "confirm_schedule_removeShop": "Remove this shop from the lane? It will be scheduled for deletion when you submit.", + "departureDialog_hint": "Applies to all shop rows on this lane; press \"Save changes\" above to persist.", + "departureDialog_title": "Edit departure time", + "departureEditAria": "Edit departure time", + "departureTooltipEditSave": "Edit departure time (saved with \"Save changes\")", + "departureTooltipNeedShops": "Add shops before setting departure time", + "dialog_addLogisticsTitle": "Add logistics", + "dialog_close": "Close", + "dialog_editLogisticsTitle": "Edit logistics master", + "diffField_logisticsCompany": "Logistics company", + "diffLogistic_unassigned": "Unassigned", + "diff_addedToLane": "Added to lane {{lane}}", + "diff_clickLeft": "Select a version on the left to view changes", + "diff_editedCaption": "Field edits (sequence / branch name / time window, etc.)", + "diff_export_blockedError": "Cannot export while the board has unsaved changes (Excel is persisted snapshots only).", + "diff_export_blockedTooltip": "Export compares two persisted snapshots only. Save or discard board changes first, then export.", + "diff_export_reportBtn": "Export version lane report", + "diff_loadFail": "Failed to load version diff", + "diff_loadingEllipsis": "…", + "diff_logisticMaster_added": "Added", + "diff_logisticMaster_edited": "Edited", + "diff_logisticMaster_section": "Logistics company changes", + "diff_markedCount": "{{count}} change(s)", + "diff_moveFrom": "From {{lane}}", + "diff_moveTo": "Move to {{lane}}", + "diff_noDiffFromPrev": "No differences vs previous version", + "diff_noOlderCompare": "No older version to compare (pick a newer version)", + "diff_noShopDiffHasBoardStaged": "No shop-row changes vs the previous snapshot. Below are unsaved board edits (including new logistics company records).", + "diff_oldestSnapshot": "Oldest snapshot—no older version to diff against.", + "diff_onLane": "Lane {{lane}}", + "diff_removedFromLane": "Removed from {{lane}}", + "diff_restoreAlreadyPending": "This version is already scheduled; press \"Save changes\" to apply.", + "diff_restoreFail": "Restore failed", + "diff_restoreScheduled": "Restore to version #{{versionId}} is scheduled; press \"Save changes\" to persist.", + "diff_restoreToHead": "Schedule restore to latest snapshot (requires Save)", + "diff_restoreToSelected": "Schedule restore to this version (requires Save)", + "diff_shopList_title": "Shop change list", + "diff_staged_boardPendingLine": "{{count}} unsaved / scheduled board item(s) — see the list below.", + "diff_staged_deleteLogisticMaster": "Delete logistics company (not saved): {{name}}", + "diff_staged_deleteUnknown": "Delete truck id={{id}} (unsaved; save or cancel to refresh details)", + "diff_staged_editLogisticMaster": "Edit logistics company (unsaved): {{fromName}} ({{fromPlate}}) → {{name}} ({{plate}})", + "diff_staged_emptyDistricts": "Empty-district blocks (unsaved): {{lane}} — {{names}}", + "diff_staged_importPending": "Import Excel (unsaved): {{file}} — {{sheets}} sheet(s), {{rows}} row(s) (persisted on \"Save changes\")", + "diff_staged_laneLogistic": "Lane logistics (unsaved): {{lane}} → {{company}}", + "diff_staged_newLane": "New lane (unsaved): {{lane}}", + "diff_staged_pendingLogisticMaster": "New logistics company (not saved yet): {{name}} (plate {{plate}}); will be created on \"Save changes\" together with route edits", + "diff_staged_restoreScheduled": "Restore to version #{{versionId}} is scheduled (calls restore only after \"Save changes\").", + "diff_staged_section_subtitle": "These match what will hit the DB after \"Save changes\"; listed separately from the version diff above (Excel is server snapshots only).", + "diff_staged_section_title": "Board: unsaved / scheduled (not persisted yet)", + "diff_staged_shopDistrictHint": " · District: {{from}}→{{to}}", + "diff_staged_shopDistrictOnly": "{{name}} ({{code}}) — lane {{lane}}: district {{from}}→{{to}} (unsaved; persisted on \"Save changes\")", + "diff_staged_shopPendingOnLane": "{{name}} ({{code}}) — lane {{lane}}: unsaved edits (drag / departure / load order; persisted on \"Save changes\"){{districtPart}}", + "diff_staged_tag_scheduled": "Scheduled", + "diff_staged_tag_unsaved": "Unsaved", + "diff_summary_added": "Added", + "diff_summary_deleted": "Deleted", + "diff_summary_fieldChange": "Field changes", + "diff_summary_moved": "Moved", + "diff_summary_title": "Summary", + "district_dialog_add": "Add district", + "district_dialog_edit": "Edit district", + "district_err_exists": "This district already exists", + "district_err_name": "Enter a district name", + "district_err_reserved": "\"Unclassified\" is built-in; do not add it again", + "district_help_mapped": "Display name is written via toDistrictRawValue to each shop's districtReference; API runs on \"Save changes\"", + "district_help_null": "Unclassified maps to districtReference = null on server", + "district_name_label": "District display name", + "district_name_ph": "Blank means \"Unclassified\"", + "drag_blockDraftShop": "Unsaved \"new shop\" rows must be saved with \"Save changes\" or removed from the card before dragging.", + "drawerClose": "Close", + "emDash": "—", + "empty_lane_noShops": "No assigned shops", + "err_dragDuplicateShop": "Target lane already has this shop (same shop / same shop code)", + "err_export": "Export failed", + "err_exportNeedSelection": "Select at least one lane on the left to export", + "err_import": "Import failed", + "err_importEmpty": "No valid lane rows found in the import file", + "err_invalidMasterId": "Invalid master record id", + "err_loadLanes": "Failed to load lanes", + "err_logisticDeleteHasLanes": "This company still has {{count}} lane(s). Reassign or remove them first.", + "err_noLanes": "No lane data", + "err_save": "Save failed", + "exportRoutes": "Export routes", + "filter_apply": "OK", + "filter_clear": "Clear", + "floor_all": "All", + "floor_label": "Floor", + "id": "id", + "importRoutes": "Import routes", + "import_staged_preview": "Import preview loaded: {{file}} ({{sheets}} sheet(s) / {{rows}} rows). Press \"Save changes\" to persist.", + "lane_companyChip": "{{count}} lane(s)", + "lane_searchPh": "Search…", + "lane_selectAll": "Select all", + "lane_selectTitle": "Lanes", + "lane_selectedCount": "{{count}} selected", + "lane_selectedNone": "No lanes selected", + "lane_shopCountInline": "{{count}} shop(s)", + "logistic_btn_apply": "Apply", + "logistic_btn_save": "Save", + "logistic_btn_saveDb": "Save to database", + "logistic_companyName": "Company name", + "logistic_driver": "Driver name", + "logistic_needMasterTpl": "\"{{name}}\" has no logistics master id—create it with \"Add logistics\" first.", + "logistic_phone": "Phone", + "logistic_plate": "Plate", + "logistics_colLaneCount": "{{count}} lane(s)", + "logistics_colShopCount": "{{count}} shop(s)", + "logistics_dirtyColumnBadge": "Unsaved logistics changes", + "logistics_dirtyLaneBadge": "Unsaved logistics on lane", + "logistics_overviewTitle": "Logistics overview", + "logistics_sidebarEmpty": "No lanes (refresh or relax filters)", + "mtmsRouteWarn_conflict4f": "4F: same shop on different lanes · weekday {{weekday}}", + "mtmsRouteWarn_conflictDep": "Non-4F: same shop on different lanes · departure {{time}}", + "mtmsRouteWarn_copyAll": "Copy all", + "mtmsRouteWarn_empty": "No data conflicts.", + "mtmsRouteWarn_parseHint": "{{count}} 4F lane(s): weekday could not be determined (excluded from alerts)", + "mtmsRouteWarn_postAddConflict": "After adding the shop, data conflicts with other lane(s). Open the bell for details.", + "mtmsRouteWarn_refresh": "Reload data", + "mtmsRouteWarn_refreshing": "Loading…", + "mtmsRouteWarn_shop": "Shop", + "mtmsRouteWarn_title": "Route data alerts", + "mtmsRouteWarn_tooltipHas": "{{count}} potential conflict(s)", + "mtmsRouteWarn_tooltipNone": "No alerts", + "nav_unsavedLeave": "You have unsaved changes. Leave this page?", + "new arrangement": "new arrangement", + "pageTitle": "MTMS route & shop board", + "quickIndex": "Quick index", + "quickPick_noKeyword": "No lanes match the keyword", + "quickPick_noLanes": "No lanes (relax floor filter or refresh)", + "restore_applied": "Snapshot restore applied; board reloaded.", + "restore_appliedDroppedStaging": "Snapshot restore applied; other staged edits in this save were skipped (edit again if needed).", + "routeReport": "Route report", + "route_err_code": "Enter a lane code", + "route_err_create": "Failed to add lane", + "route_err_departure": "Select or enter departure time", + "route_err_duplicate": "This lane (including remark group) already exists", + "route_logisticUnspecified": "(Unassigned — assign later in Logistics)", + "route_new_code_label": "Lane code", + "route_new_logistic_label": "Logistics company", + "route_new_remark_label": "Lane remark (4F)", + "route_new_store_label": "Floor", + "route_new_time_label": "Departure time", + "saveChanges": "Save changes", + "saveDisabledTooltip": "Make changes (drag, departure time, load order, logistics, etc.) before saving", + "save_clearedEmptyDistricts": "Only empty district blocks (no shops); cleared staging", + "schedule_action_create": "Add shop", + "schedule_action_delete": "Delete", + "schedule_action_ensure_lane": "New lane", + "schedule_action_move": "Move", + "schedule_applied_snackbar": "{{count}} scheduled change(s) applied on the board — press Save changes to persist.", + "schedule_bulk_seq": "Uniform seq", + "schedule_bulk_target": "Bulk target:", + "schedule_col_current_lane": "Current lane", + "schedule_col_select": "Select", + "schedule_col_shop": "Shop", + "schedule_col_target_district": "District", + "schedule_col_target_lane": "Target lane", + "schedule_col_target_seq": "Seq", + "schedule_confirm_btn": "Confirm scheduled changes", + "schedule_district_original": "Keep shop's original district", + "schedule_drag_seq": "Seq: {{seq}}", + "schedule_drop_hint": "Drag shop cards into this lane", + "schedule_err_conflict": "Some shops could not move (duplicate on target lane or draft row).", + "schedule_err_duplicate_shop": "Shop {{shop}} already exists on the target lane.", + "schedule_err_execute_at_past": "Scheduled run time is in the past. Choose a future date and time.", + "schedule_err_generic": "Schedule request failed. Please try again.", + "schedule_err_no_moves": "No shop moves to schedule.", + "schedule_err_open_pending": "Shop row #{{id}} already has a pending schedule.", + "schedule_err_target_lane_empty": "Target lane {{lane}} has no shops — add a shop to the lane first.", + "schedule_err_target_lane_missing": "Target lane {{lane}} was not found on the board.", + "schedule_exec_date": "Execution date", + "schedule_exec_time": "Execution time", + "schedule_history_applied_at": "Completed at {{at}}", + "schedule_history_apply_now": "Run now", + "schedule_history_archived": "Executed", + "schedule_history_cancel": "Cancel schedule", + "schedule_history_close": "Close", + "schedule_history_created": "Created {{at}} ({{by}})", + "schedule_history_empty": "No tasks match this filter", + "schedule_history_failed_lines": "{{count}} shop move(s) failed", + "schedule_history_filter_all": "All tasks", + "schedule_history_filter_failed": "Failed", + "schedule_history_filter_pending": "Pending", + "schedule_history_filter_success": "Succeeded", + "schedule_history_ignore": "Ignore", + "schedule_history_line_counts": "{{total}} line(s) · {{applied}} applied · {{failed}} failed · {{pending}} pending", + "schedule_history_line_placement": "Target district {{district}} · seq {{seq}}", + "schedule_history_needs_attention": "Needs attention", + "schedule_history_no_lines": "No line details", + "schedule_history_reschedule": "Reschedule", + "schedule_history_status_failed": "Failed", + "schedule_history_status_ignored": "Ignored", + "schedule_history_status_pending": "Scheduled", + "schedule_history_status_success": "Succeeded", + "schedule_history_subtitle": "Monitor, run, or troubleshoot scheduled shop-lane changes", + "schedule_history_success_lines": "{{count}} shop move(s) succeeded", + "schedule_history_title": "Scheduled task status & history", + "schedule_history_unknown_user": "System", + "schedule_history_view_lines": "View shop lines", + "schedule_import_col_action": "Action", + "schedule_import_col_lane": "Target lane", + "schedule_import_col_name": "Shop name", + "schedule_import_col_seq": "Seq", + "schedule_import_col_shop": "Shop code", + "schedule_import_confirm_btn": "Confirm import schedules", + "schedule_import_err_invalid_departure": "Shop {{shop}} has invalid departure time", + "schedule_import_err_no_target_lane": "Shop {{shop}} is missing a target lane", + "schedule_import_err_no_truck_row": "Shop {{shop}} has no board row — import or add on the board first", + "schedule_import_err_placeholder_row": "Shop {{shop}} is on a placeholder row", + "schedule_import_err_truck_not_found": "Shop {{shop}} board row not found", + "schedule_import_hint": "Use the same route Excel as board \"Import routes\"", + "schedule_import_need_datetime": "Set execution date and time first", + "schedule_import_no_valid_moves": "No schedulable changes in file (no moves, creates, deletes, or new lanes detected)", + "schedule_import_parse_summary": "Parsed: {{valid}} valid, {{errors}} error(s)", + "schedule_import_pick": "Choose file", + "schedule_import_plan_summary": "Loaded {{file}}: {{sheets}} sheet(s) / {{rows}} row(s) · move {{moves}} · add {{creates}} · delete {{deletes}} · new lane {{ensureLanes}} · {{errors}} skipped", + "schedule_import_preview_summary": "Loaded {{file}}: {{sheets}} sheet(s) / {{rows}} row(s) · {{valid}} scheduled · {{errors}} skipped", + "schedule_import_row_error": "{{shop}} ({{name}}): {{reason}}", + "schedule_import_skipped_title": "{{count}} row(s) skipped", + "schedule_import_title": "Import lane layout (same as board)", + "schedule_lane_left": "Left lane", + "schedule_lane_right": "Right lane", + "schedule_line_add_to": "Add to {{lane}}", + "schedule_line_departure_change": "Departure {{from}} → {{to}}", + "schedule_line_departure_target": "Departure {{time}}", + "schedule_line_district_change": "District {{from}} → {{to}}", + "schedule_line_district_target": "District {{district}}", + "schedule_line_ensure_lane": "Create lane {{lane}}", + "schedule_line_lane_change": "{{from}} → {{to}}", + "schedule_line_on_lane": "Lane {{lane}}", + "schedule_line_remove_from": "Remove from {{lane}}", + "schedule_line_seq": "Seq {{value}}", + "schedule_log_failed_hint": "{{count}} scheduled task(s) failed — view log", + "schedule_modal_subtitle": "Set when schedule changes should take effect", + "schedule_modal_title": "Schedule changes", + "schedule_moved_badge": "Moved in", + "schedule_no_shops": "No shops match your search", + "schedule_pick_lane": "Select lane...", + "schedule_planned_label": "Planned run", + "schedule_registered_snackbar": "{{count}} scheduled change(s) registered; the server will apply them at the planned time.", + "schedule_reschedule_time_adjusted": "Run time was in the past and was adjusted to {{at}}.", + "schedule_retry_rejects_partial": "PARTIAL schedules cannot be retried. Restore the board and create a new schedule.", + "schedule_review_count": "{{count}} change(s)", + "schedule_review_delete_action": "Remove from:", + "schedule_review_district_change": "District:", + "schedule_review_empty": "No pending changes", + "schedule_review_empty_hint": "Drag shops to adjust lane or loading sequence, or use the delete button to remove shops", + "schedule_review_lane_change": "Lane:", + "schedule_review_queue": "Change preview queue", + "schedule_review_revert": "Revert", + "schedule_review_seq": "Sequence:", + "schedule_search_ph": "Search shop name / code...", + "schedule_seq_dialog_hint": "The change is added to the preview queue and applied when you confirm the schedule.", + "schedule_seq_edit_btn": "Edit load sequence", + "schedule_seq_hint": "Default: max sequence in target lane for same district + 1", + "schedule_shop_badge": "Scheduled change", + "schedule_shop_locked": "Schedule is applying; this shop cannot be edited manually", + "schedule_step_method": "2. Open lanes to schedule and review planned changes", + "schedule_step_time": "1. Set execution time", + "schedule_summary_changes": "{{count}} change(s) (lane moves and sequence updates) will be scheduled", + "schedule_summary_counts": "{{selected}} shop(s) selected, {{ready}} with a target lane.", + "schedule_summary_pending": "Target lanes still incomplete", + "schedule_summary_ready": "All settings are ready", + "schedule_tab_import": "Import route Excel", + "schedule_tab_manual": "Drag scheduling", + "schedule_target_unset": "Not set", + "seqDialog_hint": "Press \"Save changes\" to persist to truck rows.", + "seqDialog_title": "Edit load sequence", + "seq_edit_departureLabel": "Departure time", + "seq_edit_seqLabel": "Load sequence (Seq)", + "shop_autocomplete_label": "Select shop", + "shop_autocomplete_loading": "Shop master not loaded", + "shop_autocomplete_noOptions": "All shops already on this lane or no options", + "shop_autocomplete_ph": "Filter by name or code", + "shop_searchPh": "Search shop name / code / district…", + "tabBoard": "Route board", + "tabLogistics": "Logistics", + "tools_title": "Tools", + "tooltip_clearLaneShops": "Clear all shops on this lane (press \"Save changes\" to persist)", + "tooltip_deleteLogistics": "Delete logistics company (persists after Save changes)", + "tooltip_editDistrict": "Edit district name (press \"Save changes\" to persist)", + "tooltip_editLogisticsDb": "Edit logistics company (persists after Save changes)", + "tooltip_editSeq": "Edit load sequence (press \"Save changes\" to persist)", + "tooltip_openLaneBoard": "Open this lane on the route board", + "tooltip_pickLane": "Pick lane (add to selection and scroll into view)", + "tooltip_removeEmptyDistrict": "Remove this staged empty block (deletable before save)", + "tooltip_removeFromLane": "Remove from this lane", + "val_logisticsDuplicateName": "A logistics master or staged add with this name already exists", + "val_logisticsRequired": "Enter logistics company, plate, and driver name", + "val_phoneInvalid": "Enter a valid phone number (digits)", + "versionLogDialogTitle": "Version change log", + "versionNote_saveFail": "Failed to save note", + "version_date_label": "Date", + "version_empty_filtered": "No versions match filters", + "version_empty_list": "No versions yet (use \"Save version log\")", + "version_note_placeholder": "Note (saved on blur)", + "version_note_saving": "Saving…", + "version_search_label": "Search", + "version_search_placeholder": "Version id / note / editor", + "version_ui_editedBy": "By: {{name}}", + "version_ui_filterAria": "Filter version list", + "version_ui_historyTitle": "Version history", + "version_ui_id": "Version #{{id}}", + "version_ui_listAria": "Version history list", + "version_ui_none": "No snapshot yet", + "version_ui_snapshotBadge": "Current snapshot", + "warnClipboardDep": "Dep", + "warnClipboardStore": "Store", + "warnClipboardWeekday": "Weekday", + "warnCollapse": "Collapse", + "warnExpand": "Expand", + "Applies to all shops using this truck lane": "Applies to all shops using this truck lane", + "Edit loading sequence": "Edit loading sequence", + "Edit shop truck lane": "Edit shop truck lane", + "Edit truck lane": "Edit truck lane", + "Invalid truck data": "Invalid truck data", + "Loading sequence updated successfully": "Loading sequence updated successfully", + "Failed to load shop detail": "Failed to load shop detail", + "Failed to save loading sequence": "Failed to save loading sequence", + "Route Board": "Route Board", + "Select a shop first": "Select a shop first", + "Select items without order to append to bottom": "Select items without order to append to bottom", + "Shop Detail": "Shop Detail", + "Truck ID is required": "Truck ID is required", + "Are you sure you want to delete this truck lane?": "Are you sure you want to delete this truck lane?", + "Search or select branch": "Search or select branch", + "Search or select shop code": "Search or select shop code", + "versionLogField_departureTime": "Departure time", + "versionLogField_loadingSequence": "Load sequence", + "versionLogField_branchName": "Branch name", + "versionLogField_districtReference": "District", + "versionLogField_shopCode": "Shop code", + "versionLogField_storeId": "Floor / Store", + "versionLogField_remark": "Remark", + "versionLogField_truckLanceCode": "Lane code", + "versionLogField_logisticId": "Logistics" +} diff --git a/src/i18n/en/stockIssue.json b/src/i18n/en/stockIssue.json new file mode 100644 index 0000000..5e6583f --- /dev/null +++ b/src/i18n/en/stockIssue.json @@ -0,0 +1,78 @@ +{ + "Action": "Action", + "Available Qty": "Available Qty", + "Bad Item": "Bad Item", + "Bad Item Handle": "Bad Item Handle", + "Bad Item Qty": "Bad Item Qty", + "Bad Item Records": "Bad Item Records", + "Batch Disposed All": "Batch Disposed All", + "Book Qty": "Book Qty", + "Cancel": "Cancel", + "Code": "Code", + "Defective Qty": "Defective Qty", + "Disposed": "Disposed", + "Disposing...": "Disposing...", + "DO Order Code": "DO Order Code", + "End Date": "End Date", + "Expiry Date": "Expiry Date", + "Expiry End Date": "Expiry End Date", + "Expiry Item": "Expiry Item", + "Expiry Item Handle": "Expiry Item Handle", + "Expiry Item Qty": "Expiry Item Qty", + "Expiry Item Records": "Expiry Item Records", + "Expiry Start Date": "Expiry Start Date", + "Failed to load expiry items": "Failed to load expiry items", + "Failed to submit": "Failed to submit", + "Failed to submit expiry item": "Failed to submit expiry item", + "Handled Date": "Handled Date", + "Handler": "Handler", + "Issue Qty": "Issue Qty", + "Item": "Item", + "Item Code": "Item Code", + "Item not found": "Item not found", + "Item selected": "Item selected", + "JO Order Code": "JO Order Code", + "Loading": "Loading", + "Location": "Location", + "Looked": "Looked", + "Lot No": "Lot No", + "Lot No.": "Lot No.", + "Miss Item": "Miss Item", + "Miss Qty": "Miss Qty", + "Name": "Name", + "No changes to submit": "No changes to submit", + "No items are selected yet.": "No items are selected yet.", + "No record found": "No record found", + "Picker Name": "Picker Name", + "Pick Order Code": "Pick Order Code", + "Please enter a valid quantity": "Please enter a valid quantity", + "Please set at least one search criterion": "Please set at least one search criterion", + "Processing...": "Processing...", + "Quantity exceeds available quantity": "Quantity exceeds available quantity", + "Remain available Quantity": "Remain available Quantity", + "Remaining Qty": "Remaining Qty", + "Remark": "Remark", + "Remarks": "Remarks", + "Reset": "Reset", + "Rows per page": "Rows per page", + "Saved successfully": "Saved successfully", + "Search": "Search", + "Search Criteria": "Search Criteria", + "Search to load lot lines": "Search to load lot lines", + "Start Date": "Start Date", + "Status": "Status", + "Stock UoM": "Stock UoM", + "Submit": "Submit", + "Submit Bad Item": "Submit Bad Item", + "Submit Miss Item": "Submit Miss Item", + "Submit Quantity": "Submit Quantity", + "Submitting...": "Submitting...", + "Type": "Type", + "Unknown error": "Unknown error", + "UoM": "UoM", + "User ID is required": "User ID is required", + "Warehouse": "Warehouse", + "available": "Available", + "unavailable": "Unavailable", + "Stock Issue": "Stock Issue" +} diff --git a/src/i18n/en/stockRecord.json b/src/i18n/en/stockRecord.json new file mode 100644 index 0000000..5b0ca0d --- /dev/null +++ b/src/i18n/en/stockRecord.json @@ -0,0 +1,32 @@ +{ + "title": "Stock Record", + "tke": "tke", + "adj": "adj", + "nor": "nor", + "trf": "trf", + "open": "open", + "miss": "miss", + "bad": "bad", + "Start Date": "Start Date", + "End Date": "End Date", + "Date": "Date", + "Item-lotNo": "Item-lotNo", + "UOM": "UOM", + "In Qty": "In Qty", + "Out Qty": "Out Qty", + "Balance Qty": "Balance Qty", + "Loading...": "Loading...", + "Type": "Type", + "Status": "Status", + "pending": "Pending", + "completed": "Completed", + "partially_completed": "Partially completed", + "receiving": "Receiving", + "rejected": "Rejected", + "checked": "Checked", + "determine1": "Determine 1", + "lot-change": "Lot change approval", + "qc1": "QC 1", + "qc2": "QC 2", + "qc3": "QC 3" +} diff --git a/src/i18n/en/stockTake.json b/src/i18n/en/stockTake.json new file mode 100644 index 0000000..faaef94 --- /dev/null +++ b/src/i18n/en/stockTake.json @@ -0,0 +1,179 @@ +{ + "Action": "Action", + "Actual Qty": "Actual Qty", + "Adjusted By": "Adjusted By", + "Adjustment": "Adjustment", + "Adjustment No": "Adjustment No", + "Adjustment No, Item, Lot...": "Adjustment No, Item, Lot...", + "After Qty": "After Qty", + "All": "All", + "Approved": "Approved", + "Approver": "Approver", + "Approver Approved": "Approver Approved", + "Approver Input": "Approver Input", + "Approver Pending": "Approver Pending", + "Approver Time": "Approver Time", + "Approver input empty; save skipped, row remains pending": "Approver input empty; save skipped, row remains pending", + "Approver stock take record saved successfully": "Approver stock take record saved successfully", + "Area": "Area", + "Available QTY cannot be negative": "Available QTY cannot be negative", + "Back to List": "Back to List", + "Bad Qty": "Bad Qty", + "Batch Save All": "Batch Save All", + "Batch approver save completed: {{success}} success, {{errors}} errors": "Batch approver save completed: {{success}} success, {{errors}} errors", + "Batch save completed: {{success}} success, {{errors}} errors": "Batch save completed: {{success}} success, {{errors}} errors", + "Batch save confirm message": "Batch save {{count}} stock take record(s) in the current list. Continue?", + "Before Qty": "Before Qty", + "Book Qty": "Book Qty", + "Cancel": "Cancel", + "Category": "Category", + "Clear selection all floors": "Clear selection (all floors)", + "Close": "Close", + "Confirm": "Confirm", + "Confirm batch save approver": "Confirm batch save", + "Confirm create stock take": "Confirm create stock take", + "Control Time": "Control Time", + "Create Stock Take (Select Sections)": "Create stock take (select sections)", + "Created": "Created", + "Creation date": "Creation date", + "Damage": "Damage", + "Date": "Date", + "Deselect all on this floor": "Deselect all on this floor ({{floor}})", + "Detail": "Detail", + "Difference": "Difference", + "Errors": "Errors", + "Failed to batch save approver stock take records": "Failed to batch save approver stock take records", + "Failed to batch save stock take records": "Failed to batch save stock take records", + "Failed to save approver stock take record": "Failed to save approver stock take record", + "Failed to save stock take record": "Failed to save stock take record", + "Failed to update stock take record status": "Failed to update stock take record status", + "First": "First", + "First QTY is not available": "First count quantity is not available", + "Floor area selection header": "{{floor}} area selection ({{count}} areas)", + "Floor unassigned": "Unassigned floor", + "Handle Remark": "Handle Remark", + "Invalid QTY": "Invalid quantity", + "Inventory Adjustments": "Inventory Adjustments", + "Inventory Difference": "Inventory Difference", + "Issue Detail": "Issue Detail", + "Issue No": "Issue No", + "Issue Remark": "Issue Remark", + "Item": "Item", + "Item Code": "Item Code", + "Item-lotNo-ExpiryDate": "Item-lotNo-ExpiryDate", + "Last Stock Take Date": "Last Stock Take Date", + "Location": "Location", + "Lot No": "Lot No", + "Manual": "Manual", + "Mark as Resolved": "Mark as Resolved", + "Miss Qty": "Miss Qty", + "No adjustments found": "No adjustments found", + "No data": "No data", + "No rows loaded; set search criteria and search first": "No rows loaded; set search criteria and search first", + "No sections match search": "No areas match your search", + "No stock take sections from warehouse": "No stock take sections returned from warehouse.", + "No valid input to submit": "No valid input to submit", + "No valid records to batch save": "No valid records to batch save", + "Not Match": "Not Match", + "Not filled": "(not filled)", + "Only Variance": "Only Variance", + "Pass": "Pass", + "Pending": "Pending", + "Pick Execution Issues": "Pick Execution Issues", + "Pick Order": "Pick Order", + "Pick Order Code": "Pick Order Code", + "Picker": "Picker", + "Plan Start Date": "Plan Start Date", + "Please enter Approver Bad QTY": "Please enter approver bad quantity", + "Please enter Approver QTY": "Please enter approver count quantity", + "Please enter QTY": "Please enter quantity", + "Please enter Second QTY": "Please enter second count quantity", + "ReStockTake": "ReStockTake", + "Reason": "Reason", + "Record Status": "Record Status", + "Rejected": "Rejected", + "Remark": "Remark", + "Required Qty": "Required Qty", + "Reset": "Reset", + "Return": "Return", + "Rows per page": "Rows per page", + "Save": "Save", + "Search": "Search", + "Search Criteria": "Search Criteria", + "Search section code or name": "Search code or name (e.g. ST-042 or drinks)", + "Second": "Second", + "Second QTY is not available": "Second count quantity is not available", + "Section": "Section", + "Select all on this floor": "Select all on this floor ({{floor}})", + "Select all sections all floors": "Select all areas (all floors)", + "Selected Qty": "Selected Qty", + "Shortcut Input": "Shortcut input", + "Shown": "Shown", + "Skipped": "Skipped", + "Start Stock Take Date": "Start Stock Take Date", + "Status": "Status", + "Stock Take": "Stock Take", + "Stock Take Management": "Stock Take Management", + "Stock Take Qty": "Stock Take Qty", + "Stock Take Qty Data and Variance Analysis": "Stock Take Qty Data and Variance Analysis", + "Stock Take Qty(include Bad Qty)= Available Qty": "Stock Take Qty(include Bad Qty)= Available Qty", + "Stock Take Round": "Stock Take Round", + "Stock Take Section": "Stock Take Section", + "Stock Take Section (can use , to search multiple sections)": "Stock Take Section (can use , to search multiple sections)", + "Stock Taker": "Stock Taker", + "Stock take qty exceeds maximum": "Stock take quantity cannot exceed 999,999,999,999", + "Stock take record ID is required": "Stock take record ID is required", + "Stock take record saved successfully": "Stock take record saved successfully", + "Stock take record status updated to not match": "Stock take record status updated to not match", + "Stock take round name": "Stock take round name", + "Stock take round name placeholder": "Optional, e.g. May full-site stock take", + "Stock take sections in current list": "{{count}} stock take section(s) in current list", + "Store ID": "Store ID", + "Submit All Inputted": "Submit All Inputted", + "Total": "Total", + "Total Adjustments": "Total Adjustments", + "Total Item Kind Number": "Total Item Kind Number", + "Total Items": "Total Items", + "Total Lots": "Total Lots", + "Total Sections": "Total Sections", + "Total selected sections label": "Total selected:", + "Type": "Type", + "UOM": "UOM", + "Variance": "Variance", + "Variance %": "Variance %", + "Variance filter exclusive range hint": "Show rows with -{{value}}% {{op}} variance % {{op}} {{value}}% (outside range, server-filtered)", + "Variance filter inclusive only": "Show only rows within variance range", + "Variance filter inclusive range hint": "Show rows with -{{value}}% {{op}} variance % {{op}} {{value}}% (within range)", + "View Details": "View Details", + "View ReStockTake": "View ReStockTake", + "Warehouse": "Warehouse", + "Warehouse Location": "Warehouse Location", + "Warehouse missing stock take section drawer hint": "These locations have no stock take section (ST-xxx) and cannot be included in stock take. Fix in warehouse settings.", + "Warehouse missing stock take section empty": "No warehouses missing stock take section", + "Warehouse missing stock take section go settings": "Go to warehouse settings", + "Warehouse missing stock take section showing": "Showing {{shown}} of {{count}}", + "Warehouse missing stock take section tooltip has": "{{count}} warehouse location(s) missing stock take section — click to view", + "Warehouse missing stock take section tooltip none": "All warehouses have a stock take section", + "Warehouse missing stock take section warn title": "Warehouses without stock take section", + "approving": "Approving", + "book qty": "book qty", + "completed": "Completed", + "do": "Delivery order", + "end time": "end time", + "jo": "Job order", + "lot_issue": "Lot issue", + "material": "Material", + "notMatch": "Re-count required", + "notmatch": "Re-count required", + "pass": "Counted", + "pending": "Pending", + "quality_issue": "Quality issue", + "quantity_mismatch": "Quantity mismatch", + "resolved": "Resolved", + "sections unit": "area(s)", + "selected stock take qty": "selected stock take qty", + "start time": "start time", + "stockTaking": "Stock taking", + "stock_take": "Stock take", + "variance Percentage": "variance Percentage" +} diff --git a/src/i18n/en/ticketReleaseTable.json b/src/i18n/en/ticketReleaseTable.json new file mode 100644 index 0000000..a4935c1 --- /dev/null +++ b/src/i18n/en/ticketReleaseTable.json @@ -0,0 +1,48 @@ +{ + "Actions": "Actions", + "All Floors": "All Floors", + "All Statuses": "All Statuses", + "Auto-refresh every 1 minute": "Auto-refresh every 1 minute", + "Auto-refresh every 10 minutes": "Auto-refresh every 10 minutes", + "Auto-refresh every 15 minutes": "Auto-refresh every 15 minutes", + "Auto-refresh every 5 minutes": "Auto-refresh every 5 minutes", + "Cancel": "Cancel", + "Completed Time": "Completed Time", + "Confirm": "Confirm", + "Confirm force complete": "Confirm force complete", + "Confirm revert assignment": "Confirm revert assignment", + "Day After Tomorrow": "Day After Tomorrow", + "Departure Time": "Departure Time", + "Floor": "Floor", + "Force complete DO": "Force complete DO", + "Force complete hint": "Force complete hint", + "Handler Name": "Handler Name", + "Last updated": "Last updated", + "Loading Sequence": "Loading Sequence", + "Manager only hint": "Manager only hint", + "No data available": "No data available", + "Now": "Now", + "Number of FG Items (Order Item(s) Count)": "Number of FG Items (Order Item(s) Count)", + "Operation succeeded": "Operation succeeded", + "Released Time": "Released Time", + "Reload data": "Reload data", + "Required Delivery Date": "Required Delivery Date", + "Revert assignment": "Revert assignment", + "Revert assignment hint": "Revert assignment hint", + "Rows per page": "Rows per page", + "Select Date": "Select Date", + "Shop Name": "Shop Name", + "Status": "Status", + "Store ID": "Store ID", + "Target Date": "Target Date", + "Ticket Information": "Ticket Information", + "Ticket No.": "Ticket No.", + "Ticket Release Table": "Ticket Release Table", + "Today": "Today", + "Tomorrow": "Tomorrow", + "Truck Information": "Truck Information", + "Truck Lane Code": "Truck Lane Code", + "completed": "completed", + "pending": "pending", + "released": "released" +} diff --git a/src/i18n/en/translation.json b/src/i18n/en/translation.json new file mode 100644 index 0000000..59f2a71 --- /dev/null +++ b/src/i18n/en/translation.json @@ -0,0 +1,4 @@ +{ + "Actions": "Actions", + "Release": "Release" +} diff --git a/src/i18n/en/user.json b/src/i18n/en/user.json index 8afc58a..f9cf3b3 100644 --- a/src/i18n/en/user.json +++ b/src/i18n/en/user.json @@ -1,26 +1,48 @@ { - "Create User": "新增用戶", - "User Detail": "用戶詳細資料", - "User Authority": "用戶權限", - "Authority Pool": "權限池", - "Allocated Authority": "已分配權限", - "username": "用戶名稱", - "password": "密碼", - "Confirm Password": "確認密碼", - "Reset": "重置", - "Cancel": "取消", - "Confirm": "確認", - "name": "姓名", - "User ID": "用戶ID", - "User Name": "用戶名稱", - "User Group": "用戶群組", - "Authority": "權限", - "Delete Success": "Delete Success", - "Do you want to delete?": "Do you want to delete?", - "Maintain User": "Maintain User", - "Maintain group": "Maintain group", - "view user": "view user", - "view group": "view group", - "Approval": "Approval", - "Testing": "Testing" -} \ No newline at end of file + "profile": "profile", + "Add": "Add", + "Allocated Authority": "Allocated Authority", + "Approval": "Approval", + "Authority": "Authority", + "Authority Pool": "Authority Pool", + "Cancel": "Cancel", + "Confirm": "Confirm", + "Confirm Password": "Confirm Password", + "Create User": "Create User", + "Delete": "Delete", + "Delete Success": "Delete Success", + "Do you want to delete?": "Do you want to delete?", + "Edit": "Edit", + "Edit User": "Edit User", + "Failed to fetch staff list": "Failed to fetch staff list", + "Maintain User": "Maintain User", + "Maintain group": "Maintain group", + "Remove": "Remove", + "Reset": "Reset", + "Rows per page": "Rows per page", + "Search by Authority or description or position.": "Search by Authority or description or position.", + "Testing": "Testing", + "User": "User", + "User Authority": "User Authority", + "User Detail": "User Detail", + "User Group": "User Group", + "User ID": "User ID", + "User Name": "User Name", + "Username": "Username", + "authority": "authority", + "description": "description", + "name": "name", + "password": "password", + "qrcode": "qrcode", + "staffNo": "staffNo", + "user": "user", + "username": "username", + "view group": "view group", + "view user": "view user", + "An error has occurred. Please try again later.": "An error has occurred. Please try again later.", + "Please input correct password": "Please input correct password", + "Failed to search by name": "Failed to search by name", + "Failed to search by username": "Failed to search by username", + "Staff No is required": "Staff No is required", + "User Not Found": "User Not Found" +} diff --git a/src/i18n/en/warehouse.json b/src/i18n/en/warehouse.json index 45bb138..1aef422 100644 --- a/src/i18n/en/warehouse.json +++ b/src/i18n/en/warehouse.json @@ -1,27 +1,44 @@ { - "Create Warehouse": "Create Warehouse", - "Edit Warehouse": "Edit Warehouse", - "Warehouse Detail": "Warehouse Detail", - "code": "Code", - "name": "Name", - "description": "Description", - "Edit": "Edit", - "Delete": "Delete", - "Delete Success": "Delete Success", - "Warehouse": "Warehouse", - "warehouse": "warehouse", - "Rows per page": "Rows per page", - "capacity": "Capacity", - "store_id": "Store ID", - "area": "Area", - "slot": "Slot", - "order": "Order", - "stockTakeSection": "Stock Take Section", - "Do you want to delete?": "Do you want to delete?", - "Cancel": "Cancel", - "Reset": "Reset", - "Confirm": "Confirm", - "is required": "is required", - "Search Criteria": "Search Criteria", - "Search": "Search" + "Actions": "Actions", + "Add": "Add", + "Add Success": "Add Success", + "Add Warehouse": "Add Warehouse", + "Cancel": "Cancel", + "Confirm": "Confirm", + "Create Warehouse": "Create Warehouse", + "Delete": "Delete", + "Delete Success": "Delete Success", + "Do you want to delete?": "Do you want to delete?", + "Edit": "Edit", + "Edit Warehouse": "Edit Warehouse", + "Mapping Details": "Mapping Details", + "No warehouses": "No warehouses", + "Remove": "Remove", + "Reset": "Reset", + "Rows per page": "Rows per page", + "Save": "Save", + "Saved": "Saved", + "Saved Successfully": "Saved Successfully", + "Search": "Search", + "Search Criteria": "Search Criteria", + "Stock Take Section": "Stock Take Section", + "Stock Take Section & Warehouse Mapping": "Stock Take Section & Warehouse Mapping", + "Stock Take Section Description": "Stock Take Section Description", + "Store ID": "Store ID", + "Warehouse": "Warehouse", + "Warehouse Detail": "Warehouse Detail", + "Warehouse List": "Warehouse List", + "Warehouses in this section": "Warehouses in this section", + "area": "Area", + "capacity": "Capacity", + "code": "Code", + "description": "Description", + "is required": "is required", + "name": "Name", + "order": "Order", + "slot": "Slot", + "stockTakeSection": "Stock Take Section", + "stockTakeSectionDescription": "stockTakeSectionDescription", + "store_id": "Store ID", + "warehouse": "warehouse" } diff --git a/src/i18n/zh/bagPrint.json b/src/i18n/zh/bagPrint.json new file mode 100644 index 0000000..b3e7187 --- /dev/null +++ b/src/i18n/zh/bagPrint.json @@ -0,0 +1,3 @@ +{ + "title": "打袋機" +} diff --git a/src/i18n/zh/bagUsage.json b/src/i18n/zh/bagUsage.json new file mode 100644 index 0000000..23439cd --- /dev/null +++ b/src/i18n/zh/bagUsage.json @@ -0,0 +1,12 @@ +{ + "Bag": "包裝袋", + "Bag Usage": "包裝袋使用記錄", + "Balance": "可用數量", + "Actions": "操作", + "Lot No": "批號", + "Consumed Qty": "消耗數量", + "Scrap Qty": "損耗數量", + "Job Order Code": "工單編號", + "Date": "日期", + "Back": "返回" +} diff --git a/src/i18n/zh/bomWeighting.json b/src/i18n/zh/bomWeighting.json new file mode 100644 index 0000000..a8f7277 --- /dev/null +++ b/src/i18n/zh/bomWeighting.json @@ -0,0 +1,12 @@ +{ + "BOM Weighting Score List": "BOM 權重得分", + "Material Weighting": "BOM 加權", + "Material Score": "BOM得分", + "Weighting": "權重", + "Base Score": "基礎得分", + "Edit BOM Weighting Score": "編輯 BOM 權重得分", + "Scoring Item": "評分項目", + "Range": "範圍", + "Item Code": "物品編號", + "Item Name": "物品名稱" +} diff --git a/src/i18n/zh/chart.json b/src/i18n/zh/chart.json new file mode 100644 index 0000000..d0fc2dd --- /dev/null +++ b/src/i18n/zh/chart.json @@ -0,0 +1,113 @@ +{ + "all": "全部", + "autoRefresh": "自動重新整理", + "board_equipmentUsage": "設備使用看板", + "board_jobOrderChart": "工單圖表", + "board_jobOrderLive": "工單即時看板", + "board_processLive": "工序即時看板", + "dateRange_lastDays": "最近 {{d}} 天", + "delivery_colAvgMin": "平均分鐘/單", + "delivery_colPickCount": "揀單數", + "delivery_colStaff": "員工", + "delivery_colTotalMin": "總分鐘", + "delivery_dailyByStaff": "每日按員工單數", + "delivery_endDate": "結束日期", + "delivery_item": "物品", + "delivery_itemPlaceholder": "不選則全部", + "delivery_noDataDesc": "所選期間內暫無發貨記錄,請調整日期範圍後再試。", + "delivery_orderCount": "單數", + "delivery_ordersByDate": "按日期發貨單數量", + "delivery_ordersByDate_export": "發貨單數量_按日期", + "delivery_staff": "員工", + "delivery_staffPerfCaption": "週期內每人揀單數及總耗時(首揀至完成)", + "delivery_staffPerfDateError": "員工發貨績效的起始日期不能晚於結束日期", + "delivery_staffPerformanceTitle": "員工發貨績效(每日揀貨數量與耗時)", + "delivery_staffPlaceholder": "不選則全部", + "delivery_startDate": "開始日期", + "delivery_store": "倉別", + "delivery_topItemsByCount": "發貨數量排行(按物品)", + "equipment_boardTitle": "設備使用看板", + "equipment_completed": "已完工", + "equipment_endTime": "完工時間", + "equipment_equipment": "設備", + "equipment_infoDescription1": "即時顯示設備狀態:使用中、閒置或維護中。", + "equipment_infoDescription2": "每張設備卡片顯示當前工單和工序。", + "equipment_infoDescription3": "設備工時未結案或未填寫將被標記。", + "equipment_jobOrder": "工單", + "equipment_missingHours": "未填設備工時", + "equipment_notToday": "非今日", + "equipment_operator": "操作員", + "equipment_planStart": "工單計劃開始", + "equipment_process": "工序", + "equipment_searchAndList": "查詢與列表", + "equipment_startTime": "開工時間", + "equipment_status": "狀態", + "equipment_unclosedHours": "設備工時未結案", + "exportExcel": "匯出 Excel", + "forecast_itemCode": "物品編碼", + "forecast_noScheduleData": "此日期範圍內尚無排程資料。", + "forecast_plannedOutputByDate": "按物品計劃日產量(預測)", + "forecast_plannedOutputByDate_export": "計劃日產量_按物品", + "forecast_productionSchedule": "按日期生產排程(預估產量)", + "forecast_productionSchedule_export": "生產排程_按日期", + "intervalSeconds": "間隔(秒)", + "jo_byStatus": "工單按狀態", + "jo_byStatus_export": "工單_按狀態", + "jo_createdVsCompleted": "工單創建與完成按日期", + "jo_createdVsCompleted_export": "工單_創建與完成_按日期", + "jo_datePlanStart": "日期(計劃開始)", + "jo_detailSection": "工單物料/工序/設備", + "jo_equipmentWorkingWorked": "設備使用中/已使用(按工單)", + "jo_equipmentWorkingWorked_export": "設備_使用中_已使用", + "jo_materialPendingPicked": "物料待領/已揀(按工單計劃日)", + "jo_materialPendingPicked_export": "物料_待領_已揀", + "jo_processPendingCompleted": "工序待完成/已完成(按工單計劃日)", + "jo_processPendingCompleted_export": "工序_待完成_已完成", + "laneX": "車線-X", + "minuteAbbr": "分", + "minutes": "分鐘", + "minutesWithVal": "{{val}} 分鐘", + "noData": "無數據", + "off": "關閉", + "on": "開啟", + "otherBoards": "其他看板", + "pageTitle_delivery": "發貨與配送", + "pageTitle_equipmentBoard": "設備使用看板", + "pageTitle_forecast": "預測與計劃", + "pageTitle_jobOrder": "工單", + "pageTitle_jobOrderBoard": "工單即時看板", + "pageTitle_processBoard": "工序即時看板", + "pageTitle_purchase": "採購", + "pageTitle_warehouse": "庫存與倉儲", + "process_completed": "已完工", + "process_inProgress": "進行中", + "process_nonToday": "非今日", + "process_notStarted": "未開工", + "refresh": "重新整理", + "requestFailed": "請求失敗", + "selectDate": "選擇日期", + "series_balance": "餘額", + "series_completed": "完成", + "series_consumption": "消耗", + "series_created": "新建", + "series_inbound": "入庫", + "series_month": "月份", + "series_outbound": "出庫", + "series_total": "合計", + "show": "顯示", + "today": "今日", + "warehouse_addItem": "新增物品以分項顯示", + "warehouse_balanceTrend": "庫存餘額趨勢", + "warehouse_balanceTrend_export": "庫存餘額趨勢", + "warehouse_consumptionTrend": "按月考勤消耗趨勢(出庫量)", + "warehouse_consumptionTrend_export": "月考勤消耗趨勢", + "warehouse_exportFail": "總表匯出失敗", + "warehouse_optional": "可選", + "warehouse_qty": "數量", + "warehouse_stockInOutByDate": "按日期入庫與出庫", + "warehouse_stockInOutByDate_export": "入庫_出庫_按日期", + "warehouse_stockTxnByDate": "按日期庫存流水(入/出/合計)", + "warehouse_stockTxnByDate_export": "庫存流水_按日期", + "warehouse_sumAll": "不選則全部合計", + "yesterday": "昨日" +} diff --git a/src/i18n/zh/clientMonitor.json b/src/i18n/zh/clientMonitor.json new file mode 100644 index 0000000..33efc5e --- /dev/null +++ b/src/i18n/zh/clientMonitor.json @@ -0,0 +1,3 @@ +{ + "title": "裝置連線監控" +} diff --git a/src/i18n/zh/common.json b/src/i18n/zh/common.json index 28a3cf0..39ecc24 100644 --- a/src/i18n/zh/common.json +++ b/src/i18n/zh/common.json @@ -1,677 +1,126 @@ { - "dashboard": "資訊展示面板", - "Edit": "編輯", - "Job Order Production Process": "工單生產流程", - "productionProcess": "生產流程", - "Search Criteria": "搜索條件", - "Stock Record": "庫存記錄", - "No options": "沒有選項", - "Drink": "飲料", - "packaging": "提料中", - "Issue BOM List": "問題 BOM 列表", - "File Name": "檔案名稱", - "Please Select BOM": "請選擇 BOM", - "Plan Start": "預計生產日期", - "Floor": "樓層", - "Job Order Type": "工單類型", - - "BOM Type": "BOM 類型", - "No Lot": "沒有批號", - "Select All": "全選", - "Do Workbench": "新版成品出倉", - "DO Workbench": "新版成品出倉", - "storing": "待品檢入倉", - "Submit Qty": "提交數量", - "Waiting QC Put Away Job Orders": "待QC上架工單", - "Put Awayed Job Orders": "已上架工單", - "Loading BOM Detail...": "正在載入 BOM 明細…", - "Output Quantity": "使用數量", - "Process & Equipment": "製程與設備", - "Sequence": "順序", - "Process Name": "製程名稱", - "Plan start (from)": "開始日期(從)", - "Plan start (to)": "開始日期(至)", - "Process Description": "說明", - "Search date": "搜索日期", - "Confirm to Pass this Process?": "確認要通過此工序嗎?", - "Equipment Name": "設備", - "Confirm to update this Job Order?": "確認要完成此工單嗎?", - "Cancel Job Order": "取消工單", - "Confirm delete job order": "確認刪除工單", - "Delete job order confirm message": "確定要刪除此工單嗎?此操作無法復原。", - "Confirm cancel job order": "確認取消工單", - "Cancel job order confirm message": "確定要取消此工單嗎?工單將從列表中隱藏。", - "all": "全部", - "Bom Uom": "BOM 單位", - "Searched Item": "已搜索物料", - "drink": "飲料", - "other": "其他", - "Total job orders": "總工單數量", - "Filtered": "已過濾", - "Duration (Minutes)": "時間(分)", - "Prep Time (Minutes)": "準備時間", - "Post Prod Time (Minutes)": "收尾時間", - - "Correct BOM List (Can Import)": "正確 BOM 列表(可匯入)", - "Select Another Bag Lot": "選擇另一個包裝袋", - "Finished QC Job Orders": "完成QC工單", - "Stock Issue": "出倉問題", - "Step Start Time": "步驟開始時間", - "Overall Time Remaining": "總剩餘時間", - - "Reset": "重置", - "Complexity": "複雜度", - "Uom": "單位", - "Time Sequence": "時段", - "Density": "濃淡", - "Float": "浮沉", - "Allergic Substances": "過敏原", - "Basic Info": "基本資訊", - "Base Qty": "基本數量", - "Stock Qty": "庫存數量", - "Sales Qty": "銷售數量", - "Sales UOM": "銷售單位", - "Bom Material" : "BOM 材料", - "Stock Take Section": "盤點區域", - "Stock Take Section Description": "盤點區域描述", - "Lot No": "批號", - "Depth": "顔色深淺度 深1淺5", - "Search": "搜索", - "This lot is rejected, please scan another lot.": "此批次發現問題,請掃描另一個批號。", - "Process Start Time": "工序開始時間", - "Stock Req. Qty": "需求數", - "Actual Time Start": "實際開始時間", - "Actual Time End": "實際完成時間", - "Actual Time Used": "實際使用時間", - "Staff No Required": "員工編號必填", - "Stock Take Section (can use , to search multiple sections)": "盤點區域(可使用逗號搜索多個區域)", - "User Not Found": "用戶不存在", - "Time Remaining": "剩餘時間", - "Select Printer": "選擇打印機", - "Changeover Time (mins)": "生產後轉換時間(分鐘)", - "Finished Time": "完成時間", - "Passed Step": "通過步驟", - "Assume End Time": "預計完成時間", - "Printer": "打印機", - "Finished Qc Job Order List": "完成QC工單列表", - "Total finished Qc Job Order": "總完成QC工單數量", - "Timer Paused": "計時器已暫停", - "User not found with staffNo:": "用戶不存在", - "Total finished QC job orders": "總完成QC工單數量", - "Over Time": "超時", - "Code": "編號", - "Job Order No.": "工單編號", - "FG / WIP Item": "成品/半成品", - "Production Time Remaining": "生產剩餘時間", - "Process": "工序", - "Start": "開始", -"Finish": "完成", -"Wait Time [minutes]": "等待時間(分鐘)", - "Staff No": "員工編號", - "code": "編號", - "Name": "名稱", - "Assignment successful": "分配成功", - - "Unable to get user ID": "無法獲取用戶ID", - "Unknown error: ": "未知錯誤: ", - "Please try again later.": "請稍後重試。", - "Type": "類型", - "Update Job Order": "完成工單", - "No": "沒有", - "Assignment failed: ": "分配失敗: ", - "Unknown error": "未知錯誤", - "Job Process Status Dashboard": "儀表板 - 工單狀態", - "Time used": "耗時", - "In progress": "進行中", - "Previous page": "上一頁", - "Next page": "下一頁", - "Process page summary": "工序 {{from}}–{{to}} / 共 {{total}} 道", - "Duration hours": "{{count}} 小時", - "Duration minutes": "{{count}} 分鐘", - "Duration seconds": "{{count}} 秒", - "Job process detail: time": "時間", - "Job process detail: process name": "工序", - "Job process detail: equipment": "設備", - "Job process detail: handler": "員工", - "Job process detail mode label": "工序格顯示", - "Product process status": "生產流程狀態", - "Job dashboard PP status: pending": "工序待處理", - "Job dashboard PP status: in_progress": "工序進行中", - "Job dashboard PP status: stopped": "工序暫停", - "Job dashboard PP status: completed": "工序完成", - "Job dashboard PP status: cancelled": "工序已取消", - "stopped": "已停止", - "cancelled": "已取消", - - - "Total Time": "總時間", - "Remaining Time": "剩餘時間", - "Wait Time": "等待時間", - "Wait Time [minutes]": "等待時間(分鐘)", - "End Time": "完成時間", - "WIP": "半成品", - "R&D": "研發", - "STF": "樣品", - "Other": "其他", - "Add some entries!": "添加條目", - "Add Record": "新增", - "Clean Record": "重置", - "Dashboard": "資訊展示面板", - "Stock Take Management": "盤點管理", - - "Store Management": "倉庫管理", - "Delivery": "送貨訂單", - "Scheduling": "排程", - "Settings": "設定", - "User": "用戶", - "user": "用戶", - "User Group": "用戶群組", - "Items": "物品", - "BOM Weighting Score List": "物料清單權重得分", - "Material Weighting": "物料清單加權", - "Material Score": "物料清單得分", - "Coming soon": "即將推出", - "Base Score": "基礎得分", - "Column Name": "欄位名稱", - "Range": "範圍", - "Weighting": "權重", - "Total weighting must equal 1": "權重總和必須等於 1", - "Current total": "目前總和", - "Weighting must be a number": "權重必須為數字", - "Edit": "編輯", - "Edit BOM Weighting Score": "編輯物料清單", - "Save": "儲存", - "Saving": "儲存中", - "Cancel": "取消", - "Update Success": "更新成功", - "Update Failed": "更新失敗", - "Remarks": "備註", - "Release": "放單", - "Demand Forecast Setting": "需求預測設定", - "EquipmentType-EquipmentName-Code": "設備類型-設備名稱-編號", - "Equipment": "設備", - "Time Information(mins)": "時間信息(分鐘)", - "Processing Time": "生產時間", - "Setup Time": "生產前預備時間", - "Changeover Time": "生產後轉換時間", - "Warehouse": "倉庫", - "processing": "生產中", - "warehouse": "倉庫", - "Supplier": "供應商", - "Purchase Order": "採購單", - "PO Workbench": "採購單工作台", - "Demand Forecast": "需求預測", - "Pick Order": "提料單", - "Deliver Order": "送貨訂單", - "Project": "專案", - "Product": "產品", - - "mat": "原料", - "consumables": "消耗品", - "non-consumables": "非消耗品", - "fg": "成品", - "sfg": "半成品", - "item": "貨品", - "FG": "成品", - "Qty": "數量", - "FG & Material Demand Forecast Detail": "成品及材料需求預測詳情", - "View item In-out And inventory Ledger": "查看物料出入庫及庫存日誌", - "Delivery Order": "送貨訂單", - "Detail Scheduling": "詳細排程", - "Customer": "客戶", - "qcItem": "品檢項目", - - "Today": "今天", - "Yesterday": "昨天", - "Two Days Ago": "前天", - "Input Equipment is not match with process": "輸入的設備與流程不匹配", - "Staff No is required": "員工編號必填", - - "Day Before Yesterday": "前天", - "Select Date": "選擇日期", - "Production Date": "生產日期", - "QC Check Item": "QC 品檢項目", - "QC Category": "QC 品檢模板", - "QC Item All": "QC 綜合管理", - "qcItemAll": "QC 綜合管理", - "qcCategory": "品檢模板", - "QC Check Template": "QC 檢查模板", - "Mail": "郵件", - "Import Testing": "匯入測試", - "FG":"成品", - "Qty":"數量", - "FG & Material Demand Forecast Detail":"成品及材料需求預測詳情", - "View item In-out And inventory Ledger":"查看物料出入庫及庫存日誌", - "Delivery Order":"送貨訂單", - "Detail Scheduling":"詳細排程", - "Customer":"客戶", - "qcItem":"品檢項目", - "Item":"物料", - "Production Date":"生產日期", - "QC Check Item":"QC 品檢項目", - "QC Category":"QC 品檢模板", - "QC Item All":"QC 綜合管理", - "qcCategory":"品檢模板", - "QC Check Template":"QC檢查模板", - "QR Code Handle":"二維碼列印及下載", - "Mail":"郵件", - "Import Testing":"匯入測試", - "Overview": "總覽", - "Projects": "專案", - "Create Project": "新增專案", - "Task Template": "任務範本", - "Create Task Template": "新增任務範本", - "Qc Item": "QC 項目", - "FG Production Schedule": "FG 生產排程", - "Inventory": "庫存", - "scheduling": "排程", - "settings": "設定", - "items": "物品", - "edit":"編輯", - "bag": "包裝袋", - "Bag Usage": "包裝袋使用記錄", - "Edit Equipment Type":"設備類型詳情", - "Edit Equipment":"設備詳情", - "equipmentType":"設備種類", - "Description":"描述", - "edit": "編輯", - "Edit Equipment Type": "設備類型詳情", - "Edit Equipment": "設備詳情", - "equipmentType": "設備類型", - "Description": "描述", - "Details": "詳情", - "Equipment Type Details":"設備類型詳情", - "Equipment Type":"設備類型", - "Save":"儲存", - "Cancel":"取消", - "Equipment Details":"設備詳情", - "Exclude Date":"排除日期", - "Finished Goods Name":"成品名稱", - "Equipment Type Details": "設備類型詳情", - "Save": "儲存", - "Cancel": "取消", - "Equipment Details": "設備詳情", - "Exclude Date": "排除日期", - "Finished Goods Name": "成品名稱", - "create": "新增", - "hr": "小時", - "hrs": "小時", - "min": "分鐘", - "mins": "分鐘", - "Job Order": "工單", - "Edit Job Order": "工單詳情", - "Production": "生產流程", - "Put Away": "上架", - "Put Away Scan": "上架掃碼", - "Management Job Order": "管理工單", - "Search Job Order/ Create Job Order": "搜索工單/ 建立工單", - "Finished Good Order": "成品出倉", - "Finished Good Management": "成品出倉管理", - "提料順序": "提料順序", - "Filter": "過濾", - "Item Code": "物料編號", - "Item Name": "物料名稱", - "Just Completed (workbench): requires valid quantity; expired rows must not use this button.": "工單對料:需要有效數量;過期項目不能使用此按鈕。", - "Search & Jump": "搜索並跳轉", - "Enter to jump to item": "按 Enter 直接跳到品項位置", - "Jump": "跳轉", - "Move Up": "上移", - "Move Down": "下移", - "Move Top": "置頂", - "Move Bottom": "置底", - "Add Item": "加入品項", - "Refresh": "重新載入", - "Unsaved changes": "有未儲存的變更", - "Select items without order to append to bottom": "只會顯示尚未設定順序的品項,確認後會加到清單底部", - "Only show FG items without order": "請先輸入關鍵字再搜索(只會查詢未設定順序的品項)", - "Insert position must be >= 1": "插入位置必須大於或等於 1", - "Insert at": "插入位置", - "Order number": "順序號碼", - "Order": "順序", - "Location": "位置", - "finishedGood": "成品", - "Router": "執貨路線", - "Job Order Pickexcution": "工單提料", - "No data available": "沒有資料", - "Job Type": "工單類型", - "Start Scan": "開始掃碼", - "Stop Scan": "停止掃碼", - "Scan Result": "掃碼結果", - "Expiry Date": "有效期", - "Pick Order Code": "提料單編號", - "Target Date": "需求日期", - "Lot Required Pick Qty": "批號需求數量", - "Are you sure you want to delete this process?": "您確定要刪除此工序嗎?", - "Job Order Match": "工單對料", - "All Pick Order Lots": "所有提料單批號", - "Row per page": "每頁行數", - "Select Unit": "選擇單位", - "No data available": "沒有資料", - "Bom Req. Qty": "BOM", - "Material Name": "材料清單", - "Material Code": "材料清單", - "Bom UOM": "使用單位", - "Base UOM": "基本單位", - "Stock UOM": "庫存單位", - "jodetail": "工單細節", - "Sign out": "登出", - "By-product": "副產品", - "Complete Step": "完成步驟", - "Defect": "不良品", - "Output from Process": "工序產出", - "Quantity": "數量", - "Scrap": "損耗", - "Unit": "單位", - "Back to List": "返回列表", - "Production Output Data Entry": "生產輸出數據輸入", - "Step": "步驟", - "Quality Check": "品質檢查", - "Action": "操作", - "Changeover Time (mins)": "生產後轉換時間(分鐘)", - "Completed": "完成", - "completed": "完成", - "Date": "日期", - "Failed to submit scan data. Please try again.": "掃碼數據提交失敗. 請重試.", - "In Progress": "進行中", - "Is Dark": " 顔色深淺度", - "Is Dense": "濃淡", - "Is Float": "浮沉", - "Job Order Code": "工單編號", - - "Output Qty": "輸出數量", - "Pending": "待處理", - "pending": "待處理", - "Please scan equipment code (optional if not required)": "請掃描設備編號(可選)", - "Please scan operator code": "請掃描操作員編號", - "Please scan operator code first": "請先掃描操作員編號", - "Processing Time (mins)": "步驟時間(分鐘)", - "Production Process Information": "生產流程信息", - "Production Process Steps": "生產流程步驟", - "Scan Operator & Equipment": "掃描操作員和設備", - "Setup Time (mins)": "生產前預備時間(分鐘)", - "Start": "開始", - "Start QR Scan": "開始掃碼", - "Status": "狀態", - "in_progress": "進行中", - "In_Progress": "進行中", - "inProgress": "進行中", - "Step Name": "名稱", - "Stop QR Scan": "停止掃碼", - "Submit & Start": "提交並開始", - "Total Steps": "總步驟數", - "Unknown": "", - "Validation failed. Please check operator and equipment.": "驗證失敗. 請檢查操作員和設備.", - "View": "查看", - "Back": "返回", - "BoM Material": "材料清單", - "N/A": "不適用", - "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原 | 時間次序 | 複雜度", - "Please scan equipment code": "請掃描設備編號", - "Equipment Code": "設備編號", - "Seq": "步驟", - "SEQ": "步驟", - "Job Order Info": "工單信息", - "Matching Stock": "工單對料", - "No data found": "沒有找到資料", - "Production Priority": "生產優先序", - "Production Process": "工藝流程", - "Production Process Line Remark": "工藝明細", - "Remark": "明細", - "Req. Qty": "需求數量", - "Seq No": "加入步驟", - "Total pick orders": "總提料單數量", - "Seq No Remark": "序號明細", - "Stock Available": "庫存數", - "Confirm": "確認", - "Do you want to delete?": "您確定要刪除嗎?", - "Stock Status": "庫存狀態", - "Target Production Date": "目標生產日期", - "id": "ID", - "Finished lines": "已完成流程", - "Please scan staff no": "請掃描員工編號", - "Paused": "已暫停", - "paused": "已暫停", - "Pause Reason": "暫停原因", - "Reason": "原因", - "Invalid Stock In Line Id": "無效庫存行ID", - "Production date": "生產日期", - "update production priority": "更新生產優先序", - "Assume Time Need": "預計所需時間", - "Required Qty": "需求數", - "Bom Required Qty": "Bom 使用份量", - "Total processes": "總流程數", - "View Details": "查看詳情", - "view stockin": "品檢", - "minutes": "分鐘", - "Start Time": "開始時間", - "Consumed Qty": "消耗數量", - "Scrap Qty": "損耗數量", - "Add Bag": "新增包裝袋", - "Submit Bag Consumption": "提交包裝袋消耗", - "Bag Consumption": "包裝袋消耗", - "Bag": "包裝袋", - "Select Bag": "選擇包裝袋", - "No completed Job Order pick orders with matching found": "沒有相關記錄", - "Handler": "提料員", - "Completed Step": "完成步驟", - "Continue": "繼續", - "Executing": "執行中", - "Order Complete": "訂單完成", - "Pause": "暫停", - "Production Output Data": "生產輸出數據", - "Step Information": "步驟信息", - "Stop": "停止", - "Putaway Detail": "上架詳情", - "Lines with sufficient stock: ": "可提料項目數量: ", - "Lines with insufficient stock: ": "未能提料項目數量: ", - "Total lines: ": "總數量:", - "Balance": "可用數量", - "Selected Qty": "選擇數量", - "Submitting...": "提交中...", - "Batch Count": "批數", - "Shop": "店鋪", - "ShopAndTruck": "店鋪路線管理", - "DO floor (supplier)": "送貨單樓層(供應商)", - "Route Board": "車線看板", - "Shop Information": "店鋪資訊", - "Shop Name": "店鋪名稱", - "Shop Branch": "店鋪分店", - "Select a shop first": "請先選擇店鋪", - "Search or select branch": "搜索或選擇分店", - "Mass Edit": "批量編輯", - "Save All": "全部儲存", - "All shops updated successfully": "所有店鋪已成功更新", - "Shop Code": "店鋪編號", - "Truck Lane": "車線", - "Truck Lane Detail": "車線詳情", - "TruckLance Code": "車線編號", - "TruckLance Status": "車線狀態", - "Departure Time": "出發時間", - "Loading Sequence": "裝載順序", - "District Reference": "區域參考", - "Store ID": "樓層", - "Remark": "備註", - "Powder_Mixture": "箱料粉", - "Powder Mixture": "箱料粉", - "Not Match": "數值不符", - "Pass": "已盤點", - "pass": "已盤點", - "Actions": "操作", - "Insert": "插入", - "Move to order": "移動到指定順序", - "Target order": "目標順序", - "In front of": "前面", - "Behind": "後面", - "This will move the item to exact order": "確認後會把此品項移到指定順序位置,並自動重排其餘順序", - "View Detail": "查看詳情", - "Back": "返回", - "Back to Truck Lane List": "返回車線列表", - "Back to List": "返回列表", - "Add Truck Lane": "新增車線", - "Add New Truck Lane": "新增車線", - "Truck Information": "車線資訊", - "No Truck data available": "沒有卡車資料", - "No shops found using this truck lane": "沒有找到使用此車線的店鋪", - "Shops Using This Truck Lane": "使用此車線的店鋪", - "Complete": "完成", - "Missing Data": "缺少資料", - "No TruckLance": "無車線", - "Edit shop truck lane": "編輯店鋪車線", - "Delete truck lane": "刪除車線", - "Edit loading sequence": "編輯裝載順序", - "Save changes": "儲存變更", - "Cancel editing": "取消編輯", - "Edit truck lane": "編輯車線", - "Truck ID is required": "需要卡車ID", - "Truck lane not found": "找不到車線", - "No truck lane data available": "沒有車線資料", - "Failed to load truck lanes": "載入車線失敗", - "Failed to load shops": "載入店鋪失敗", - "Loading sequence updated successfully": "裝載順序更新成功", - "Failed to save loading sequence": "儲存裝載順序失敗", - "Truck lane deleted successfully": "車線刪除成功", - "Failed to delete truck lane": "刪除車線失敗", - "Are you sure you want to delete this truck lane?": "您確定要刪除此車線嗎?", - "Invalid shop data": "無效的店鋪資料", - "Contact No": "聯絡電話", - "Contact Email": "聯絡郵箱", - "Contact Name": "聯絡人", - "Addr1": "地址1", - "Addr2": "地址2", - "Addr3": "地址", - "Shop not found": "找不到店鋪", - "Shop ID is required": "需要店鋪ID", - "Invalid Shop ID": "無效的店鋪ID", - "Failed to load shop detail": "載入店鋪詳情失敗", - "Failed to load shop details": "載入店鋪詳情失敗", - "Failed to save truck data": "儲存卡車資料失敗", - "Failed to delete truck lane": "刪除車線失敗", - "Failed to create truck": "建立卡車失敗", - "Please fill in the following required fields:": "請填寫以下必填欄位:", - "TruckLance Code": "車線編號", - "Enter or select remark": "輸入或選擇備註", - "Not editable for this Store ID": "此樓層不可編輯", - "No Truck Lane data available": "沒有車線資料", - "Please log in to view shop details": "請登入以查看店鋪詳情", - "Invalid truck data": "無效的卡車資料", - "Invalid departure time": "無效的出發時間", - "No trucks found for this truck lane": "此車線沒有可更新的資料", - "Departure time updated for all shops on this truck lane": "已將出發時間更新至此車線下的所有店鋪", - "Applies to all shops using this truck lane": "將套用至所有使用此車線的店鋪", - "Edit departure time": "編輯出發時間", - "Failed to load truck lane detail": "載入車線詳情失敗", - "Shop Detail": "店鋪詳情", - "Just Pass": "已完成", - "Truck Lane Detail": "車線詳情", - "Filter by Status": "按狀態篩選", - "All": "全部", - "General Data": "基本資料", - "Repair and Maintenance": "維護和保養", - "Repair and Maintenance Status": "維護和保養狀態", - "Latest Repair and Maintenance Date": "最新維護和保養日期", - "Last Repair and Maintenance Date": "上次維護和保養日期", - "Repair and Maintenance Remarks": "維護和保養備註", - "Rows per page": "每頁行數", - "Equipment Name": "設備名稱", - "Equipment Code": "設備編號", - "Yes": "是", - "No": "否", - "No.": "編號", - "Operator Name & No.": "操作員名稱及編號", - "Count of Job Orders": "已處理工單", - "Total Processing Time": "總工時", - "Material Pick Status": "物料提料狀態", - "Operator KPI Dashboard": "儀表板 - 操作員KPI概覽", - "Operator": "員工資訊", - "Equipment Name and Code": "設備名稱及編號", - "Remaining Time (min)": "剩餘時間(分鐘)", - "Production Equipment Status Dashboard": "儀表板 - 生產設備最新狀態", - "Idle": "閒置", - "Process": "工序", - "Job Details": "工單編號及生產產品", - "Required Time": "所需時間", - "Estimated Completion Time": "預計完成時間", - "Job Order and Product": "工單及貨品", - "Update Equipment Maintenance and Repair": "更新設備的維護和保養", - "Equipment Information": "設備資訊", - "Loading": "載入中...", - "Equipment not found": "找不到設備", - "Error saving data": "保存數據時出錯", - "TruckLance Code is required": "需要車線編號", - "Truck shop details updated successfully": "卡車店鋪詳情更新成功", - "Failed to save truck shop details": "儲存卡車店鋪詳情失敗", - "Truck lane information is required": "需要車線資訊", - "Shop added to truck lane successfully": "店鋪已成功新增至車線", - "Failed to create shop in truck lane": "新增店鋪至車線失敗", - "Add Shop": "新增店鋪", - "Search or select shop name": "搜索或選擇店鋪名稱", - "stocktakemanagement": "盤點管理", - "stockRecord": "盤點記錄", - "Search or select shop code": "搜索或選擇店鋪編號", - "Search or select remark": "搜索或選擇備註", - "Edit shop details": "編輯店鋪詳情", - "Add Shop to Truck Lane": "新增店鋪至車線", - "Truck lane code already exists. Please use a different code.": "車線編號已存在,請使用其他編號。", - "MaintenanceEdit": "編輯維護和保養", - "ps": "排程", - "Printer": "列印機", - "Delete": "刪除", - "Delete Success": "刪除成功", - "Delete Failed": "刪除失敗", - "Create Printer": "新增列印機", - "Report": "報告", - "Issue": "問題", - "Note:": "注意:", - "Required Qty": "需求數量", - "Verified Qty": "確認數量", - "Max": "最大值", - "Min": "最小值", - "Max": "最大值", - "This form is for reporting issues only. You must report either missing items or bad items.": "此表單僅用於報告問題。您必須報告缺少的物品或不良物品。", - "Pick Execution Issue Form": "提料問題表單", - "Missing items": "缺少物品", - "Total (Verified + Bad + Missing) must equal Required quantity": "總數必須等於需求數量", - "Missing item Qty": "缺少物品數量", - "seq": "序號", - "Job Order Pick Execution": "工單提料", - "Bad Item Qty": "不良物品數量", - "Issue Remark": "問題備註", - "At least one issue must be reported": "至少需要報告一個問題", - "Qty is required": "數量是必填項", - "Verified quantity cannot exceed received quantity": "確認數量不能超過接收數量", - "Handled By": "處理者", - "submit": "提交", - "Received Qty": "接收數量", - "bomWeighting": "物料清單權重得分", - "Now": "現時", - "Last updated": "最後更新", - "Auto-refresh every 5 minutes": "每5分鐘自動刷新", - "Auto-refresh every 10 minutes": "每10分鐘自動刷新", - "Auto-refresh every 15 minutes": "每15分鐘自動刷新", - "Auto-refresh every 1 minute": "每1分鐘自動刷新", - "Brand": "品牌", - "Price Inquiry": "價格查詢", - "No Purchase Order After 2026-01-01": "在2026-01-01後沒有採購記錄", - "No Import Record": "沒有導入記錄", - - "wip": "半成品", - "cmb": "消耗品", - "nm": "雜項及非消耗品", - "MAT": "材料", - "CMB": "消耗品", - "NM": "雜項及非消耗品", - "Download Template": "下載範本", - "Upload": "上傳", - "Downloading...": "正在下載...", - "Uploading...": "正在上傳...", - "Upload successful": "上傳成功", - "Upload failed": "上傳失敗", - "Download failed": "下載失敗", - "Upload completed with count": "已更新 {{count}} 個項目。", - "Upload row errors": "以下行有問題:", - "item(s) updated": "個項目已更新。", - "Average unit price": "平均單位價格", - "Latest market unit price": "最新市場價格", - "Current Stock": "現有庫存", - "masterDataIssue_nav": "BOM/貨品單位問題" -} \ No newline at end of file + "Actions": "操作", + "Add Document": "新增文件", + "All": "全部", + "Allergic Substances": "過敏原", + "An error has occurred. Please try again later.": "發生錯誤,請稍後再試。", + "Are you sure you want to delete this item?": "您確定要刪除此項目嗎?", + "Back": "返回", + "Basic Info": "基本資訊", + "Bom Required Qty": "BOM 使用份量", + "Bom UOM": "BOM 單位", + "Brand": "品牌", + "CMB": "消耗品", + "CO": "消耗品", + "Cancel": "取消", + "Column Name": "欄位名稱", + "Coming soon": "即將推出", + "Complexity": "複雜度", + "Confirm": "確認", + "Confirm Delete": "確認刪除", + "Cost (HKD)": "費用 (HKD)", + "Current total": "目前總和", + "Day Before Yesterday": "前天", + "Delete": "刪除", + "Delete Failed": "刪除失敗", + "Density": "濃淡", + "Depth": "顔色深淺度 深1淺5", + "Description": "描述", + "Details": "詳情", + "Duration (Minutes)": "時間(分)", + "Edit": "編輯", + "Enter any additional observations or notes...": "輸入其他觀察或備註...", + "Enter or select remark": "輸入或選擇備註", + "Error saving data": "保存數據時出錯", + "Failed to fetch data": "無法取得資料", + "Filter": "過濾", + "Finished Good Detail": "成品出倉詳情", + "Finished Good Management": "成品出倉管理", + "Finished Good Order": "成品出倉", + "Float": "浮沉", + "General Data": "基本資料", + "Grade {{grade}}": "等級 {{grade}}", + "Invalid Job Order Id": "無效工單編號", + "Invoice": "發票", + "Invoice Date": "發票日期", + "Item Code": "物品編號", + "Item Name": "物品名稱", + "Loading": "載入中...", + "Loading order summary": "正在載入訂單摘要", + "Location": "位置", + "MA": "材料", + "MAT": "材料", + "MI": "雜項", + "Management Job Order": "管理工單", + "Material Name": "材料清單", + "Min": "最小值", + "NM": "雜項及非消耗品", + "No": "否", + "No Lot": "沒有批號", + "No data available": "沒有資料", + "No options": "沒有選項", + "No processes found for this job order": "找不到此工單的工序", + "Order": "順序", + "Pending": "待處理", + "Please Select BOM": "請選擇 BOM", + "Please try again later.": "請稍後重試。", + "Project Code": "專案代碼", + "Project Code and Name": "專案代碼與名稱", + "QC Template not found": "找不到 QC 範本", + "Qty": "數量", + "RM": "原料", + "Range": "範圍", + "Refresh": "重新載入", + "Remarks": "備註", + "Remove Document": "移除文件", + "Report": "報告", + "Reset": "重置", + "Row per page": "每頁行數", + "Rows per page": "每頁行數", + "Sales Qty": "銷售數量", + "Sales UOM": "銷售單位", + "Save": "儲存", + "Saving": "儲存中", + "Search": "搜索", + "Search Criteria": "搜索條件", + "Select Date": "選擇日期", + "Session expired or unauthorized.": "工作階段已過期或未經授權。", + "Sign out": "登出", + "Status": "狀態", + "Stock Qty": "庫存數量", + "Supporting Document": "證明文件", + "Task": "任務", + "Time Sequence": "時段", + "Today": "今天", + "Total weighting must equal 1": "權重總和必須等於 1", + "Unauthorized: Please log in again": "未經授權:請重新登入", + "Uom": "單位", + "Update Failed": "更新失敗", + "Update Success": "更新成功", + "Weighting must be a number": "權重必須為數字", + "Yes": "是", + "Yesterday": "昨天", + "all": "全部", + "bomWeighting": "BOM 權重得分", + "cmb": "消耗品", + "collapsible table": "可折疊表格", + "consumable": "消耗品", + "consumables": "消耗品", + "create": "新增", + "edit": "編輯", + "expand row": "展開行", + "group mode": "群組模式", + "item": "貨品", + "items": "物品", + "mat": "原料", + "menu": "選單", + "nm": "雜項及非消耗品", + "non-consumables": "非消耗品", + "other": "其他", + "profile": "個人資料", + "revert": "還原", + "settings": "設定", + "testing sections tabs": "測試區域分頁", + "warehouse": "倉庫", + "材料": "材料" +} diff --git a/src/i18n/zh/dashboard.json b/src/i18n/zh/dashboard.json index a937856..017ab5c 100644 --- a/src/i18n/zh/dashboard.json +++ b/src/i18n/zh/dashboard.json @@ -1,5 +1,7 @@ { "Dashboard": "資訊展示面板", + "dashboard": "資訊展示面板", + "dashboard tabs": "儀表板分頁", "Order status": "訂單狀態", "pending": "待處理", "receiving": "收貨中", @@ -16,7 +18,7 @@ "Warehouse status": "倉庫狀態", "Progress chart": "進度圖表", "Po Code/Jo Code": "採購編號/工單編號", - "Purchase Order Code": "採購單號", + "Purchase Order Code": "採購訂單編號", "Item Name": "貨品名稱", "Escalation Level": "上報等級", "Reason": "上報原因", @@ -59,7 +61,7 @@ "Responsible Escalation List": "負責的上報列表", "show completed logs": "顯示已完成上報", "Rows per page": "每頁行數", - "Truck Schedule Dashboard": "車線調度儀表板", + "Truck Schedule Dashboard": "車線儀表板", "Store ID": "樓層", "All Stores": "所有樓層", "Auto-refresh every 5 minutes": "每5分鐘自動刷新", @@ -105,20 +107,18 @@ "Column 2": "欄位2", "Column 3": "欄位3", "No data available": "暫無資料", + "Overview": "總覽", "Supplier Code": "供應商編號", "Supplier Name": "供應商名稱", - "Purchase Order Code": "採購訂單編號", "Statistics": "統計", "Show Supplier Code": "顯示供應商編號", "Show Purchase Order Codes": "顯示採購訂單編號", "x/y orders received": "x/y張單已處理", "Goods Receipt Status New": "採購單接收狀態", "Status": "狀態", - "Now": "現時", - "Last updated": "最後更新", - "Auto-refresh every 5 minutes": "每5分鐘自動刷新", + "Two column navigable table": "雙欄導航表格", + "Now": "現時", "Auto-refresh every 10 minutes": "每10分鐘自動刷新", - "Auto-refresh every 15 minutes": "每15分鐘自動刷新", "Auto-refresh every 1 minute": "每1分鐘自動刷新", "Usage statistics": "報告管理與送貨路線摘要使用統計", "Usage stats description": "報告管理與送貨路線摘要:進入頁面次數、下載與直接列印次數。", diff --git a/src/i18n/zh/deliveryOrderFloor.json b/src/i18n/zh/deliveryOrderFloor.json index 6718d20..6cabec6 100644 --- a/src/i18n/zh/deliveryOrderFloor.json +++ b/src/i18n/zh/deliveryOrderFloor.json @@ -1,6 +1,6 @@ { "title": "送貨單樓層設定(供應商代碼)", - "Intro": "以下為系統設定中的供應商代碼(逗號分隔)。點編輯可修改;是否影響 DO 行為取決於後端是否讀取對應 settings。", + "Intro": "管理各樓層的供應商代碼。點擊編輯按鈕可新增或移除供應商。", "2F supplier": "2F 供應商", "4F supplier": "4F 供應商", "Edit 2F": "編輯 2F", @@ -29,5 +29,6 @@ "Comma separated hint": "多個代碼請以英文逗號分隔,勿加空白", "Save": "儲存", "Saved": "已儲存", - "Cancel": "取消" + "Cancel": "取消", + "DO floor (supplier)": "送貨單樓層(供應商)" } diff --git a/src/i18n/zh/demandForecast.json b/src/i18n/zh/demandForecast.json new file mode 100644 index 0000000..feb7352 --- /dev/null +++ b/src/i18n/zh/demandForecast.json @@ -0,0 +1,12 @@ +{ + "Demand Forecast Setting": "需求預測設定", + "Finished Goods Name": "成品名稱", + "Exclude Date": "排除日期", + "Mon": "週一", + "Tue": "週二", + "Wed": "週三", + "Thu": "週四", + "Fri": "週五", + "Sat": "週六", + "Sun": "週日" +} diff --git a/src/i18n/zh/detailScheduling.json b/src/i18n/zh/detailScheduling.json index c0bb011..8666fa5 100644 --- a/src/i18n/zh/detailScheduling.json +++ b/src/i18n/zh/detailScheduling.json @@ -1,21 +1,21 @@ { - "Detail Scheduling": "詳細排程", - "Search Criteria": "搜索條件", - "Search": "搜索", - "Reset": "重置", - "Search by Project Code or Project Name or Client Name or Project Category or Project Type or Project Status or Project Start Date or Project End Date": "搜索專案編號、專案名稱、客戶名稱、專案類別、專案類型、專案狀態、專案開始日期、專案結束日期", - "Project Code": "專案編號", - "Project Name": "專案名稱", - "Client Name": "客戶名稱", - "Project Category": "專案類別", - "Project Type": "專案類型", - "Project Status": "專案狀態", - "Demand Forecast Period": "需求預測期", - "Scheduled At": "預定時間", - "Product Count(s)": "產品數量", - "Product Count": "產品數量", - "Schedule Period": "排程期間", - "Product": "產品", - "Details": "詳情", - "types": "類型" -} \ No newline at end of file + "Detail Scheduling": "詳細排程", + "Search Criteria": "搜索條件", + "Search": "搜索", + "Reset": "重置", + "Search by Project Code or Project Name or Client Name or Project Category or Project Type or Project Status or Project Start Date or Project End Date": "搜索專案編號、專案名稱、客戶名稱、專案類別、專案類型、專案狀態、專案開始日期、專案結束日期", + "Project Code": "專案編號", + "Project Name": "專案名稱", + "Client Name": "客戶名稱", + "Project Category": "專案類別", + "Project Type": "專案類型", + "Project Status": "專案狀態", + "Demand Forecast Period": "需求預測期", + "Scheduled At": "預定時間", + "Product Count(s)": "產品數量", + "Product Count": "產品數量", + "Schedule Period": "排程期間", + "Product": "產品", + "Details": "詳情", + "types": "類型" +} diff --git a/src/i18n/zh/do.json b/src/i18n/zh/do.json index 5ab7452..a22423c 100644 --- a/src/i18n/zh/do.json +++ b/src/i18n/zh/do.json @@ -1,97 +1,98 @@ { - "Delivery Order": "送貨訂單", - "Shop Name": "店鋪名稱", - "Delivery Order No.": "送貨訂單編號", - "Delivery Order Date": "送貨訂單日期", - "Delivery Order Status": "送貨訂單狀態", - "Order Date": "訂單日期", - "Estimated Arrival": "預計送貨日期", - "Estimated Arrival From": "預計送貨日期", - "Estimated Arrival To": "預計送貨日期至", - "Status": "來貨狀態", - "Etra": "加單", - "Loading": "正在加載...", - "No delivery orders selected for batch release. Uncheck orders you want to exclude, or search again to reset selection.": "沒有選擇送貨訂單進行批量放單。取消勾選您想排除的訂單,或重新搜索以重置選擇。", - "No Records": "沒有找到記錄", - "OK": "確認", - "Truck X": "車線-X", - "DO Workbench": "新版成品出倉", - "Order Date From": "訂單日期", - "Workbench Batch Release": "批量放單", - "do workbench": "新版成品出倉", - "Do Workbench": "新版成品出倉", - "Delivery Order Code": "送貨訂單編號", - "Floor": "樓層", - "Truck lane search requires date title": "需選擇預計送貨日期", - "Truck lane search requires date message": "已填寫車線號碼時,請一併選擇預計送貨日期後再搜索。", - "Truck Lance Code": "車線號碼", - "Select Remark": "選擇備註", - "Confirm Assignment": "確認分配", - "Submit Qty": "提交數量", - "Required Date": "所需日期", - "Submit Miss Item": "提交缺貨品", - "Submit Quantity": "提交數量", - "Store": "位置", - "Lane Code": "車線號碼", - "Available Orders": "可用訂單", - "Just Complete": "已完成", - "Order Date To": "訂單日期至", - "Warning: Some delivery orders do not have matching trucks for the target date.": "警告:部分送貨訂單於目標日期沒有可匹配的車線。", - "Truck Availability Warning": "車線可用性警告", - "Problem DO(s): ": "問題送貨訂單", - "Fetching all matching records...": "正在獲取所有匹配的記錄...", - "Progress": "進度", - "Loading...": "正在加載...", - "Available Trucks": "可用車線", - "No trucks available": "沒有車線可用", - "Remark": "備註", - "Just Completed": "已完成", - "Code": "門店訂單編號", - "code": "門店訂單編號", - "Create": "新增", - "Supplier Name": "供應商名稱", - "Details": "詳情", - "Pending": "待處理", - "pending": "待處理", - "Receiving": "接收中", - "receiving": "接收中", - "Completed": "已完成", - "completed": "已完成", - "Please wait": "請稍候", - "Selected Shop(s): ": "已選擇店舖數量: ", - "Selected Item(s): ": "總貨品數量: ", - "Confirm": "確認", - "Cancel": "取消", - "Releasing": "處理中", - "Shop Code": "店鋪編號", - "Supplier Code": "供應商編號", - "Estimated Arrival Date": "預計送貨日期", - "Item No.": "商品編號", - "Item Name": "商品名稱", - "Quantity": "數量", - "uom": "單位", - "Lot No.": "批號", - "Expiry Date": "有效期", - "Location": "庫位", - "Price": "價格", - "Action": "操作", - "Edit": "編輯", - "Delete": "刪除", - "Release": "放單", - "Back": "返回", - "Batch Release": "批量放單", - "Batch release completed successfully.": "已完成批量放單", - "Edit Delivery Order Detail": "編輯送貨訂單詳情", - "DO released successfully! Pick orders created.": "送貨訂單放單成功!提料單已建立。", - "Stock Available": "庫存可用", - "row selected": "行已選擇", - "An error has occurred. Please try again later.": "發生錯誤,請稍後再試。", - "Assign 2/F": "分配2/F", - "Assign 4/F": "分配4/F", - "Failed to assign pick orders. Please try again later.": "分配提料單失敗,請稍後再試。", - "Failed to release pick orders. Please try again later.": "放單提料單失敗,請稍後再試。", - "Pick Order Assignment": "提料單分配", - "Release 2/F": "放單2/F", - "Release 4/F": "放單4/F", - "Stock Status": "庫存狀態" -} \ No newline at end of file + "Delivery Order": "送貨訂單", + "Shop Name": "店鋪名稱", + "Delivery Order No.": "送貨訂單編號", + "Delivery Order Date": "送貨訂單日期", + "Delivery Order Status": "送貨訂單狀態", + "Order Date": "訂單日期", + "Estimated Arrival": "預計送貨日期", + "Estimated Arrival From": "預計送貨日期", + "Estimated Arrival To": "預計送貨日期至", + "Status": "來貨狀態", + "Etra": "加單", + "Loading": "正在加載...", + "No delivery orders selected for batch release. Uncheck orders you want to exclude, or search again to reset selection.": "沒有選擇送貨訂單進行批量放單。取消勾選您想排除的訂單,或重新搜索以重置選擇。", + "No Records": "沒有找到記錄", + "OK": "確認", + "Truck X": "車線-X", + "DO Workbench": "新版成品出倉", + "Order Date From": "訂單日期", + "Workbench Batch Release": "批量放單", + "do workbench": "新版成品出倉", + "Do Workbench": "新版成品出倉", + "Delivery Order Code": "送貨訂單編號", + "Floor": "樓層", + "Truck lane search requires date title": "需選擇預計送貨日期", + "Truck lane search requires date message": "已填寫車線號碼時,請一併選擇預計送貨日期後再搜索。", + "Truck Lance Code": "車線號碼", + "Select Remark": "選擇備註", + "Confirm Assignment": "確認分配", + "Submit Qty": "提交數量", + "Required Date": "所需日期", + "Submit Miss Item": "提交缺貨品", + "Submit Quantity": "提交數量", + "Store": "位置", + "Lane Code": "車線號碼", + "Available Orders": "可用訂單", + "Just Complete": "已完成", + "Order Date To": "訂單日期至", + "Warning: Some delivery orders do not have matching trucks for the target date.": "警告:部分送貨訂單於目標日期沒有可匹配的車線。", + "Truck Availability Warning": "車線可用性警告", + "Problem DO(s): ": "問題送貨訂單", + "Fetching all matching records...": "正在獲取所有匹配的記錄...", + "Progress": "進度", + "Loading...": "正在加載...", + "Available Trucks": "可用車線", + "No trucks available": "沒有車線可用", + "Remark": "備註", + "Just Completed": "已完成", + "Code": "門店訂單編號", + "code": "門店訂單編號", + "Create": "新增", + "Supplier Name": "供應商名稱", + "Details": "詳情", + "Pending": "待處理", + "pending": "待處理", + "Receiving": "接收中", + "receiving": "接收中", + "Completed": "已完成", + "completed": "已完成", + "Please wait": "請稍候", + "Selected Shop(s): ": "已選擇店舖數量: ", + "Selected Item(s): ": "總貨品數量: ", + "Confirm": "確認", + "Cancel": "取消", + "Releasing": "處理中", + "Shop Code": "店鋪編號", + "Supplier Code": "供應商編號", + "Estimated Arrival Date": "預計送貨日期", + "Item No.": "商品編號", + "Item Name": "商品名稱", + "Quantity": "數量", + "uom": "單位", + "Lot No.": "批號", + "Expiry Date": "有效期", + "Location": "庫位", + "Price": "價格", + "Action": "操作", + "Edit": "編輯", + "Delete": "刪除", + "Release": "放單", + "Back": "返回", + "Batch Release": "批量放單", + "Batch release completed successfully.": "已完成批量放單", + "Edit Delivery Order Detail": "編輯送貨訂單詳情", + "DO released successfully! Pick orders created.": "送貨訂單放單成功!提料單已建立。", + "Stock Available": "庫存可用", + "row selected": "行已選擇", + "An error has occurred. Please try again later.": "發生錯誤,請稍後再試。", + "Assign 2/F": "分配2/F", + "Assign 4/F": "分配4/F", + "Failed to assign pick orders. Please try again later.": "分配提料單失敗,請稍後再試。", + "Failed to release pick orders. Please try again later.": "放單提料單失敗,請稍後再試。", + "Pick Order Assignment": "提料單分配", + "Release 2/F": "放單2/F", + "Release 4/F": "放單4/F", + "Stock Status": "庫存狀態", + "Delivery": "送貨訂單" +} diff --git a/src/i18n/zh/doWorkbench.json b/src/i18n/zh/doWorkbench.json new file mode 100644 index 0000000..d918e48 --- /dev/null +++ b/src/i18n/zh/doWorkbench.json @@ -0,0 +1,49 @@ +{ + "DO Workbench": "新版成品出倉", + "Confirm": "確認", + "Cancel": "取消", + "Shop Name": "店鋪名稱", + "Target Date": "需求日期", + "Back to List": "返回列表", + "Store ID": "樓層", + "Handler": "提料員", + "Pick Order": "提料單", + "items": "物品", + "Delivery Order": "送貨訂單", + "Edit Delivery Order Detail": "編輯送貨訂單詳情", + "DO Workbench Search": "DO Workbench 搜索", + "Item Code": "物品編號", + "Item Name": "物品名稱", + "Lot No": "批號", + "Location": "位置", + "Required Qty": "需求數量", + "Status": "狀態", + "Search date": "搜索日期", + "completed": "完成", + "Completed": "完成", + "View Details": "查看詳情", + "Loading Sequence": "裝載順序", + "Departure Time": "出發時間", + "Select Date": "選擇日期", + "Today": "今天", + "Shop": "店鋪", + "Pick Order Code": "提料單編號", + "All Pick Order Lots": "所有提料單批號", + "Stop QR Scan": "停止掃碼", + "Start QR Scan": "開始掃碼", + "Submitting...": "提交中...", + "Lot Required Pick Qty": "批號需求數量", + "Scan Result": "掃碼結果", + "No data available": "沒有資料", + "This lot is rejected, please scan another lot.": "此批次發現問題,請掃描另一個批號。", + "Issue": "問題", + "Edit": "編輯", + "Rows per page": "每頁行數", + "Floor": "樓層", + "pending": "待處理", + "Now": "現時", + "Auto-refresh every 5 minutes": "每5分鐘自動刷新", + "Last updated": "最後更新", + "Truck Information": "車線資訊", + "Actions": "操作" +} diff --git a/src/i18n/zh/equipment.json b/src/i18n/zh/equipment.json new file mode 100644 index 0000000..f3953f6 --- /dev/null +++ b/src/i18n/zh/equipment.json @@ -0,0 +1,21 @@ +{ + "Create Material": "新增物料", + "Equipment": "設備", + "Equipment Type": "設備類型", + "Update Equipment Maintenance and Repair": "更新設備的維護和保養", + "Create Equipment": "新增設備", + "Edit Equipment": "編輯設備", + "Create Equipment Type": "新增設備類型", + "Edit Equipment Type": "編輯設備類型", + "Equipment Information": "設備資訊", + "Equipment Name": "設備名稱", + "Equipment Code": "設備編號", + "Equipment Details": "設備詳情", + "Equipment Type Details": "設備類型詳情", + "Equipment not found": "找不到設備", + "Last Repair and Maintenance Date": "上次維護和保養日期", + "Latest Repair and Maintenance Date": "最新維護和保養日期", + "Repair and Maintenance": "維護和保養", + "Repair and Maintenance Remarks": "維護和保養備註", + "Repair and Maintenance Status": "維護和保養狀態" +} diff --git a/src/i18n/zh/finishedGood.json b/src/i18n/zh/finishedGood.json new file mode 100644 index 0000000..4fbd183 --- /dev/null +++ b/src/i18n/zh/finishedGood.json @@ -0,0 +1,4 @@ +{ + "Finished Good Order": "成品出倉", + "Finished Good Detail": "成品出倉詳情" +} diff --git a/src/i18n/zh/finishedgoodmanagement.json b/src/i18n/zh/finishedgoodmanagement.json new file mode 100644 index 0000000..76c02d6 --- /dev/null +++ b/src/i18n/zh/finishedgoodmanagement.json @@ -0,0 +1,38 @@ +{ + "Finished Good Management": "成品出倉管理", + "提料順序": "提料順序", + "Item Code": "物品編號", + "Item Name": "物品名稱", + "Search & Jump": "搜索並跳轉", + "Jump": "跳轉", + "Move Up": "上移", + "Move Down": "下移", + "Move Top": "置頂", + "Move Bottom": "置底", + "Add Item": "新增物品", + "Saving": "儲存中", + "Save": "儲存", + "Refresh": "重新載入", + "Unsaved changes": "有未儲存的變更", + "Order": "順序", + "Code": "編號", + "Name": "名稱", + "Shipping Warehouse": "出貨倉位", + "Receiving Warehouse": "入貨倉位", + "Actions": "操作", + "Loading": "載入中...", + "No data available": "沒有資料", + "Insert": "插入", + "Search": "搜索", + "Insert at": "插入位置", + "Insert position must be >= 1": "插入位置必須大於或等於 1", + "Order number": "順序號碼", + "In front of": "前面", + "Behind": "後面", + "Only show FG items without order": "請先輸入關鍵字再搜索(只會查詢未設定順序的品項)", + "Cancel": "取消", + "Confirm": "確認", + "Move to order": "移動到指定順序", + "Target order": "目標順序", + "This will move the item to exact order": "確認後會把此品項移到指定順序位置,並自動重排其餘順序" +} diff --git a/src/i18n/zh/home.json b/src/i18n/zh/home.json index 8f319b9..23b8b1b 100644 --- a/src/i18n/zh/home.json +++ b/src/i18n/zh/home.json @@ -1,5 +1,4 @@ { - "Demand Qty": "需求數量", - "Add some entries!": "添請添加條目" - -} \ No newline at end of file + "Demand Qty": "需求數量", + "Add some entries!": "請添加條目" +} diff --git a/src/i18n/zh/importBom.json b/src/i18n/zh/importBom.json new file mode 100644 index 0000000..6160967 --- /dev/null +++ b/src/i18n/zh/importBom.json @@ -0,0 +1,12 @@ +{ + "Create Material": "新增材料", + "Update Equipment Maintenance and Repair": "更新設備的維護和保養", + "Correct BOM List (Can Import)": "正確 BOM 列表(可匯入)", + "Issue BOM List": "問題 BOM 列表", + "Loading BOM Detail...": "正在載入 BOM 明細…", + "Bom Material": "材料清單", + "Is Drink": "飲料", + "Drink": "飲料", + "Powder_Mixture": "箱料粉", + "Base Score": "基礎得分" +} diff --git a/src/i18n/zh/importExcel.json b/src/i18n/zh/importExcel.json new file mode 100644 index 0000000..c6a0784 --- /dev/null +++ b/src/i18n/zh/importExcel.json @@ -0,0 +1,15 @@ +{ + "title": "Excel 匯入", + "Download Template": "下載範本", + "Download failed": "下載失敗", + "Downloading...": "正在下載...", + "File Name": "檔案名稱", + "No Import Record": "沒有導入記錄", + "Upload": "上傳", + "Upload completed with count": "已更新 {{count}} 個項目。", + "Upload failed": "上傳失敗", + "Upload row errors": "以下行有問題:", + "Upload successful": "上傳成功", + "Uploading...": "正在上傳...", + "item(s) updated": "個項目已更新。" +} diff --git a/src/i18n/zh/inventory.json b/src/i18n/zh/inventory.json index a3118bf..f418c6a 100644 --- a/src/i18n/zh/inventory.json +++ b/src/i18n/zh/inventory.json @@ -3,25 +3,19 @@ "Code": "編號", "Name": "名稱", "Type": "類型", - "Status": "來貨狀態", + "Lot No": "批號", + "Expiry Date": "到期日", + "Warehouse": "倉庫", + "材料": "材料", + "consumable": "消耗品", "Qty": "盤點數量", - "UoM": "單位", - "Approver Pending": "審核待處理", - "Approver Approved": "審核通過", - "Approver Time": "審核時間", "Total need stock take": "總需盤點數量", "Waiting for Approver": "待審核數量", "Total Approved": "已審核數量", "mat": "原料", "variance": "差異", - "Plan Start Date": "計劃開始日期", - "Total Items": "總貨品數量", - "Total Lots": "總批號數量", - "Stock Take Round": "盤點輪次", "ApproverAll": "審核員", - "Stock Take Section (can use , to search multiple sections)": "盤點區域(可使用逗號搜索多個區域)", "Approver All": "審核員全部盤點", - "Variance %": "差異百分比", "fg": "成品", "FG": "成品", "sfg": "半成品", @@ -39,207 +33,71 @@ "WIP": "半成品", "cmb": "消耗品", "nm": "雜項及非消耗品", - "Back to List": "返回列表", - "Start Stock Take Date": "盤點日期", - "Record Status": "記錄狀態", - "Stock take record status updated to not match": "盤點記錄狀態更新為要求重點", "available": "可用", "unavailable": "不可用", - "Issue Qty": "問題數量", "tke": "盤點", "Total Stock Takes": "總盤點數量", "Submit completed: {{success}} success, {{errors}} errors": "提交完成:{{success}} 成功,{{errors}} 失敗", - "No valid input to submit": "請先填寫盤點數量", "Body is unavailable": "輸入不可用", - "Submit All Inputted": "提交所有輸入", - "Submit Bad Item": "提交不良品", - "Remain available Quantity": "剩餘可用數量", - "Submitting...": "提交中...", - "Item-lotNo-ExpiryDate": "貨品-批號-到期日", - "Submit Miss Item": "提交缺貨", - "Confirm": "確認", "Confirm create stock take for all sections?": "確認為所有區域創建盤點?", - "Item-lotNo-ExpiryDate": "貨品-批號-到期日", "not available": "不可用", - "Book Qty": "帳面庫存", - "Submit Quantity": "實際問題數量", "Batch Submit All": "批量提交所有", - "Batch Save All": "批量保存所有", - "Batch Submit All": "批量提交所有", - "Batch Save All": "批量保存所有", "not match": "要求重點", - "Not Match": "要求重點", - "Pass": "已盤點", - "Area": "倉位", - - "Selected Qty": "選擇數量", - "Inventory Difference": "庫存差異", "Show Search Filters": "顯示搜索器", "Hide Search Filters": "隱藏搜索器", - "Stock Take Qty(include Bad Qty)= Available Qty": "盤點數= 可用數", - "Stock Take Qty Data and Variance Analysis": "盤點數數據與差異分析", - "View ReStockTake": "查看重新盤點", - "Stock Take Qty": "盤點數", - "variance Percentage": "差異百分比", "-{{Variance}}≤Variance Percentage ≤{{Variance}} will be filtered out": "-{{Variance}}%≤差異百分比≤{{Variance}}%將被過濾掉", - "Variance filter inclusive only": "反選", "Variance filter strict bounds": "不使用=", - "Variance filter exclusive range hint": "只顯示 -{{value}}% {{op}} 差異% {{op}} {{value}}% 的列(範圍外)", - "Variance filter inclusive range hint": "只顯示 -{{value}}% {{op}} 差異% {{op}} {{value}}% 的列(範圍內)", - "Stock Take Qty": "盤點數", - "Total": "總數", - "Shown": "顯示", "Filtered out": "過濾掉", - "ReStockTake": "重新盤點", - "Stock Taker": "盤點員", "Total Item Number": "貨品數量", - "Total Item Kind Number": "貨品種類數量", "Please enter QTY and Bad QTY": "請輸入盤點數量和不良數量", - "Available QTY cannot be negative": "可用數量不能為負數", "Invalid QTY": "無效的數量", "Stock take qty exceeds maximum": "盤點數量不可超過 999,999,999,999", "Start Time": "開始時間", - "Difference": "差異", "stockTaking": "盤點中", - "Pick Order Code": "提料單編號", - "DO Order Code": "送貨單編號", - "JO Order Code": "工單編號", - "Picker Name": "提料員", - "Rows per page": "每頁行數", - "rejected": "已拒絕", "miss": "缺貨", "bad": "不良", "expiry": "過期", - "open":"開倉", + "open": "開倉", "Bom Req. Qty": "需求數(BOM單位)", - "selected stock take qty": "已選擇盤點數量", - "book qty": "帳面庫存", - "start time": "開始時間", - "end time": "結束時間", - "Control Time": "操作時間", - "Stock Taker": "盤點員", - "Total Item Number": "貨品數量", - "Start Time": "開始時間", - "Difference": "差異", - "stockTaking": "盤點中", - "selected stock take qty": "已選擇盤點數量", - "book qty": "帳面庫存", - "start time": "開始時間", - "end time": "結束時間", "notmatch": "要求重點", - "Only Variance": "僅差異", - "Control Time": "操作時間", - "Batch approver save completed: {{success}} success, {{errors}} errors": "批次審核儲存完成:成功 {{success}} 筆,錯誤 {{errors}} 筆", "pass": "已盤點", "not pass": "不通過", "Available": "可用", "approving": "審核中", "pending": "待處理", - "Last Stock Take Date": "上次盤點日期", - "Remark": "備註", "notMatch": "要求重點", - "notMatch": "要求重點", - "Stock take record saved successfully": "盤點記錄保存成功", - "View Details": "查看詳細", "Input": "輸入", "Something went wrong fetching data in server.": "在伺服器上取得資料時發生錯誤。", - "First": "第一次", - "Save": "保存", - "Second": "第二次", - "Section": "區域", - "No data": "沒有數據", "save": "保存", "AVAILABLE": "可用", "View": "查看", - "Total Sections": "總區域數量", "approver": "審核員", - "Approver": "審核員", "Create Stock Take for All Sections": "為所有區域創建盤點", - "Create Stock Take (Select Sections)": "建立盤點(選擇區域)", "Select stock take sections to create hint": "請選擇要建立新盤點輪次的盤點區域(同一批次將共用同一輪次編號)。", - "Stock take round name": "盤點輪次名稱", - "Stock take round name placeholder": "選填,例如:五月全廠盤點", "Select sections placeholder": "可多選", "Select all sections": "全選區域", "Clear selection": "清除選取", "Selected section count": "已選擇 {{count}} 個區域", - "Floor unassigned": "未設定樓層", - "No stock take sections from warehouse": "目前沒有盤點區域資料", "Expand floor sections": "展開此樓層區域", "Collapse floor sections": "收合此樓層區域", - "Select all on this floor": "全選此樓層 ({{floor}})", - "Deselect all on this floor": "取消全選此樓層 ({{floor}})", - "Creation date": "建立日期", - "Floor area selection header": "{{floor}} 區域選擇 ({{count}} 區域)", - "Search section code or name": "搜索代碼或名稱 (例如 ST-042 或 飲品)", - "Select all sections all floors": "全選區域 (所有樓層)", - "Clear selection all floors": "清除已選 (所有樓層)", - "Total selected sections label": "總計已選擇 :", - "sections unit": "個區域", - "No sections match search": "沒有符合搜索條件的區域", "section": "區域", - "Stock Take Section": "盤點區域", - "Store ID":"樓層", - "Warehouse Location": "倉庫位置", - "UOM": "單位", "First Qty": "第一次盤點數量", "Second Qty": "第二次盤點數量", - "Bad Qty": "不良數量", "Remarks": "備註", "Available Qty": "可用數量", "Sales UoM": "銷售單位", "Stock UoM": "庫存單位", "Available Qty Per Smallest Unit": "可用數量 (基本單位)", "Base UoM": "基本單位", - "Lot No": "批號", - "Expiry Date": "到期日", "No items are selected yet.": "未選擇貨品", "Item selected": "已選擇貨品", - "Warehouse": "倉庫", - "Adjusted By": "處理者", - "Adjustment": "處理", - "Adjustment No": "處理編號", - "Adjustment No, Item, Lot...": "處理編號, 貨品, 批號...", - "After Qty": "處理後數量", - "All": "全部", - "Approved": "已批准", - "Before Qty": "處理前數量", - "Damage": "損壞", - "Date": "日期", - "Item": "貨品", - "Location": "位置", - "Manual": "手動", - "No adjustments found": "未找到調整", - "Pending": "待處理", - "Reason": "原因", - "Rejected": "已拒絕", - "Return": "退貨", - "Search": "搜索", - "Stock Take": "盤點", - "This is a demo with fake data. API integration pending.": "", - "Total Adjustments": "總調整數量", - "Action": "操作", - "Actual Qty": "實際數量", - "Category": "類型", - "Close": "關閉", "Delivery Order": "交貨單", - "Detail": "詳細", - "Handle Remark": "處理備註", - "Issue Detail": "問題詳細", - "Issue No": "問題編號", - "Issue Remark": "問題備註", "Job Order": "工單", - "Mark as Resolved": "標記為已解決", "Material": "物料", - "Miss Qty": "缺貨數量", "UNAVAILABLE": "不可用", "No issues found": "未找到問題", - "Approver stock take record saved successfully": "審核員盤點記錄保存成功", - "Approver input empty; save skipped, row remains pending": "審核員盤點數與不良數皆未輸入,已略過儲存,該列維持待審核", - "No rows loaded; set search criteria and search first": "尚未載入資料,請設定搜索條件並按搜索", "Batch approver save completed: {{success}} success, {{skipped}} skipped, {{errors}} errors": "批次審核儲存完成:成功 {{success}} 筆,略過 {{skipped}} 筆,錯誤 {{errors}} 筆", - "Approver Input": "審核員輸入", "Approve": "審核", "complete": "完成", "completed": "已完成", @@ -247,23 +105,14 @@ "completed date": "完成日期", "completed remarks": "完成備註", "completed status": "完成狀態", - - "Pick Order": "提貨單", "Pick Order, Issue No, Item, Lot...": "揀貨單, 問題編號, 貨品, 批號...", - "Picker": "盤點員", "Refresh": "刷新", - "Required Qty": "所需數量", "Resolved": "已解決", "Total Issues": "總問題數量", - "Inventory Adjustments": "存貨調整", "Inventory Exception Management": "存貨異常管理", - "Stock Take Management": "盤點管理", - "Pick Execution Issues": "揀貨執行問題", "Back": "返回", - "Cancel": "取消", "Confirm Adjustment": "確認調整", "Counted Qty": "盤點數量", - "Item Code": "貨品編號", "Item Name": "貨品名稱", "Perform Stock Take": "執行盤點", "Review Variance": "審核差異", @@ -275,16 +124,6 @@ "Stock take adjustment confirmed! (Demo only)": "盤點調整確認!(僅演示)", "Stock take adjustment has been confirmed successfully!": "盤點調整確認成功!", "System Qty": "系統數量", - "Variance": "差異", - "Processing...": "處理中...", - "Disposing...": "處理中...", - "Batch Disposed All": "批量處理完成", - "Looked": "已查看", - "Disposed": "已處置", - "Miss Item": "缺貨", - "Bad Item": "不良", - "Expiry Item": "過期", - "Batch save completed: {{success}} success, {{errors}} errors": "批量保存完成:{{success}} 成功,{{errors}} 失敗", "Batch Save Inputted": "批量保存已輸入", "Batch Save Completed": "批量保存完成", "Bad Item Handle": "不良品處理", @@ -302,16 +141,6 @@ "Expiry Item Qty": "過期品數量", "Handler": "處理人", "Quantity exceeds available quantity": "數量超過可用數量", - "Please enter a valid quantity": "請輸入有效數量", - "Failed to submit": "提交失敗", - "Unknown error": "未知錯誤", - "Search Criteria": "搜索條件", - "Reset": "重置", - "Defective Qty": "不良數量", - "Remaining Qty": "剩餘數量", - "Lot No.": "批號", - "User ID is required": "需要用戶ID", - "Stock Record": "庫存記錄", "Item-lotNo": "貨品-批號", "In Qty": "入庫數量", @@ -320,13 +149,10 @@ "Balance Qty": "庫存數量", "Start Date": "開始日期", "End Date": "結束日期", - "Loading": "加載中", - "adj": "調整", - "nor": "正常", + "Loading": "加載中", + "adj": "調整", + "nor": "正常", "trf": "轉倉", - - - "Stock transfer successful": "轉倉成功", "Stock transfer merged ambiguous": "已併入較早建立的批次(同批號有多筆可用)", "Stock transfer merged existing lot": "轉倉成功(已併入既有批號)", @@ -341,18 +167,16 @@ "Target Location": "目標倉位", "Original Qty": "原有數量", "Qty To Be Transferred": "待轉數量", + "Remaining Qty": "剩餘數量", "Submit": "確認", - "Printer": "列印機", "Print Qty": "列印數量", "Print": "列印", "Print sent": "已送出列印", "Print failed": "列印失敗", - "Stock Adjustment": "庫存調整", "Edit mode": "編輯模式", "Add entry": "新增倉存", - "productLotNo": "產品批號", "dnNo": "送貨單編號", "Optional - system will generate": "選填,系統將自動生成", @@ -363,6 +187,8 @@ "Reason for removal": "移除原因", "Confirm remove": "確認移除", "Adjusted Qty": "調整後倉存", + "Difference": "差異", + "Action": "操作", "Saved successfully": "儲存成功", "Save failed": "儲存失敗", "Remove": "移除", @@ -376,18 +202,5 @@ "Stop QR Scan": "停止掃碼", "No Data": "沒有數據", "Please set at least one search criterion": "請至少設定一項搜索條件", - "Approver search empty hint": "請設定搜索條件後點擊搜索", - "Confirm batch save approver": "確認批次儲存審核", - "Batch save confirm message": "將對目前列表中的 {{count}} 筆盤點記錄執行批次儲存,是否繼續?", - "Stock take sections in current list": "目前列表涉及 {{count}} 個盤點區域", - "Confirm create stock take": "確認建立盤點", - "Not filled": "(未填寫)", - "Warehouse missing stock take section warn title": "未設定盤點區域的倉庫", - "Warehouse missing stock take section tooltip has": "有 {{count}} 個倉庫未設定盤點區域,點擊查看", - "Warehouse missing stock take section tooltip none": "所有倉庫均已設定盤點區域", - "Warehouse missing stock take section drawer hint": "以下倉庫位置尚未設定盤點區域(ST-xxx),無法納入盤點區域選擇。請至倉庫設定補上。", - "Warehouse missing stock take section go settings": "前往倉庫設定", - "Warehouse missing stock take section showing": "顯示前 {{shown}} 筆,共 {{count}} 筆", - "Warehouse missing stock take section empty": "沒有未設定盤點區域的倉庫" - + "Approver search empty hint": "請設定搜索條件後點擊搜索" } diff --git a/src/i18n/zh/itemPrice.json b/src/i18n/zh/itemPrice.json new file mode 100644 index 0000000..f1ec378 --- /dev/null +++ b/src/i18n/zh/itemPrice.json @@ -0,0 +1,30 @@ +{ + "Price Inquiry": "價格查詢", + "No Purchase Order After 2026-01-01": "在2026-01-01後沒有採購記錄", + "Code": "編號", + "Name": "名稱", + "Searching": "搜索中", + "No item selected": "未選擇貨品", + "Average unit price": "平均單位價格", + "Latest market unit price": "最新市場價格", + "No Import Record": "沒有導入記錄", + "Download Template": "下載範本", + "Downloading...": "正在下載...", + "Upload": "上傳", + "Uploading...": "正在上傳...", + "Download failed": "下載失敗", + "Upload failed": "上傳失敗", + "Upload successful": "上傳成功", + "item(s) updated": "個項目已更新。", + "Upload row errors": "以下行有問題:", + "FG": "成品", + "RM": "原料", + "SFG": "半成品", + "WIP": "半成品", + "CMB": "消耗品", + "NM": "雜項及非消耗品", + "MA": "材料", + "MI": "雜項", + "CO": "消耗品", + "MAT": "材料" +} diff --git a/src/i18n/zh/items.json b/src/i18n/zh/items.json index 6bfdc96..057301c 100644 --- a/src/i18n/zh/items.json +++ b/src/i18n/zh/items.json @@ -1,58 +1,70 @@ -{"Demand Forecast Period": "需求預測期", -"Scheduled At": "預定時間", -"Product Count(s)": "產品數量", -"Product Count": "產品數量", -"Schedule Period": "排程期間", -"Product": "產品", -"Details": "詳情", -"Product Details": "產品詳情", -"Edit Product / Material": "編輯產品 / 材料", -"Product / Material": "產品 / 材料", -"Product / Material Details": "產品 / 材料詳情", - -"Qc items": "QC 項目", -"Qc Category": "質檢模板", -"Name": "名稱", -"name": "名稱", -"description": "描述", -"Type": "類型", -"shelfLife": "保質期", -"remarks": "備註", -"countryOfOrigin": "原產地", -"maxQty": "最大數量", -"Code": "編號", -"code": "編號", -"instruction": "說明", -"lowerLimit": "下限", -"upperLimit": "上限", -"no rows": "沒有資料", -"Save": "儲存", -"Confirm": "確認", -"Cancel": "取消", -"Finished Goods Name": "成品名稱", -"Reset": "重置", -"Search": "搜索", -"Release": "發佈", -"Actions": "操作", -"LocationCode": "預設位置", -"DefaultLocationCode": "預設位置", -"Special Type": "特殊類型", -"None": "正常", -"isEgg": "雞蛋", -"isFee": "費用", -"isBag": "袋子", -"Back": "返回", -"Status": "狀態", -"Complete": "完成", -"Missing Data": "缺少資料", -"Loading QC items...": "正在加載質檢項目...", -"Select a QC template to view items": "選擇質檢模板以查看項目", -"No QC items in this template": "此模板無質檢項目", -"QC Checklist": "質檢項目", -"QC Type": "質檢種類", -"IPQC": "IPQC", -"EPQC": "EPQC" -, - "Item": "貨品", - "Code or name": "編號或名稱" -} \ No newline at end of file +{ + "Demand Forecast Period": "需求預測期", + "Scheduled At": "預定時間", + "Product Count(s)": "產品數量", + "Product Count": "產品數量", + "Schedule Period": "排程期間", + "Product": "產品", + "Details": "詳情", + "Product Details": "產品詳情", + "Edit Product / Material": "編輯物品", + "Product / Material": "物品", + "Product / Material Details": "物品詳情", + "Qc items": "QC 項目", + "Qc Category": "質檢模板", + "Name": "名稱", + "name": "名稱", + "description": "描述", + "Type": "類型", + "shelfLife": "保質期", + "remarks": "備註", + "countryOfOrigin": "原產地", + "maxQty": "最大數量", + "Code": "編號", + "code": "編號", + "instruction": "說明", + "lowerLimit": "下限", + "upperLimit": "上限", + "no rows": "沒有資料", + "Save": "儲存", + "Confirm": "確認", + "Cancel": "取消", + "Finished Goods Name": "成品名稱", + "Reset": "重置", + "Search": "搜索", + "Release": "發佈", + "Actions": "操作", + "LocationCode": "預設位置", + "DefaultLocationCode": "預設位置", + "Special Type": "特殊類型", + "None": "正常", + "isEgg": "雞蛋", + "isFee": "費用", + "isBag": "袋子", + "fg": "成品", + "wip": "半成品", + "mat": "材料", + "cmb": "合併", + "nm": "非物料", + "Back": "返回", + "Status": "狀態", + "Complete": "完成", + "Missing Data": "缺少資料", + "Loading QC items...": "正在加載質檢項目...", + "Select a QC template to view items": "選擇質檢模板以查看項目", + "No QC items in this template": "此模板無質檢項目", + "QC Checklist": "質檢項目", + "QC Type": "質檢種類", + "IPQC": "IPQC", + "EPQC": "EPQC", + "Item": "貨品", + "Code or name": "編號或名稱", + "Product": "物品", + "Item Code": "物品編號", + "Item Name": "物品名稱", + "Sales Qty": "銷售數量", + "Sales UOM": "銷售單位", + "Stock Qty": "庫存數量", + "Uom": "單位", + "Cost (HKD)": "費用 (HKD)" +} diff --git a/src/i18n/zh/jo.json b/src/i18n/zh/jo.json index 708666a..4a2e6e0 100644 --- a/src/i18n/zh/jo.json +++ b/src/i18n/zh/jo.json @@ -1,682 +1,645 @@ { - "Job Order": "工單", - "Please select": "請選擇", - "Edit Job Order Detail": "工單詳情", - "Details": "細節", + "2/F plastic box carton Qty": "2/F 膠茜數目", + "2/F plastic box carton qty count": "2/F 膠茜數目", + "3/F plastic box carton Qty": "3/F 膠茜數目", + "3/F plastic box carton qty count": "3/F 膠茜數目", + "4/F plastic box carton Qty": "4/F 膠茜數目", + "4/F plastic box carton qty count": "4/F 膠茜數目", + "Action": "操作", "Actions": "操作", - "Drink": "飲料", - "Process": "工序", - "Create Job Order": "建立工單", - "Code": "工單編號", - "storing": "待品檢入倉", - "Name": "成品/半成品名稱", - "Picked Qty": "已提料數量", - "Insufficient available quantity on lot (may have been picked by another user)": "掃描的批次已被其他用戶完全提料。請掃描其他批次。", - "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.": "請檢查周圍是否有QR碼,可能是剛剛入庫或轉移入庫或轉移出庫。", - "is expired. Please check around have available QR code or not.": "已過期。請檢查周圍是否有可用的 QR 碼。", - "Confirm All": "確認所有提料", - "Wait Time [minutes]": "等待時間(分鐘)", - "Job Process Status Dashboard": "儀表板 - 工單狀態", - "Time used": "耗時", - "In progress": "進行中", - "Previous page": "上一頁", - "Next page": "下一頁", - "Process page summary": "工序 {{from}}–{{to}} / 共 {{total}} 道", - "Duration hours": "{{count}} 小時", - "Duration minutes": "{{count}} 分鐘", - "Duration seconds": "{{count}} 秒", - "Job process detail: time": "時間", - "Job process detail: process name": "工序", - "Job process detail: equipment": "設備", - "Job process detail: handler": "員工", - "Job process detail mode label": "工序格顯示", - "Product process status": "生產流程狀態", - "stopped": "暫停", - "cancelled": "已取消", - "Job dashboard PP status: pending": "工序待處理", - "Job dashboard PP status: in_progress": "工序進行中", - "Job dashboard PP status: stopped": "工序暫停", - "Job dashboard PP status: completed": "工序完成", - "Job dashboard PP status: cancelled": "工序已取消", - "This lot is rejected, please scan another lot.": "此批次已拒收,請掃描另一個批次。", - "Edit": "改數", - "Code / Lot No": "工單編號/批號", - "Just Complete": "已完成", - "Stock Req. Qty": "需求數", - "Bad Package Qty": "不良包裝數量", - "Progress": "進度", - "Search Job Order/ Create Job Order":"搜索工單/建立工單", - "UoM": "銷售單位", - "Select Another Bag Lot":"選擇另一個包裝袋", - "No": "沒有", - "Powder Mixture": "箱料粉", - "Powder_Mixture": "箱料粉", - "Qty will submit": "提交數量", - "Packaging":"提料中", - "Overall Time Remaining": "總剩餘時間", - "User not found with staffNo:": "用戶不存在", - "Time Remaining": "剩餘時間", - "Base UOM": "基本單位", - "Stock UOM": "庫存單位", - "Over Time": "超時", - "Staff No:": "員工編號:", - "Timer Paused": "計時器已暫停", - "Staff No Required": "員工編號必填", - "Changeover Time (mins)": "生產後轉換時間(分鐘)", - "Update Time Information": "更新時間信息", - "Staff No": "員工編號", - "Status": "工單狀態", - "Lot No.": "批號", - "Pass": "通過", - "Delete Job Order": "刪除工單", - "Cancel Job Order": "取消工單", - "Confirm delete job order": "確認刪除工單", - "Delete job order confirm message": "確定要刪除此工單嗎?此操作無法復原。", - "Confirm cancel job order": "確認取消工單", - "Cancel job order confirm message": "確定要取消此工單嗎?工單將從列表中隱藏。", - "Bom": "半成品/成品編號", - "Release": "放單", - "Pending": "待掃碼", - "Put Awayed": "已上架", - "cancel": "已取消", - "Pending for pick": "待提料", - "Planning": "計劃中", - "Processing": "已開始工序", - "Storing": "成品入倉中", - "Multiplier": "倍數", - "Assume End Time": "預計完成時間", - "Base Qty": "標準生產數", - "Batch Count": "批數", - "Production Priority required!": "生產優先度必填!", - "Update Estimated Production Date": "更新預計生產日期", - "Create": "建立", - "Passed Step": "通過步驟", - "view putaway": "查看上架詳情", - "process epqc": "進行成品檢驗", - "Consumed Qty": "消耗數量", - "Scrap Qty": "損耗數量", + "Actual Pick Qty": "實際提料數量", "Add Bag": "新增包裝袋", - "Balance": "可用數量", - "Submit Bag Consumption": "提交包裝袋消耗", - "Bag Consumption": "包裝袋消耗", - "Bag": "包裝袋", - "Select Bag": "選擇包裝袋", - "scan picked material": "掃碼確認提料", - "escalation processing": "處理上報記錄", - "process stockIn": "進行收貨程序", - "Update Production Priority": "更新生產優先度", - "minutes": "分鐘", - "Start Time": "開始時間", - "release jo": "工單詳情", - "complete jo": "完成工單", - "update success": "成功更新資料", - "Scanned": "已掃碼", - "Scan Status": "掃碼狀態", - "Start Job Order": "開始工單", - "Unable to get user ID": "無法獲取用戶ID", - "Assignment successful": "分配成功", - "Target Production Date": "預計生產日期", - "Production Priority": "生產優先度", + "Add Record": "添加記錄", + "Add Selected Items to Created Items": "將已選擇的物品添加到創建的物品中", + "Add some entries!": "請添加條目", + "All": "全部", + "All Pick Order Lots": "所有提料單批號", + "All floors": "全部樓層", + "All pick orders created successfully": "所有提料單建立成功", + "Are you sure you want to delete this procoess?": "您確定要刪除此工序嗎?", "Assignment failed: ": "分配失敗: ", - "Unknown error": "未知錯誤", - "Unknown error: ": "未知錯誤: ", - "Start Qty": "開始數量", - "End Qty": "結束數量", - "Balance Qty": "剩餘數量", - "Bag Lot Lines": "包裝袋批號明細", + "Assignment successful": "分配成功", + "Assume End Time": "預計完成時間", + "At least one issue must be reported": "至少有一個問題必須報告", + "Auto-refresh every 1 minute": "每1分鐘自動刷新", + "Auto-refresh every 10 minutes": "每10分鐘自動刷新", + "Auto-refresh every 15 minutes": "每15分鐘自動刷新", + "Auto-refresh every 5 minutes": "每5分鐘自動刷新", + "Available Qty": "可用數量", + "Available in warehouse": "在倉庫中可用", + "BOM Description": "BOM 說明", + "BOM Status": "BOM 預備狀況", + "BOM Type": "BOM 類型", + "Back": "返回", + "Back to List": "返回列表", + "Bad Item Qty": "不良物品數量", + "Bad Package Qty": "不良包裝數量", + "Bag": "包裝袋", + "Bag Code": "包裝袋編號", + "Bag Consumption": "包裝袋消耗", "Bag Consumption Records": "包裝袋消耗記錄", "Bag List": "包裝袋列表", - "Time": "時間", + "Bag Lot Lines": "包裝袋批號明細", "Bag Name": "包裝袋名稱", - "Bag Code": "包裝袋編號", - - "Sequence": "序", - "Seq": "步驟", - "SEQ": "步驟", - "Today": "今天", - "Yesterday": "昨天", - "Two Days Ago": "前天", - "Item Code": "物料編號", - "Floor": "樓層", - "Paused": "已暫停", - "paused": "已暫停", - "Total pick orders": "總提料單數量", - "Pause Reason": "暫停原因", "Bag Usage": "包裝袋使用記錄", - "Reason": "原因", - - "update production priority": "更新生產優先序", - "Staff No": "員工編號", - "Please scan staff no": "請掃描員工編號", - "Stock Status": "可提料", - "Total lines: ": "所需貨品項目數量: ", - "Lines with sufficient stock: ": "可提料項目數量: ", - "Lines with insufficient stock: ": "未能提料項目數量: ", - "Item Name": "材料名稱", - "Material Code": "材料編號", - "Select Unit": "選擇單位", - "Job Order Pickexcution": "工單提料", - "Pick Order Detail": "提料單細節", - "Finished Job Order Record": "已完成工單記錄", - "No. of Items to be Picked": "需提料數量", - "No. of Items with Issue During Pick": "問題數量", - "Pick Start Time": "提料開始時間", - "Pick End Time": "提料結束時間", - "FG / WIP Item": "成品/半成品", - "Pick Order No.- Job Order No.- Item": "提料單編號-工單編號-成品/半成品", - "Pick Time Taken (minutes)": "提料時間(分鐘)", - "Index": "編號", - "Route": "路線", - "Qty": "數量", - "Unit": "單位", - "Issue": "問題", - "Location": "位置", - "Scan Result": "掃碼結果", - "Just Completed (workbench): requires valid quantity; expired rows must not use this button.": "工單對料:需要有效數量;過期項目不能使用此按鈕。", - "This is last lot, so no available lot.": "這是最後一個批次,所以沒有可用批次。", - "Expiry Date": "有效期", - "Target Date": "需求日期", - "Lot Required Pick Qty": "批號需求數", - "Available Qty": "可用數量", - - "Job Order Match": "工單對料", - "Lot No": "批號", - "Submit Required Pick Qty": "提交需求數", - "All Pick Order Lots": "所有提料單批號", - "Row per page": "每頁行數", - "No data available": "沒有資料", - "jodetail": "工單細節", - "Start QR Scan": "開始QR掃碼", - "Stop QR Scan": "停止QR掃碼", - "Rows per page": "每頁行數", - "Job Order Item Name": "工單產品名稱", - "Job Order Code": "工單編號", - "View Details": "查看詳情", - "Skip": "跳過", - "packaging": "提料中", - "Handler": "提料員", - "RELEASED": "已放單", - "Released": "已放單", - "COMPLETED": "已完成", - "Now": "現時", - "Last updated": "最後更新", - "Auto-refresh every 5 minutes": "每5分鐘自動刷新", - "Auto-refresh every 10 minutes": "每10分鐘自動刷新", - "Auto-refresh every 15 minutes": "每15分鐘自動刷新", - "Auto-refresh every 1 minute": "每1分鐘自動刷新", - - "completed Job Order pick orders with Matching": "工單已完成提料和對料", - "No completed Job Order pick orders with matching found": "沒有相關記錄", - "completed Job Order pick orders with matching": "工單已完成提料和對料", - "Total": "總數", - "Back to List": "返回列表", - "second Scan Status": "對料狀態", - "Actual Pick Qty": "實際提料數量", - "Processing Status": "處理狀態", - "Lot Availability": "批號可用性", - "Pick Order Id": "提料單編號", - "Pick Order Code": "提料單編號", - "Select a printer": "選擇打印機", - "Please select a printer": "請選擇打印機", - "Next": "下一步", - "Pick Order Conso Code": "提料單組合編號", - "Enter the number of cartons": "請輸入箱數", - "Pick Order Target Date": "提料單需求日期", - "Pick Order Status": "提料單狀態", - "Second Scan Status": "對料狀態", - "Job Order Pick Order Details": "工單提料單詳情", - "Scanning...": "掃碼中", - "Unassigned Job Orders": "未分配工單", - "Please scan the item qr code": "請掃描物料二維碼", - "Please make sure the qty is enough": "物料數量不足", - "Please make sure all required items are picked": "請確保所有物料已被提取", - "Do you want to start job order": "是否開始工單", - "Submit": "提交", - "issue": "問題", - "issue remark": "問題描述", - "handler": "處理者", - "qty is required": "數量是必需的", - "qty is not allowed to be greater than remaining available qty": "數量不能大於剩餘可用數量", - "qty is not allowed to be greater than required qty": "數量不能大於需求數量", - "qty is not allowed to be greater than picked qty": "數量不能大於已提料數量", - "QR code verified.": "QR碼驗證成功。", - "QR code does not match any item in current orders.": "QR碼不匹配當前工單的物料。", - "This form is for reporting issues only. You must report either missing items or bad items.": "此表單僅用於報告問題。您必須報告缺失的物料或不良的物料。", - "Pick Execution Issue Form": "提料執行問題表單", - "Verified Qty": "驗證數量", - "Missing item Qty": "缺失的物料數量", - "Bad Item Qty": "不良的物料數量", - "submit": "提交", - "Issue Remark": "問題描述", - "Received Qty": "接收數量", - "Confirm Lot Substitution": "確認批號替換", - "Processing...": "處理中", + "Balance": "可用數量", + "Balance Qty": "剩餘數量", + "Base Qty": "標準生產數", + "Base UOM": "基本單位", + "Batch Count": "批數", + "BoM Material": "BOM 材料", + "Bom": "BOM", + "Bom Req. Qty": "BOM", + "Bom Uom": "BOM 單位", + "By-product": "副產品", + "COMPLETED": "已完成", + "Cancel": "取消", + "Cancel Job Order": "取消工單", + "Cancel job order confirm message": "確定要取消此工單嗎?工單將從列表中隱藏。", + "Changeover Time": "生產後轉換時間", + "Changeover Time (mins)": "生產後轉換時間(分鐘)", + "Changeover Time (mins)": "生產後轉換時間(分鐘)", + "Clean Record": "清空記錄", + "Code": "工單編號", + "Code / Lot No": "工單編號/批號", "Complete Job Order Record": "已完成工單記錄", - - "Lot Details": "批號細節", - "No lot details available": "沒有批號細節", - "Second Scan Completed": "對料已完成", - "Second Scan Pending": "對料待處理", - "items completed": "物料已完成", + "Complete Step": "完成步驟", + "Completed": "完成", + "Completed Step": "完成步驟", + "Confirm": "確認", + "Confirm All": "確認所有提料", + "Confirm Lot Substitution": "確認批號替換", + "Confirm cancel job order": "確認取消工單", + "Confirm delete job order": "確認刪除工單", + "Consolidate": "整合", + "Consolidated Code": "整合編號", + "Consumable": "消耗品", + "Consumed Qty": "消耗數量", + "Continue": "繼續", + "Count of Job Orders": "工單數量", + "Create": "建立", + "Create Job Order": "建立工單", + "Create New Group": "創建新組", + "Create Pick Order": "創建提料單", + "Create Project": "新增專案", + "Create Task Template": "新增任務範本", + "Created Items": "創建物品", "Current Stock": "當前庫存", - "Lot Actual Pick Qty": "批號實際提料數量", - "Qty Already Picked": "已提料數量", - "Reject": "拒絕", - "Stock Unit": "庫存單位", - "Group": "組", - "Input Equipment is not match with process": "輸入的設備與流程不匹配", - "Item": "成品/半成品", - "Select Date": "選擇日期", - "No Group": "沒有組", - "No created items": "沒有創建物料", - "Order Quantity": "需求數", - "Bom Req. Qty": "BOM", - "Bom Uom": "使用單位", - "Selected": "已選擇", - "Are you sure you want to delete this procoess?": "您確定要刪除此工序嗎?", - "Please select item": "請選擇物料", - "acceptedQty must not greater than": "接受數量不能大於", - "enter a qty": "請輸入數量", - "minimal value is 1": "最小值為1", - "qty": "數量", - "select qc": "選擇QC", - "targetDate": "需求日期", - "type": "類型", - "uom": "單位", - "value must be a number": "值必須是數字", - "update qc info": "更新QC資訊", + "Customer": "客戶", + "Date": "日期", + "Default Warehouse": "默認倉庫", + "Defect": "缺陷", + "Delete Job Order": "刪除工單", + "Delete job order confirm message": "確定要刪除此工單嗎?此操作無法復原。", + "Deliver Order": "送貨訂單", "Delivery Code": "交貨編號", "Delivery Date": "交貨日期", + "Delivery Note Code": "送貨單編號", + "Delivery Order": "送貨訂單", + "Demand Forecast": "需求預測", + "Demand Forecast Setting": "需求預測設定", "Departure Time": "出發時間", - "Shop Address": "商店地址", - "Shop ID": "商店ID", - "Shop Name": "商店名稱", - "Shop PO Code": "商店PO編號", - "Store ID": "商店ID", - "Ticket No.": "票號", - "Truck No.": "車線編號", - "No Item": "沒有物料", - "None": "沒有", - "Add Selected Items to Created Items": "將已選擇的物料添加到創建的物料中", - "All pick orders created successfully": "所有提料單建立成功", - "Consumable": "消耗品", - "Create New Group": "創建新組", - "Create Pick Order": "創建提料單", - "Created Items": "創建物料", + "Describe the issue with bad items": "描述不良物品的問題", + "Description": "描述", + "Detail Scheduling": "詳細排程", + "Details": "詳情", + "Do you want to start job order": "是否開始工單", + "Download Excel": "下載 Excel", + "Drink": "飲料", + "Duration hours": "{{count}} 小時", + "Duration minutes": "{{count}} 分鐘", + "Duration seconds": "{{count}} 秒", + "Edit": "改數", + "Edit Equipment": "設備詳情", + "Edit Equipment Type": "設備類型詳情", + "Edit Job Order": "工單詳情", + "Edit Job Order Detail": "工單詳情", "End Product": "成品", + "End Qty": "結束數量", + "Enter all floors plastic box carton qty": "請輸入各樓層膠茜數目", + "Enter bad item quantity (required if no missing items)": "請輸入不良物品數量(如果沒有缺失物品)", + "Enter missing quantity (required if no bad items)": "請輸入缺失物品數量(如果沒有不良物品)", + "Enter plastic box carton qty": "請輸入膠茜數目", + "Enter print quantity": "請輸入打印數量", + "Enter the number of cartons": "請輸入箱數", + "Enter the number of cartons: ": "請輸入箱數:", + "Equipment": "設備", + "Equipment Code": "設備編號", + "Equipment Details": "設備詳情", + "Equipment Name and Code": "設備名稱及編號", + "Equipment Type Details": "設備類型詳情", + "EquipmentType-EquipmentName-Code": "設備類型-設備名稱-編號", + "Escalation History": "升級歷史", + "Escalation Info": "升級信息", + "Escalation Result": "升級結果", + "Estimated Production Date": "預計生產日期", + "Exclude Date": "排除日期", + "Executing": "執行中", + "Expected Lot:": "預期批號:", + "Expiry Date": "有效期", + "Exporting...": "匯出中...", + "FG": "成品", + "FG & Material Demand Forecast Detail": "成品及材料需求預測詳情", + "FG / WIP Item": "成品/半成品", + "FG Production Schedule": "FG 生產排程", "Failed to create group": "創建組失敗", + "Failed to load plastic box carton qty dashboard": "載入膠茜數目使用數量失敗,請稍後再試。", + "Failed to submit scan data. Please try again.": "掃碼數據提交失敗. 請重試.", + "Finish": "完成", + "Finished Good Order": "成品出倉", + "Finished Goods Name": "成品名稱", + "Finished Job Order Record": "已完成工單記錄", + "Finished lines": "完成行", "First created group": "第一次創建組", + "Floor": "樓層", + "Group": "組", + "Handled By": "處理者", + "Handler": "提料員", + "Idle": "閒置", + "If you confirm, the system will:": "如果您確認,系統將:", + "Import Testing": "匯入測試", + "In Progress": "進行中", + "In progress": "進行中", + "In_Progress": "進行中", + "Index": "編號", + "Input Equipment is not match with process": "輸入的設備與流程不匹配", + "Insufficient available quantity on lot (may have been picked by another user)": "掃描的批次已被其他用戶完全提料。請掃描其他批次。", + "Invalid Stock In Line Id": "無效庫存行ID", "Invalid date format": "日期格式無效", - "Item already exists in created items": "物料已存在於創建的物料中", - "Job Order not found or has no items": "工單不存在或沒有物料", + "Inventory": "庫存", + "Is Dark": " 顔色深淺度", + "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原 | 時間順序 | 複雜度", + "Is Dense": "濃淡", + "Is Float": "浮沉", + "Issue": "問題", + "Issue Remark": "問題備註", + "Item": "成品/半成品", + "Item Code": "物品編號", + "Item Name": "物品名稱", + "Item already exists in created items": "物品已存在於創建的物品中", + "Items": "物品", + "Jo Pick Order Detail": "工單提料詳情", + "Job Order": "工單", + "Job Order Code": "工單編號", + "Job Order Info": "工單信息", + "Job Order Item Name": "工單產品名稱", + "Job Order Match": "工單對料", + "Job Order No.": "工單編號", + "Job Order Pick Execution": "工單提料", + "Job Order Pick Order Details": "工單提料單詳情", + "Job Order Pickexcution": "工單提料", + "Job Order Qty": "工單數量", + "Job Order Type": "工單類型", + "Job Order not found or has no items": "工單不存在或沒有物品", + "Job Process Status Dashboard": "儀表板 - 工單狀態", + "Job Type": "工單類型", + "Job dashboard PP status: cancelled": "工序已取消", + "Job dashboard PP status: completed": "工序完成", + "Job dashboard PP status: in_progress": "工序進行中", + "Job dashboard PP status: pending": "工序待處理", + "Job dashboard PP status: stopped": "工序暫停", + "Job process detail mode label": "工序格顯示", + "Job process detail: equipment": "設備", + "Job process detail: handler": "員工", + "Job process detail: process name": "工序", + "Job process detail: time": "時間", + "Just Complete": "已完成", + "Just Completed (workbench): requires valid quantity; expired rows must not use this button.": "工單對料:需要有效數量;過期項目不能使用此按鈕。", + "Last 7 days": "最近7天", + "Last updated": "最後更新", "Latest created group": "最新創建組", + "Lines with insufficient stock: ": "未能提料項目數量: ", + "Lines with sufficient stock: ": "可提料項目數量: ", "Loading...": "加載中", - "Material": "物料", - "No results found": "沒有結果", - "Please enter at least code or name": "請輸入至少編號或名稱", - "Please enter quantity for all selected items": "請輸入所有已選擇的物料的數量", - "Please select at least one item to submit": "請選擇至少一個物料提交", - "Please select group and enter quantity for all selected items": "請選擇組並輸入所有已選擇的物料的數量", - "Please select group for all selected items": "請選擇組對所有已選擇的物料", - "Please select product type": "請選擇產品類型", - "Please select target date": "請選擇需求日期", - "Please select type": "請選擇類型", - "Product Type": "產品類型", - "Reset": "重置", - "Search": "搜索", - "Search Criteria": "搜索條件", - "Search Items": "搜索物料", - "Search Results": "搜索結果", - "Selected items will join above created group": "已選擇的物料將加入上述創建的組", - "reset": "重置", + "Location": "位置", + "Lot Actual Pick Qty": "批號實際提料數量", + "Lot Availability": "批號可用性", + "Lot Details": "批號細節", + "Lot No": "批號", + "Lot No.": "批號", + "Lot Number Mismatch": "批號不匹配", + "Lot Required Pick Qty": "批號需求數", "Lot has been rejected and marked as unavailable.": "批號已被拒絕並標記為不可用。", + "LotNo": "批號", + "Mail": "郵件", + "Management Job Order": "管理工單", "Manual Input": "手動輸入", - "Partial quantity submitted. Please submit more or complete the order.": "", - "Pick order completed successfully!": "提料單完成成功!", - "Please finish QR code scan and pick order.": "請完成QR碼掃描和提料單。", - "Please submit the pick order.": "請提交提料單。", - "Processing QR code...": "處理QR碼...", - "QR Code Scan for Lot": "批號QR碼掃描", - "QR Scan Result:": "QR掃描結果:", - "The input is not the same as the expected lot number.": "輸入的批號與預期批號不同。", - "This order is insufficient, please pick another lot.": "此訂單不足,請選擇另一個批號。", - "Verified successfully!": "驗證成功!", - "At least one issue must be reported": "至少有一個問題必須報告", - "Available in warehouse": "在倉庫中可用", - "Remaining Available Qty": "剩餘可用數量", - "Describe the issue with bad items": "描述不良物料的問題", - "Enter bad item quantity (required if no missing items)": "請輸入不良物料數量(如果沒有缺失物料)", - "Enter missing quantity (required if no bad items)": "請輸入缺失物料數量(如果沒有不良物料)", + "Matching Stock": "工單對料", + "Material": "材料", + "Material Code": "材料編號", + "Material Pick Status": "物料提料狀態", "Max": "最大", - "Note:": "注意:", - "Qty is not allowed to be greater than required qty": "數量不能大於所需數量", - "Qty is required": "數量是必需的", - "Still need to pick": "仍需提料", - "Verified quantity cannot exceed received quantity": "驗證數量不能超過接收數量", - "submitting": "提交中", - "Consolidate": "整合", - "Consolidated Code": "整合編號", - "Items": "物料", - "Released By": "發佈者", - "Product": "產品", - "Target Date From": "需求日期從", - "Target Date To": "需求日期到", - "Type": "類型", - "Confirm": "確認", - "Expected Lot:": "預期批號:", - "If you confirm, the system will:": "如果您確認,系統將:", - "Lot Number Mismatch": "批號不匹配", - "Scanned Lot:": "掃描批號:", - "The scanned item matches the expected item, but the lot number is different. Do you want to proceed with this different lot?": "掃描的物料與預期物料匹配,但批號不同。您想繼續使用不同的批號嗎?", - "Update your suggested lot to the this scanned lot": "更新您建議的批號為此掃描的批號", - "Default Warehouse": "默認倉庫", - "LotNo": "批號", - "No Warehouse": "沒有倉庫", - "Please scan warehouse qr code.": "請掃描倉庫QR碼。", - "Po Code": "PO編號", - "Putaway Detail": "入庫細節", - "Select warehouse": "選擇倉庫", - "Supplier": "供應商", - "acceptedQty": "接受數量", - "bind": "綁定", - "expiryDate": "有效期", - "itemName": "產品名稱", - "itemNo": "成品編號", - "not default warehosue": "不是默認倉庫", - "printQty": "打印數量", - "productionDate": "生產日期", - "warehouse": "倉庫", - "Add Record": "添加記錄", - "Add some entries!": "請添加條目", - "Clean Record": "清空記錄", - "Escalation History": "升級歷史", - "Escalation Info": "升級信息", - "Escalation Result": "升級結果", - "QC Info": "QC信息", - "acceptQty": "接受數量", - "acceptQty must not greater than": "接受數量不能大於", - "escalation": "升級", - "failedQty": "失敗數量", - "qcResult": "QC結果", - "remarks": "備註", - "supervisor": "主管", + "Missing item Qty": "缺少物品數量", + "Missing items": "缺少物品", + "Multiplier": "倍數", + "N/A": "不適用", + "Name": "成品/半成品名稱", + "Next": "下一步", + "Next page": "下一頁", + "No": "沒有", + "No Group": "沒有組", + "No Item": "沒有物品", "No Qc": "沒有QC", - "acceptedWeight": "接受重量", - "receivedQty": "接收數量", - "stock in information": "庫存信息", "No Uom": "沒有單位", - "Print Pick Record": "打印版頭紙", - "Printed Successfully.": "成功列印", - "Submit All Scanned": "提交所有已掃描項目", - "Submitting...": "提交中...", - "is unavable. Please check around have available QR code or not.": "此批號不可用,請檢查周圍是否有可用的 QR 碼。", - "COMPLETED": "已完成", - "success": "成功", - "Total (Verified + Bad + Missing) must equal Required quantity": "驗證數量 + 不良數量 + 缺失數量必須等於需求數量", - "BOM Status": "材料預備狀況", - "Job Order Type": "工單類型", - "Estimated Production Date": "預計生產日期", - "Plan Start": "預計生產日期", - "Remember plan start as default": "記住為預設日期", - "Plan Start From": "預計生產日期", - "Delivery Note Code": "送貨單編號", - "Plan Start To": "預計生產日期至", - "By-product": "副產品", - "Complete Step": "完成步驟", - "Defect": "缺陷", - "Output from Process": "流程輸出", - "Quantity": "數量", - "Scrap": "廢料", - "Unit": "單位", - "Back to List": "返回列表", - "Production Output Data Entry": "生產輸出數據輸入", - "Step": "步驟", - "Quality Check": "品質檢查", - "Action": "操作", - "Changeover Time (mins)": "生產後轉換時間(分鐘)", - "Completed": "完成", - "completed": "完成", - "Date": "日期", - "Failed to submit scan data. Please try again.": "掃碼數據提交失敗. 請重試.", - "In Progress": "進行中", - "Is Dark": " 顔色深淺度", - "Is Dense": "濃淡", - "Is Float": "浮沉", - "Job Order Code": "工單編號", + "No Warehouse": "沒有倉庫", + "No chart data": "沒有圖表資料", + "No completed Job Order pick orders with matching found": "沒有相關記錄", + "No created items": "沒有創建物品", + "No data available": "沒有資料", + "No data found": "沒有找到資料", + "No lot details available": "沒有批號細節", + "No results found": "沒有結果", + "No. of Items to be Picked": "需提料數量", + "No. of Items with Issue During Pick": "問題數量", + "None": "沒有", + "Note:": "注意:", + "Now": "現時", + "Number must be at least 1": "數字必須至少為1", + "Number of cartons": "箱數", + "Open 挑號 QR 碼 on a pick line first, then scan {2fic} to use manual lot substitution.": "請先點選一列並開啟「挑號 QR 碼」,再掃描 {2fic} 以使用手動換批。", "Operator": "操作員", + "Operator KPI": "操作員KPI", + "Operator KPI Dashboard": "儀表板 - 操作員KPI概覽", + "Operator Name & No.": "操作員名稱及編號", + "Order Complete": "訂單完成", + "Order Quantity": "需求數", + "Other": "其他", "Output Qty": "輸出數量", + "Output from Process": "流程輸出", + "Over Time": "超時", + "Overall Time Remaining": "總剩餘時間", + "Overview": "總覽", + "Packaging": "提料中", + "Partial quantity submitted. Please submit more or complete the order.": "已提料部分數量。請提交更多或完成訂單。", + "Pass": "通過", + "Passed Step": "通過步驟", + "Pause": "暫停", + "Pause Reason": "暫停原因", + "Paused": "已暫停", "Pending": "待處理", - "pending": "待處理", - - "Please scan equipment code (optional if not required)": "請掃描設備編號(可選)", + "Pending for pick": "待提料", + "Pick End Time": "提料結束時間", + "Pick Execution Issue Form": "提料問題表單", + "Pick Order": "提料單", + "Pick Order Code": "提料單編號", + "Pick Order Conso Code": "提料單組合編號", + "Pick Order Detail": "提料單細節", + "Pick Order Id": "提料單編號", + "Pick Order No.- Job Order No.- Item": "提料單編號-工單編號-成品/半成品", + "Pick Order Status": "提料單狀態", + "Pick Order Target Date": "提料單需求日期", + "Pick Start Time": "提料開始時間", + "Pick Time Taken (minutes)": "提料時間(分鐘)", + "Pick order completed successfully!": "提料單完成成功!", + "Picked Qty": "已提料數量", + "Plan Start": "預計生產日期", + "Plan Start From": "預計生產日期", + "Plan Start To": "預計生產日期至", + "Planning": "計劃中", + "Plastic box carton Qty": "膠茜數目", + "Plastic box carton qty dashboard": "膠茜數目使用數量", + "Plastic box carton qty multi period report": "膠茜數目使用數量_多時段報表", + "Plastic box carton qty report last 7 days": "膠茜數目使用數量(最近7天)", + "Plastic box carton qty report this month": "膠茜數目使用數量(本月)", + "Plastic box carton qty report this year": "膠茜數目使用數量(本年)", + "Plastic box carton qty usage": "膠茜數目", + "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.": "請檢查周圍是否有QR碼,可能是剛剛入庫或轉移入庫或轉移出庫。", + "Please enter at least code or name": "請輸入至少編號或名稱", + "Please enter quantity for all selected items": "請輸入所有已選擇的物品的數量", + "Please finish QR code scan and pick order.": "請完成QR碼掃描和提料單。", + "Please make sure all required items are picked": "請確保所有物品已被提取", + "Please make sure the qty is enough": "物品數量不足", "Please scan equipment code": "請掃描設備編號", - "Equipment Code": "設備編號", + "Please scan equipment code (optional if not required)": "請掃描設備編號(可選)", "Please scan operator code": "請掃描操作員編號", "Please scan operator code first": "請先掃描操作員編號", + "Please scan staff no": "請掃描員工編號", + "Please scan the item qr code": "請掃描物品二維碼", + "Please scan warehouse qr code.": "請掃描倉庫QR碼。", + "Please select": "請選擇", + "Please select a printer": "請選擇打印機", + "Please select a printer first": "請先選擇打印機", + "Please select at least one item to submit": "請選擇至少一個物品提交", + "Please select group and enter quantity for all selected items": "請選擇組並輸入所有已選擇的物品的數量", + "Please select group for all selected items": "請選擇組對所有已選擇的物品", + "Please select item": "請選擇物品", + "Please select product type": "請選擇產品類型", + "Please select target date": "請選擇需求日期", + "Please select type": "請選擇類型", + "Please submit the pick order.": "請提交提料單。", + "Po Code": "PO編號", + "Powder Mixture": "箱料粉", + "Powder_Mixture": "箱料粉", + "Previous page": "上一頁", + "Print Pick Record": "打印版頭紙", + "Print Quantity": "打印數量", + "Printed Successfully.": "成功列印", + "Printer": "打印機", + "Process": "工序", + "Process page summary": "工序 {{from}}–{{to}} / 共 {{total}} 道", + "Processing": "已開始工序", + "Processing QR code...": "處理QR碼...", + "Processing Status": "處理狀態", + "Processing Time": "生產時間", "Processing Time (mins)": "步驟時間(分鐘)", + "Processing...": "處理中", + "Product": "產品", + "Product Type": "產品類型", + "Product process status": "生產流程狀態", + "Production": "生產流程", + "Production Date": "生產日期", + "Production Equipment Status Dashboard": "儀表板 - 生產設備最新狀態", + "Production Output Data": "生產輸出數據", + "Production Output Data Entry": "生產輸出數據輸入", + "Production Priority": "生產優先序", + "Production Priority required!": "生產優先度必填!", + "Production Process": "工藝流程", "Production Process Information": "生產流程信息", + "Production Process Line Remark": "工藝明細", "Production Process Steps": "生產流程步驟", + "Production Time Remaining": "生產剩餘時間", + "Production date": "生產日期", + "Progress": "進度", + "Project": "專案", + "Projects": "專案", + "Purchase Order": "採購單", + "Put Away": "上架", + "Put Away Scan": "上架掃碼", + "Put Awayed": "已上架", + "Putaway Detail": "入庫細節", + "QC Category": "QC品檢模板", + "QC Check Item": "QC品檢項目", + "QC Check Template": "QC檢查模板", + "QC Info": "QC信息", + "QR Code Handle": "二維碼列印及下載", + "QR Code Scan for Lot": "批號QR碼掃描", + "QR Scan Result:": "QR掃描結果:", + "QR code does not match any item in current orders.": "QR碼不匹配當前工單的物品。", + "QR code verified.": "QR碼驗證成功。", + "Qc Item": "QC 項目", + "Qty": "數量", + "Qty Already Picked": "已提料數量", + "Qty cannot be negative": "數量不可為負數", + "Qty is not allowed to be greater than required qty": "數量不能大於所需數量", + "Qty is required": "數量是必需的", + "Qty must be a whole number": "請輸入整數(不可為小數)", + "Qty will submit": "提交數量", + "Quality Check": "品質檢查", + "Quantity": "數量", + "R&D": "研發", + "RELEASED": "已放單", + "Reason": "原因", + "Received Qty": "接收數量", + "Reject": "拒絕", + "Release": "放單", + "Released": "已放單", + "Released By": "發佈者", + "Remaining Available Qty": "剩餘可用數量", + "Remaining Time (min)": "剩餘時間(分鐘)", + "Remark": "明細", + "Remember plan start as default": "記住為預設日期", + "Repair": "維修", + "Req. Qty": "需求數量", + "Required Qty": "需求數量", + "Reset": "重置", + "Route": "路線", + "Router": "執貨路線", + "Row per page": "每頁行數", + "Rows per page": "每頁行數", + "SEQ": "步驟", + "STF": "員工餐", + "Save": "儲存", "Scan Operator & Equipment": "掃描操作員和設備", + "Scan Result": "掃碼結果", + "Scan Status": "掃碼狀態", + "Scanned": "已掃碼", + "Scanned Lot:": "掃描批號:", + "Scanning...": "掃碼中", + "Scrap": "廢料", + "Scrap Qty": "損耗數量", + "Search": "搜索", + "Search Criteria": "搜索條件", + "Search Items": "搜索物品", + "Search Job Order/ Create Job Order": "搜索工單/建立工單", + "Search Results": "搜索結果", + "Second Scan Completed": "對料已完成", + "Second Scan Pending": "對料待處理", + "Second Scan Status": "對料狀態", + "Select Another Bag Lot": "選擇另一個包裝袋", + "Select Bag": "選擇包裝袋", + "Select Date": "選擇日期", + "Select Printer": "選擇打印機", + "Select Unit": "選擇單位", + "Select a printer": "選擇打印機", + "Select warehouse": "選擇倉庫", + "Selected": "已選擇", + "Selected items will join above created group": "已選擇的物品將加入上述創建的組", + "Seq": "步驟", + "Seq No": "加入步驟", + "Seq No Remark": "序號明細", "Seq:": "步驟", + "Sequence": "序", + "Setup Time": "生產前預備時間", "Setup Time (mins)": "生產前預備時間(分鐘)", + "Shop Address": "商店地址", + "Shop ID": "商店ID", + "Shop Name": "商店名稱", + "Shop PO Code": "商店PO編號", + "Sign out": "登出", + "Skip": "跳過", + "Staff No": "員工編號", + "Staff No Required": "員工編號必填", + "Staff No:": "員工編號:", "Start": "開始", + "Start Job Order": "開始工單", "Start QR Scan": "開始掃碼", - "Status": "狀態", - "in_progress": "進行中", - "In_Progress": "進行中", - "inProgress": "進行中", - + "Start Qty": "開始數量", + "Start Scan": "開始掃碼", + "Start Time": "開始時間", + "Status": "狀態", + "Step": "步驟", + "Step Information": "步驟信息", "Step Name": "名稱", + "Still need to pick": "仍需提料", + "Stock Available": "庫存數", + "Stock Req. Qty": "需求數", + "Stock Status": "庫存狀態", + "Stock UOM": "庫存單位", + "Stock Unit": "庫存單位", + "Stop": "停止", "Stop QR Scan": "停止掃碼", + "Stop Scan": "停止掃碼", + "Store ID": "商店ID", + "Storing": "成品入倉中", + "Submit": "提交", "Submit & Start": "提交並開始", + "Submit All Scanned": "提交所有已掃描項目", + "Submit Bag Consumption": "提交包裝袋消耗", + "Submit Required Pick Qty": "提交需求數", + "Submitting...": "提交中...", + "Summary": "彙總", + "Supplier": "供應商", + "Target Date": "需求日期", + "Target Date From": "需求日期從", + "Target Date To": "需求日期到", + "Target Production Date": "目標生產日期", + "Task Template": "任務範本", + "The input is not the same as the expected lot number.": "輸入的批號與預期批號不同。", + "The scanned item matches the expected item, but the lot number is different. Do you want to proceed with this different lot?": "掃描的物品與預期物品匹配,但批號不同。您想繼續使用不同的批號嗎?", + "This form is for reporting issues only. You must report either missing items or bad items.": "此表單僅用於報告問題。您必須報告缺少的物品或不良物品。", + "This is last lot, so no available lot.": "這是最後一個批次,所以沒有可用批次。", + "This lot is rejected, please scan another lot.": "此批次已拒收,請掃描另一個批次。", + "This month": "本月", + "This order is insufficient, please pick another lot.": "此訂單不足,請選擇另一個批號。", + "This year": "本年", + "Ticket No.": "票號", + "Time": "時間", + "Time Information(mins)": "時間信息(分鐘)", + "Time Remaining": "剩餘時間", + "Time used": "耗時", + "Timer Paused": "計時器已暫停", + "Today": "今天", + "Total": "總數", + "Total (Verified + Bad + Missing) must equal Required quantity": "總數必須等於需求數量", + "Total Processing Time": "總生產時間", "Total Steps": "總步驟數", - "Unknown": "", - "Job Type": "工單類型", + "Total lines: ": "所需貨品項目數量: ", + "Total pick orders": "總提料單數量", + "Total plastic box carton qty": "總膠茜數目", + "Total processes": "總流程數", + "Truck No.": "車線編號", + "Two Days Ago": "前天", + "Type": "類型", + "Unable to get user ID": "無法獲取用戶ID", + "Unassigned Job Orders": "未分配工單", + "Unit": "單位", + "Unknown": "未知", + "Unknown error": "未知錯誤", + "Unknown error: ": "未知錯誤: ", + "UoM": "銷售單位", + "Update Estimated Production Date": "更新預計生產日期", "Update Job Order": "完成工單", - "Production Date":"生產日期", - "Jo Pick Order Detail":"工單提料詳情", - "WIP": "半成品", - "R&D": "研發", - "STF": "員工餐", - "Other": "其他", + "Update Production Priority": "更新生產優先度", + "Update Time Information": "更新時間信息", + "Update your suggested lot to the this scanned lot": "更新您建議的批號為此掃描的批號", + "User not found with staffNo:": "用戶不存在", "Validation failed. Please check operator and equipment.": "驗證失敗. 請檢查操作員和設備.", + "Verified Qty": "驗證數量", + "Verified quantity cannot exceed received quantity": "驗證數量不能超過接收數量", + "Verified successfully!": "驗證成功!", "View": "查看", - "Back": "返回", - "N/A": "不適用", - "BoM Material": "材料清單", - "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原 | 時間順序 | 複雜度", - "Enter the number of cartons: ": "請輸入箱數:", - "Number of cartons": "箱數", - "You need to enter a number": "您需要輸入一個數字", - "Number must be at least 1": "數字必須至少為1", - - "Job Order Info": "工單信息", - "Matching Stock": "工單對料", - "No data found": "沒有找到資料", - "Print Quantity": "打印數量", - "Select Printer": "選擇打印機", - "Printer": "打印機", - "Enter print quantity": "請輸入打印數量", - "Production Priority": "生產優先序", - "Please select a printer first": "請先選擇打印機", - "Production Process": "工藝流程", - "Production Process Line Remark": "工藝明細", - "Remark": "明細", - "Req. Qty": "需求數量", - "Seq No": "加入步驟", - "Seq No Remark": "序號明細", - "Stock Available": "庫存數", - "Stock Status": "庫存狀態", - "Target Production Date": "目標生產日期", - "Description": "描述", - - "id": "ID", - "Finished lines": "完成行", - "Invalid Stock In Line Id": "無效庫存行ID", - "Production date": "生產日期", - "Required Qty": "需求數量", - "Total processes": "總流程數", "View Details": "查看詳情", - "view stockin": "品檢", - "Completed Step": "完成步驟", - "Continue": "繼續", - "Executing": "執行中", - "Order Complete": "訂單完成", - "Pause": "暫停", - "Production Output Data": "生產輸出數據", - "Step Information": "步驟信息", - "Stop": "停止", - "Demand Forecast Setting": "需求預測設定", - "EquipmentType-EquipmentName-Code": "設備類型-設備名稱-編號", - - "Equipment": "設備", - "Time Information(mins)": "時間信息(分鐘)", - "Processing Time": "生產時間", - "Setup Time": "生產前預備時間", - "Changeover Time": "生產後轉換時間", + "View item In-out And inventory Ledger": "查看物品出入庫及庫存日誌", + "WIP": "半成品", + "Wait Time [minutes]": "等待時間(分鐘)", "Warehouse": "倉庫", - "Supplier": "供應商", - "Purchase Order":"採購單", - "Demand Forecast":"需求預測", - "Pick Order": "提料單", - "Deliver Order":"送貨訂單", - "Project":"專案", - "Product":"產品", - "Material":"材料", - "mat":"原料", + "Yesterday": "昨天", + "You need to enter a number": "您需要輸入一個數字", + "acceptQty": "接受數量", + "acceptQty must not greater than": "接受數量不能大於", + "acceptedQty": "接受數量", + "acceptedQty must not greater than": "接受數量不能大於", + "acceptedWeight": "接受重量", + "bind": "綁定", + "bomWarn_close": "關閉", + "bomWarn_copyAll": "複製清單", + "bomWarn_empty": "目前沒有 BOM 數據問題。", + "bomWarn_issue_MISSING_BOM_CODE": "BOM 編號為空", + "bomWarn_issue_MISSING_BOM_NAME": "BOM 名稱為空", + "bomWarn_issue_MISSING_ITEM": "關聯物品不存在或已刪除", + "bomWarn_issue_MISSING_SALES_UOM": "物品缺少銷售單位", + "bomWarn_issue_MISSING_STOCK_UOM": "物品缺少庫存單位,品檢可能失敗", + "bomWarn_issue_MISSING_STOCK_UOM_CONVERSION": "庫存單位缺少或已刪除 UOM 換算,品檢可能失敗", + "bomWarn_issue_MISSING_UOM_CONVERSION": "銷售單位缺少或已刪除 UOM 換算", + "bomWarn_loadFailed": "無法載入 BOM 問題清單,請稍後再試。", + "bomWarn_refresh": "重新檢查", + "bomWarn_refreshing": "檢查中…", + "bomWarn_rowBomId": "BOM ID", + "bomWarn_rowItemId": "物品 ID", + "bomWarn_title": "BOM 數據問題", + "bomWarn_tooltipHas": "有 {{count}} 筆 BOM 數據問題", + "bomWarn_tooltipNone": "沒有 BOM 數據問題", + "cancel": "已取消", + "cancelled": "已取消", + "complete jo": "完成工單", + "completed": "完成", + "completed Job Order pick orders with Matching": "工單已完成提料和對料", + "completed Job Order pick orders with matching": "工單已完成提料和對料", "consumables": "消耗品", - "non-consumables": "非消耗品", - "fg": "成品", - "sfg": "半成品", - "item": "貨品", - "FG":"成品", - "FG & Material Demand Forecast Detail":"成品及材料需求預測詳情", - "View item In-out And inventory Ledger":"查看物料出入庫及庫存日誌", - "Delivery Order":"送貨訂單", - "Detail Scheduling":"詳細排程", - "Customer":"客戶", - "qcItem":"品檢項目", - "QC Check Item":"QC品檢項目", - "QC Category":"QC品檢模板", - "qcCategory":"品檢模板", - "QC Check Template":"QC檢查模板", - "QR Code Handle":"二維碼列印及下載", - "Mail":"郵件", - "Import Testing":"匯入測試", - "Overview": "總覽", - "Projects": "專案", - "Create Project": "新增專案", - "Task Template": "任務範本", - "Create Task Template": "新增任務範本", - "Qc Item": "QC 項目", - "FG Production Schedule": "FG 生產排程", - "Inventory": "庫存", - "scheduling":"排程", - "settings": "設定", - "items": "物料", - "edit":"編輯", - "Edit Equipment Type":"設備類型詳情", - "Edit Equipment":"設備詳情", - "equipmentType":"設備類型", - "Description":"描述", - "Details": "詳情", - "Equipment Type Details":"設備類型詳情", - "Save":"儲存", - "Cancel":"取消", - "Equipment Details":"設備詳情", - "Exclude Date":"排除日期", - "Finished Goods Name":"成品名稱", "create": "新增", + "edit": "編輯", + "enter a qty": "請輸入數量", + "equipmentType": "設備類型", + "escalation": "升級", + "escalation processing": "處理上報記錄", + "expiryDate": "有效期", + "failedQty": "失敗數量", + "fg": "成品", + "finishedGood": "成品", + "handler": "處理者", "hr": "小時", "hrs": "小時", + "id": "ID", + "inProgress": "進行中", + "in_progress": "進行中", + "is expired. Please check around have available QR code or not.": "已過期。請檢查周圍是否有可用的 QR 碼。", + "is unavable. Please check around have available QR code or not.": "此批號不可用,請檢查周圍是否有可用的 QR 碼。", + "issue": "問題", + "issue remark": "問題描述", + "item": "貨品", + "itemName": "產品名稱", + "itemNo": "成品編號", + "items": "物品", + "items completed": "物品已完成", + "jodetail": "工單細節", + "mat": "原料", "min": "分鐘", + "minimal value is 1": "最小值為1", "mins": "分鐘", - "Edit Job Order": "工單詳情", - "Production": "生產流程", - "Put Away": "上架", - "Put Away Scan": "上架掃碼", - "Finished Good Order": "成品出倉", - "finishedGood": "成品", - "Router": "執貨路線", -"Equipment Name and Code": "設備名稱及編號", -"Remaining Time (min)": "剩餘時間(分鐘)", -"Idle": "閒置", -"Repair": "維修", -"Production Equipment Status Dashboard": "儀表板 - 生產設備最新狀態", -"Operator KPI Dashboard": "儀表板 - 操作員KPI概覽", - "Start Scan": "開始掃碼", - "Stop Scan": "停止掃碼", - "Operator Name & No.": "操作員名稱及編號", - "Count of Job Orders": "工單數量", - "Total Processing Time": "總生產時間", - "Material Pick Status": "物料提料狀態", - "Job Order Qty": "工單數量", - "Sign out": "登出", - "Job Order No.": "工單編號", - "Operator KPI": "操作員KPI", - "FG / WIP Item": "成品/半成品", - "Production Time Remaining": "生產剩餘時間", - "Process": "工序", - "Start": "開始", - "This form is for reporting issues only. You must report either missing items or bad items.": "此表單僅用於報告問題。您必須報告缺少的物品或不良物品。", - "Pick Execution Issue Form": "提料問題表單", - "Missing items": "缺少物品", - "Total (Verified + Bad + Missing) must equal Required quantity": "總數必須等於需求數量", - "Missing item Qty": "缺少物品數量", - "Bad Item Qty": "不良物品數量", - "Issue Remark": "問題備註", + "minutes": "分鐘", + "non-consumables": "非消耗品", + "not default warehosue": "不是默認倉庫", + "packaging": "提料中", + "paused": "已暫停", + "pending": "待處理", + "printQty": "打印數量", + "process epqc": "進行成品檢驗", + "process stockIn": "進行收貨程序", + "productionDate": "生產日期", + "qcCategory": "品檢模板", + "qcItem": "品檢項目", + "qcResult": "QC結果", + "qty": "數量", + "qty is not allowed to be greater than picked qty": "數量不能大於已提料數量", + "qty is not allowed to be greater than remaining available qty": "數量不能大於剩餘可用數量", + "qty is not allowed to be greater than required qty": "數量不能大於需求數量", + "qty is required": "數量是必需的", + "receivedQty": "接收數量", + "release jo": "工單詳情", + "remarks": "備註", + "reset": "重置", + "scan picked material": "掃碼確認提料", + "scheduling": "排程", + "second Scan Status": "對料狀態", + "select qc": "選擇QC", "seq": "序號", - "Handled By": "處理者", - "Job Order Pick Execution": "工單提料", - "BOM Type": "BOM 類型", - "BOM Description": "BOM 說明", - "Floor": "樓層", - "Finish": "完成", - "Open 挑號 QR 碼 on a pick line first, then scan {2fic} to use manual lot substitution.": "請先點選一列並開啟「挑號 QR 碼」,再掃描 {2fic} 以使用手動換批。", - "Enter plastic box carton qty": "請輸入膠茜數目", - "Enter all floors plastic box carton qty": "請輸入各樓層膠茜數目", - "2/F plastic box carton Qty": "2/F 膠茜數目", - "3/F plastic box carton Qty": "3/F 膠茜數目", - "4/F plastic box carton Qty": "4/F 膠茜數目", - "Qty cannot be negative": "數量不可為負數", - "Qty must be a whole number": "請輸入整數(不可為小數)", - "Plastic box carton Qty": "膠茜數目", - "Plastic box carton qty dashboard": "膠茜數目使用數量", - "Plastic box carton qty usage": "膠茜數目", - "Failed to load plastic box carton qty dashboard": "載入膠茜數目使用數量失敗,請稍後再試。", - "No chart data": "沒有圖表資料", - "2/F plastic box carton qty count": "2/F 膠茜數目", - "3/F plastic box carton qty count": "3/F 膠茜數目", - "4/F plastic box carton qty count": "4/F 膠茜數目", - "Total plastic box carton qty": "總膠茜數目", - "All floors": "全部樓層", - "Download Excel": "下載 Excel", - "Exporting...": "匯出中...", - "Summary": "彙總", - "Last 7 days": "最近7天", - "This month": "本月", - "This year": "本年", - "Plastic box carton qty report last 7 days": "膠茜數目使用數量(最近7天)", - "Plastic box carton qty report this month": "膠茜數目使用數量(本月)", - "Plastic box carton qty report this year": "膠茜數目使用數量(本年)", - "Plastic box carton qty multi period report": "膠茜數目使用數量_多時段報表", - "All": "全部", - "bomWarn_title": "BOM 數據問題", - "bomWarn_tooltipHas": "有 {{count}} 筆 BOM 數據問題", - "bomWarn_tooltipNone": "沒有 BOM 數據問題", - "bomWarn_empty": "目前沒有 BOM 數據問題。", - "bomWarn_refresh": "重新檢查", - "bomWarn_refreshing": "檢查中…", - "bomWarn_copyAll": "複製清單", - "bomWarn_close": "關閉", - "bomWarn_loadFailed": "無法載入 BOM 問題清單,請稍後再試。", - "bomWarn_rowBomId": "BOM ID", - "bomWarn_rowItemId": "物料 ID", - "bomWarn_issue_MISSING_BOM_CODE": "BOM 編號為空", - "bomWarn_issue_MISSING_BOM_NAME": "BOM 名稱為空", - "bomWarn_issue_MISSING_ITEM": "關聯物料不存在或已刪除", - "bomWarn_issue_MISSING_SALES_UOM": "物料缺少銷售單位", - "bomWarn_issue_MISSING_UOM_CONVERSION": "銷售單位缺少或已刪除 UOM 換算", - "bomWarn_issue_MISSING_STOCK_UOM": "物料缺少庫存單位,品檢可能失敗", - "bomWarn_issue_MISSING_STOCK_UOM_CONVERSION": "庫存單位缺少或已刪除 UOM 換算,品檢可能失敗" + "settings": "設定", + "sfg": "半成品", + "stock in information": "庫存信息", + "stopped": "暫停", + "storing": "待品檢入倉", + "submit": "提交", + "submitting": "提交中", + "success": "成功", + "supervisor": "主管", + "targetDate": "需求日期", + "type": "類型", + "uom": "單位", + "update production priority": "更新生產優先序", + "update qc info": "更新QC資訊", + "update success": "成功更新資料", + "value must be a number": "值必須是數字", + "view putaway": "查看上架詳情", + "view stockin": "品檢", + "warehouse": "倉庫" } diff --git a/src/i18n/zh/laserPrint.json b/src/i18n/zh/laserPrint.json new file mode 100644 index 0000000..1460d76 --- /dev/null +++ b/src/i18n/zh/laserPrint.json @@ -0,0 +1,3 @@ +{ + "title": "檸檬機(激光機)" +} diff --git a/src/i18n/zh/login.json b/src/i18n/zh/login.json index da69471..146be30 100644 --- a/src/i18n/zh/login.json +++ b/src/i18n/zh/login.json @@ -1,10 +1,11 @@ { - "Invalid username or password.": "帳號或密碼錯誤。", - "Something went wrong. Please try again later.": "出了些問題。請稍後再試。", - "Username": "帳號", - "Password": "密碼", - "Please enter a username": "請輸入帳號", - "Please enter a password": "請輸入密碼", - "Login": "登入", - "Sign In": "登入" -} \ No newline at end of file + "Invalid username or password.": "帳號或密碼錯誤。", + "Login": "登入", + "Password": "密碼", + "Please enter a password": "請輸入密碼", + "Please enter a username": "請輸入帳號", + "Sign In": "登入", + "Something went wrong. Please try again later.": "出了些問題。請稍後再試。", + "Username": "帳號", + "toggle password visibility": "切換密碼可見性" +} diff --git a/src/i18n/zh/m18ImportTesting.json b/src/i18n/zh/m18ImportTesting.json index bc93308..89e296c 100644 --- a/src/i18n/zh/m18ImportTesting.json +++ b/src/i18n/zh/m18ImportTesting.json @@ -1,16 +1,16 @@ { - "Import Master Data": "匯入主資料", - "Modified Date From": "修改日期從", - "Modified Date From *": "修改日期從 *", - "Modified Date To": "修改日期到", - "Modified Date To *": "修改日期到 *", - "Import Purchase Order": "匯入採購單", - "Import Delivery Order": "匯入出貨單", - "Import Purchase Quotation": "匯入採購報價單", - "Import Po": "匯入採購單", - "Import Do": "匯入出貨單", - "Import Pq": "匯入採購報價單", - "Ready to import": "準備匯入", - "Status": "狀態" - -} \ No newline at end of file + "testing sections tabs": "測試區域分頁", + "Import Master Data": "匯入主資料", + "Modified Date From": "修改日期從", + "Modified Date From *": "修改日期從 *", + "Modified Date To": "修改日期到", + "Modified Date To *": "修改日期到 *", + "Import Purchase Order": "匯入採購單", + "Import Delivery Order": "匯入出貨單", + "Import Purchase Quotation": "匯入採購報價單", + "Import Po": "匯入採購單", + "Import Do": "匯入出貨單", + "Import Pq": "匯入採購報價單", + "Ready to import": "準備匯入", + "Status": "狀態" +} diff --git a/src/i18n/zh/m18Sync.json b/src/i18n/zh/m18Sync.json new file mode 100644 index 0000000..eafa124 --- /dev/null +++ b/src/i18n/zh/m18Sync.json @@ -0,0 +1,3 @@ +{ + "title": "M18 同步" +} diff --git a/src/i18n/zh/mail.json b/src/i18n/zh/mail.json index 0402484..c583278 100644 --- a/src/i18n/zh/mail.json +++ b/src/i18n/zh/mail.json @@ -1,22 +1,25 @@ { - "Mail": "郵件", - "Mail List": "郵件列表", - "Mail Name": "郵件名稱", - "Mail Description": "郵件描述", - "Mail Status": "郵件狀態", - "Mail Created At": "郵件創建時間", - "Mail Updated At": "郵件更新時間", - "Setting": "設定", - "Settings": "設定", - "Template": "模板", - "Code": "代碼", - "Description": "描述", - "Subject CHT": "主旨 (繁體中文)", - "Select Template (View By Code - Description)": "選擇模板 (代碼 - 描述)", - "MAIL.smtp.host": "SMTP 主機", - "MAIL.smtp.port": "SMTP 埠口", - "MAIL.smtp.username": "SMTP 使用者名稱", - "MAIL.smtp.password": "SMTP 密碼", - "MAIL.smtp.auth": "SMTP 認證", - "MAIL.smtp.ssl": "SMTP SSL" -} \ No newline at end of file + "Mail": "郵件", + "Mail List": "郵件列表", + "Mail Name": "郵件名稱", + "Mail Description": "郵件描述", + "Mail Status": "郵件狀態", + "Mail Created At": "郵件創建時間", + "Mail Updated At": "郵件更新時間", + "Setting": "設定", + "Settings": "設定", + "Template": "模板", + "Code": "代碼", + "Description": "描述", + "Subject CHT": "主旨 (繁體中文)", + "Select Template (View By Code - Description)": "選擇模板 (代碼 - 描述)", + "MAIL.smtp.host": "SMTP 主機", + "MAIL.smtp.port": "SMTP 埠口", + "MAIL.smtp.username": "SMTP 使用者名稱", + "MAIL.smtp.password": "SMTP 密碼", + "MAIL.smtp.auth": "SMTP 認證", + "MAIL.smtp.ssl": "SMTP SSL", + "Save Fail": "儲存失敗", + "Save Success": "儲存成功", + "send to everyone": "發送給所有人" +} diff --git a/src/i18n/zh/masterDataIssue.json b/src/i18n/zh/masterDataIssue.json index 9fb1d1b..622c549 100644 --- a/src/i18n/zh/masterDataIssue.json +++ b/src/i18n/zh/masterDataIssue.json @@ -1,104 +1,101 @@ { - "Current Stock": "現有庫存", - "masterDataIssue": "BOM/貨品單位問題", - "masterDataIssue_pageTitle": "BOM/貨品單位問題", - "masterDataIssue_tab_bom": "BOM", - "masterDataIssue_tab_item": "貨品", - "masterDataIssue_count": "共 {{count}} 筆問題", - "masterDataIssue_search": "搜索", - "masterDataIssue_refresh": "重新檢查", - "masterDataIssue_refreshing": "檢查中…", - "masterDataIssue_copy": "複製清單", - "masterDataIssue_empty": "目前沒有問題。", - "masterDataIssue_loadFailed": "無法載入問題清單,請稍後再試。", - "masterDataIssue_filter_all": "全部", - "masterDataIssue_material": "原料", - "masterDataIssue_col_scope": "範圍", - "masterDataIssue_col_bom": "BOM", - "masterDataIssue_col_item": "貨品", - "masterDataIssue_col_issue": "問題", - "masterDataIssue_col_expected": "應為", - "masterDataIssue_col_actual": "實際", - "masterDataIssue_scope_BOM": "BOM 總表", - "masterDataIssue_scope_BOM_MATERIAL": "BOM 原材料", - "masterDataIssue_scope_ITEM": "貨品", - "masterDataIssue_nav": "數據問題", - "masterDataIssue_MISSING_BOM_CODE": "BOM 編號為空", - "masterDataIssue_MISSING_BOM_NAME": "BOM 名稱為空", - "masterDataIssue_MISSING_ITEM": "關聯貨品不存在或已刪除", - "masterDataIssue_MISSING_BASE_UOM": "缺少基本單位", - "masterDataIssue_MISSING_SALES_UOM": "缺少銷售單位", - "masterDataIssue_MISSING_STOCK_UOM": "缺少庫存單位", - "masterDataIssue_MISSING_PICKING_UOM": "缺少揀貨單位", - "masterDataIssue_MISSING_PURCHASE_UOM": "缺少採購單位", - "masterDataIssue_DELETED_BASE_UOM": "基本單位已刪除", - "masterDataIssue_DELETED_SALES_UOM": "銷售單位已刪除", - "masterDataIssue_DELETED_STOCK_UOM": "庫存單位已刪除", - "masterDataIssue_DELETED_PURCHASE_UOM": "採購單位已刪除", - "masterDataIssue_MISSING_BASE_UOM_CONVERSION": "基本單位缺少或已刪除 UOM 換算", - "masterDataIssue_MISSING_SALES_UOM_CONVERSION": "銷售單位缺少或已刪除 UOM 換算", - "masterDataIssue_MISSING_STOCK_UOM_CONVERSION": "庫存單位缺少或已刪除 UOM 換算", - "masterDataIssue_MISSING_PICKING_UOM_CONVERSION": "揀貨單位缺少或已刪除 UOM 換算", - "masterDataIssue_MISSING_PURCHASE_UOM_CONVERSION": "採購單位缺少或已刪除 UOM 換算", - "masterDataIssue_MISSING_UOM_CONVERSION": "缺少或已刪除 UOM 換算", - "masterDataIssue_MISSING_STOCK_UOM_CONVERSION": "庫存單位缺少或已刪除 UOM 換算", - "masterDataIssue_MULTIPLE_BASE_UOM": "多筆基本單位設定", - "masterDataIssue_MULTIPLE_SALES_UOM": "多筆銷售單位設定", - "masterDataIssue_MULTIPLE_STOCK_UOM": "多筆庫存單位設定", - "masterDataIssue_MULTIPLE_PICKING_UOM": "多筆揀貨單位設定", - "masterDataIssue_MULTIPLE_PURCHASE_UOM": "多筆採購單位設定", - "masterDataIssue_BOM_OUTPUT_UOM_MISMATCH_SALES": "BOM 產出單位與成品銷售單位不一致", - "masterDataIssue_BOM_OUTPUT_UOM_TEXT_DRIFT": "BOM 產出單位文字與 UOM 主檔不一致", - "masterDataIssue_BOM_MATERIAL_MISSING_ITEM": "BOM 原料貨品不存在或已刪除", - "masterDataIssue_BOM_MATERIAL_SALES_UOM_MISMATCH": "BOM 原料銷售單位與貨品主檔不一致", - "masterDataIssue_BOM_MATERIAL_BASE_UOM_MISMATCH": "BOM 原料基本單位與貨品主檔不一致", - "masterDataIssue_BOM_MATERIAL_STOCK_UOM_MISMATCH": "BOM 原料庫存單位與貨品主檔不一致", - "masterDataIssue_BOM_MATERIAL_UOM_FK_INVALID": "BOM 原料 UOM 參照無效或已刪除", - - "masterDataIssue_group_count": "共 {{groups}} 筆 · {{issues}} 項問題", - "masterDataIssue_col_subject": "主體", - "masterDataIssue_col_summary": "問題摘要", - "masterDataIssue_col_actions": "操作", - "masterDataIssue_viewDetail": "查看詳情", - "masterDataIssue_close": "關閉", - "masterDataIssue_detail_subtitle": "共 {{count}} 項問題", - "masterDataIssue_detail_usedInBom": "出現於 BOM", - "masterDataIssue_issueCount": "{{count}} 項", - "masterDataIssue_fgItem": "成品貨品", - "masterDataIssue_usedInBom": "用於 {{count}} 個 BOM:{{codes}}", - "masterDataIssue_bomMore": " 等 {{count}} 個", - - "masterDataIssue_col_problem": "問題", - "masterDataIssue_col_bom_uom": "BOM 單位", - "masterDataIssue_col_item_uom": "M18 單位", - "masterDataIssue_modifiedAt": "修改時間", - "masterDataIssue_unit_active": "使用中", - "masterDataIssue_unit_inactive": "已停用", - "masterDataIssue_unit_missing": "沒有單位", - "masterDataIssue_filter_type": "類型", - "masterDataIssue_unit_base": "基本單位", - "masterDataIssue_unit_sales": "銷售單位", - "masterDataIssue_unit_stock": "庫存單位", - "masterDataIssue_unit_picking": "揀貨單位", - "masterDataIssue_unit_purchase": "採購單位", - "masterDataIssue_unit_output": "產出單位", - "masterDataIssue_line_itemMissing": "缺少:{{units}}", - "masterDataIssue_line_itemGeneric": "{{problem}}(應為「{{expected}}」,實際「{{actual}}」)", - "masterDataIssue_line_uomBoth": "{{bom}}:銷售/庫存單位應為「{{expected}}」,BOM 為「{{actual}}」", - "masterDataIssue_line_uomSales": "{{bom}}:銷售單位應為「{{expected}}」,BOM 為「{{actual}}」", - "masterDataIssue_line_uomStock": "{{bom}}:庫存單位應為「{{expected}}」,BOM 為「{{actual}}」", - "masterDataIssue_line_outputUom": "{{bom}}:產出單位應為「{{expected}}」,BOM 為「{{actual}}」", - "masterDataIssue_line_outputText": "{{bom}}:產出單位文字應為「{{expected}}」,BOM 為「{{actual}}」", - "masterDataIssue_line_missingUnits": "{{bom}}:缺少 {{units}}", - "masterDataIssue_line_generic": "{{bom}}:{{problem}}(應為「{{expected}}」,實際「{{actual}}」)", - "masterDataIssue_line_problemOnly": "{{bom}}:{{problem}}", - "masterDataIssue_detail_uomBoth": "銷售/庫存單位與主檔不一致", - "masterDataIssue_detail_uomSales": "銷售單位與主檔不一致", - "masterDataIssue_detail_uomStock": "庫存單位與主檔不一致", - "masterDataIssue_detail_uomBase": "基本單位與主檔不一致", - "masterDataIssue_materialUsedInBom": "用於 BOM:{{codes}}", - "masterDataIssue_line_pairBoth": "銷售/庫存單位應為「{{expected}}」,BOM 為「{{actual}}」", - "masterDataIssue_line_pairSales": "銷售單位應為「{{expected}}」,BOM 為「{{actual}}」", - "masterDataIssue_line_pairStock": "庫存單位應為「{{expected}}」,BOM 為「{{actual}}」", - "masterDataIssue_line_pairBase": "基本單位應為「{{expected}}」,BOM 為「{{actual}}」" -} \ No newline at end of file + "Current Stock": "現有庫存", + "masterDataIssue": "BOM/貨品單位問題", + "masterDataIssue_pageTitle": "BOM/貨品單位問題", + "masterDataIssue_tab_bom": "BOM", + "masterDataIssue_tab_item": "貨品", + "masterDataIssue_count": "共 {{count}} 筆問題", + "masterDataIssue_search": "搜索", + "masterDataIssue_refresh": "重新檢查", + "masterDataIssue_refreshing": "檢查中…", + "masterDataIssue_copy": "複製清單", + "masterDataIssue_empty": "目前沒有問題。", + "masterDataIssue_loadFailed": "無法載入問題清單,請稍後再試。", + "masterDataIssue_filter_all": "全部", + "masterDataIssue_material": "原料", + "masterDataIssue_col_scope": "範圍", + "masterDataIssue_col_bom": "BOM", + "masterDataIssue_col_item": "貨品", + "masterDataIssue_col_issue": "問題", + "masterDataIssue_col_expected": "應為", + "masterDataIssue_col_actual": "實際", + "masterDataIssue_scope_BOM": "BOM 總表", + "masterDataIssue_scope_BOM_MATERIAL": "BOM 原材料", + "masterDataIssue_scope_ITEM": "貨品", + "masterDataIssue_nav": "BOM/貨品單位問題", + "masterDataIssue_MISSING_BOM_CODE": "BOM 編號為空", + "masterDataIssue_MISSING_BOM_NAME": "BOM 名稱為空", + "masterDataIssue_MISSING_ITEM": "關聯貨品不存在或已刪除", + "masterDataIssue_MISSING_BASE_UOM": "缺少基本單位", + "masterDataIssue_MISSING_SALES_UOM": "缺少銷售單位", + "masterDataIssue_MISSING_STOCK_UOM": "缺少庫存單位", + "masterDataIssue_MISSING_PICKING_UOM": "缺少揀貨單位", + "masterDataIssue_MISSING_PURCHASE_UOM": "缺少採購單位", + "masterDataIssue_DELETED_BASE_UOM": "基本單位已刪除", + "masterDataIssue_DELETED_SALES_UOM": "銷售單位已刪除", + "masterDataIssue_DELETED_STOCK_UOM": "庫存單位已刪除", + "masterDataIssue_DELETED_PURCHASE_UOM": "採購單位已刪除", + "masterDataIssue_MISSING_BASE_UOM_CONVERSION": "基本單位缺少或已刪除 UOM 換算", + "masterDataIssue_MISSING_SALES_UOM_CONVERSION": "銷售單位缺少或已刪除 UOM 換算", + "masterDataIssue_MISSING_STOCK_UOM_CONVERSION": "庫存單位缺少或已刪除 UOM 換算", + "masterDataIssue_MISSING_PICKING_UOM_CONVERSION": "揀貨單位缺少或已刪除 UOM 換算", + "masterDataIssue_MISSING_PURCHASE_UOM_CONVERSION": "採購單位缺少或已刪除 UOM 換算", + "masterDataIssue_MISSING_UOM_CONVERSION": "缺少或已刪除 UOM 換算", + "masterDataIssue_MULTIPLE_BASE_UOM": "多筆基本單位設定", + "masterDataIssue_MULTIPLE_SALES_UOM": "多筆銷售單位設定", + "masterDataIssue_MULTIPLE_STOCK_UOM": "多筆庫存單位設定", + "masterDataIssue_MULTIPLE_PICKING_UOM": "多筆揀貨單位設定", + "masterDataIssue_MULTIPLE_PURCHASE_UOM": "多筆採購單位設定", + "masterDataIssue_BOM_OUTPUT_UOM_MISMATCH_SALES": "BOM 產出單位與成品銷售單位不一致", + "masterDataIssue_BOM_OUTPUT_UOM_TEXT_DRIFT": "BOM 產出單位文字與 UOM 主檔不一致", + "masterDataIssue_BOM_MATERIAL_MISSING_ITEM": "BOM 原料貨品不存在或已刪除", + "masterDataIssue_BOM_MATERIAL_SALES_UOM_MISMATCH": "BOM 原料銷售單位與貨品主檔不一致", + "masterDataIssue_BOM_MATERIAL_BASE_UOM_MISMATCH": "BOM 原料基本單位與貨品主檔不一致", + "masterDataIssue_BOM_MATERIAL_STOCK_UOM_MISMATCH": "BOM 原料庫存單位與貨品主檔不一致", + "masterDataIssue_BOM_MATERIAL_UOM_FK_INVALID": "BOM 原料 UOM 參照無效或已刪除", + "masterDataIssue_group_count": "共 {{groups}} 筆 · {{issues}} 項問題", + "masterDataIssue_col_subject": "主體", + "masterDataIssue_col_summary": "問題摘要", + "masterDataIssue_col_actions": "操作", + "masterDataIssue_viewDetail": "查看詳情", + "masterDataIssue_close": "關閉", + "masterDataIssue_detail_subtitle": "共 {{count}} 項問題", + "masterDataIssue_detail_usedInBom": "出現於 BOM", + "masterDataIssue_issueCount": "{{count}} 項", + "masterDataIssue_fgItem": "成品貨品", + "masterDataIssue_usedInBom": "用於 {{count}} 個 BOM:{{codes}}", + "masterDataIssue_bomMore": " 等 {{count}} 個", + "masterDataIssue_col_problem": "問題", + "masterDataIssue_col_bom_uom": "BOM 單位", + "masterDataIssue_col_item_uom": "M18 單位", + "masterDataIssue_modifiedAt": "修改時間", + "masterDataIssue_unit_active": "使用中", + "masterDataIssue_unit_inactive": "已停用", + "masterDataIssue_unit_missing": "沒有單位", + "masterDataIssue_filter_type": "類型", + "masterDataIssue_unit_base": "基本單位", + "masterDataIssue_unit_sales": "銷售單位", + "masterDataIssue_unit_stock": "庫存單位", + "masterDataIssue_unit_picking": "揀貨單位", + "masterDataIssue_unit_purchase": "採購單位", + "masterDataIssue_unit_output": "產出單位", + "masterDataIssue_line_itemMissing": "缺少:{{units}}", + "masterDataIssue_line_itemGeneric": "{{problem}}(應為「{{expected}}」,實際「{{actual}}」)", + "masterDataIssue_line_uomBoth": "{{bom}}:銷售/庫存單位應為「{{expected}}」,BOM 為「{{actual}}」", + "masterDataIssue_line_uomSales": "{{bom}}:銷售單位應為「{{expected}}」,BOM 為「{{actual}}」", + "masterDataIssue_line_uomStock": "{{bom}}:庫存單位應為「{{expected}}」,BOM 為「{{actual}}」", + "masterDataIssue_line_outputUom": "{{bom}}:產出單位應為「{{expected}}」,BOM 為「{{actual}}」", + "masterDataIssue_line_outputText": "{{bom}}:產出單位文字應為「{{expected}}」,BOM 為「{{actual}}」", + "masterDataIssue_line_missingUnits": "{{bom}}:缺少 {{units}}", + "masterDataIssue_line_generic": "{{bom}}:{{problem}}(應為「{{expected}}」,實際「{{actual}}」)", + "masterDataIssue_line_problemOnly": "{{bom}}:{{problem}}", + "masterDataIssue_detail_uomBoth": "銷售/庫存單位與主檔不一致", + "masterDataIssue_detail_uomSales": "銷售單位與主檔不一致", + "masterDataIssue_detail_uomStock": "庫存單位與主檔不一致", + "masterDataIssue_detail_uomBase": "基本單位與主檔不一致", + "masterDataIssue_materialUsedInBom": "用於 BOM:{{codes}}", + "masterDataIssue_line_pairBoth": "銷售/庫存單位應為「{{expected}}」,BOM 為「{{actual}}」", + "masterDataIssue_line_pairSales": "銷售單位應為「{{expected}}」,BOM 為「{{actual}}」", + "masterDataIssue_line_pairStock": "庫存單位應為「{{expected}}」,BOM 為「{{actual}}」", + "masterDataIssue_line_pairBase": "基本單位應為「{{expected}}」,BOM 為「{{actual}}」" +} diff --git a/src/i18n/zh/material.json b/src/i18n/zh/material.json new file mode 100644 index 0000000..6aa2b4d --- /dev/null +++ b/src/i18n/zh/material.json @@ -0,0 +1,4 @@ +{ + "Material": "材料", + "Create Claim": "新增索賠" +} diff --git a/src/i18n/zh/navigation.json b/src/i18n/zh/navigation.json new file mode 100644 index 0000000..8a48e09 --- /dev/null +++ b/src/i18n/zh/navigation.json @@ -0,0 +1,99 @@ +{ + "menu": "選單", + "nav.bagPrint": "打袋機", + "nav.breadcrumb.bagPrint": "打袋機", + "nav.breadcrumb.chart": "圖表報告", + "nav.breadcrumb.chartDelivery": "發貨與配送", + "nav.breadcrumb.chartForecast": "預測與計劃", + "nav.breadcrumb.chartJobOrder": "工單", + "nav.breadcrumb.chartJobOrderBoard": "工單即時看板", + "nav.breadcrumb.chartPurchase": "採購", + "nav.breadcrumb.chartWarehouse": "庫存與倉儲", + "nav.breadcrumb.demandForecast": "需求預測設定", + "nav.breadcrumb.deliveryOrderFloor": "送貨單樓層", + "nav.breadcrumb.doWorkbenchEdit": "DO Workbench 詳情", + "nav.breadcrumb.doWorkbenchPick": "DO Workbench 揀貨", + "nav.breadcrumb.doWorkbenchSearch": "DO Workbench 搜索", + "nav.breadcrumb.equipment": "設備", + "nav.breadcrumb.equipmentMaintenanceEdit": "維護編輯", + "nav.breadcrumb.finishedGood": "成品出倉", + "nav.breadcrumb.finishedGoodManagement": "成品出倉管理", + "nav.breadcrumb.home": "總覽", + "nav.breadcrumb.importTesting": "匯入測試", + "nav.breadcrumb.inventory": "存貨", + "nav.breadcrumb.joEdit": "工單詳情", + "nav.breadcrumb.joTesting": "工單測試", + "nav.breadcrumb.joWorkbench": "工單工作台", + "nav.breadcrumb.laserPrint": "檸檬機(激光機)", + "nav.breadcrumb.m18Sync": "M18 同步", + "nav.breadcrumb.poEdit": "編輯", + "nav.breadcrumb.poWorkbench": "採購單工作台", + "nav.breadcrumb.priceInquiry": "價格查詢", + "nav.breadcrumb.printer": "列印機", + "nav.breadcrumb.projects": "專案", + "nav.breadcrumb.projectsCreate": "新增專案", + "nav.breadcrumb.qcItem": "QC 項目", + "nav.breadcrumb.qcItemAll": "QC 綜合管理", + "nav.breadcrumb.masterDataIssues": "BOM / 物料單位問題", + "nav.breadcrumb.qrCodeHandle": "二維碼列印及下載", + "nav.breadcrumb.report": "報告管理", + "nav.breadcrumb.routeBoard": "車線看板", + "nav.breadcrumb.schedulingDetailed": "詳細排程", + "nav.breadcrumb.schedulingDetailedEdit": "FG 生產排程", + "nav.breadcrumb.schedulingRough": "需求預測", + "nav.breadcrumb.schedulingRoughEdit": "成品及材料需求預測詳情", + "nav.breadcrumb.shop": "車線店鋪管理", + "nav.breadcrumb.shopDetail": "店鋪詳情", + "nav.breadcrumb.tasks": "任務範本", + "nav.breadcrumb.tasksCreate": "新增任務範本", + "nav.breadcrumb.truckLaneDetail": "車線詳情", + "nav.chart.delivery": "發貨與配送", + "nav.chart.forecast": "預測與計劃", + "nav.chart.jobOrder": "工單", + "nav.chart.jobOrderBoard": "工單即時看板", + "nav.chart.purchase": "採購", + "nav.chart.warehouse": "庫存與倉儲", + "nav.chartReports": "圖表報告", + "nav.dashboard": "資訊展示面板", + "nav.deliveryOrder": "送貨訂單", + "nav.jobOrder.bagUsage": "包裝袋使用記錄", + "nav.jobOrder.pickExecution": "工單提料", + "nav.jobOrder.productionProcess": "工單生產流程", + "nav.jobOrder.searchCreate": "搜索工單/ 建立工單", + "nav.jobOrderManagement": "管理工單", + "nav.laserPrint": "檸檬機(激光機)", + "nav.m18Sync": "M18 同步", + "nav.productionServer": "正式伺服器", + "nav.report": "報告管理", + "nav.scheduling": "排程", + "nav.settings": "設定", + "nav.settings.bomWeighting": "BOM 權重得分", + "nav.settings.clientMonitor": "裝置連線監控", + "nav.settings.deliveryOrderFloor": "送貨單樓層(供應商)", + "nav.settings.demandForecast": "需求預測設定", + "nav.settings.equipment": "設備", + "nav.settings.importBom": "匯入 BOM", + "nav.settings.importExcel": "Excel 匯入", + "nav.settings.importTesting": "匯入測試", + "nav.settings.items": "物品", + "nav.settings.masterDataIssues": "BOM / 物料單位問題", + "nav.settings.priceInquiry": "價格查詢", + "nav.settings.printer": "列印機", + "nav.settings.qcCategory": "QC 品檢模板", + "nav.settings.qcItem": "QC 品檢項目", + "nav.settings.qcItemAll": "QC 綜合管理", + "nav.settings.qrCodeHandle": "二維碼列印及下載", + "nav.settings.shopAndTruck": "車線店鋪管理", + "nav.settings.user": "用戶", + "nav.settings.warehouse": "倉庫", + "nav.store.doWorkbench": "新版成品出倉", + "nav.store.finishedGoodManagement": "成品出倉管理", + "nav.store.inventoryLedger": "查看物品出入庫及庫存日誌", + "nav.store.pickOrder": "提料單", + "nav.store.purchaseOrder": "採購單", + "nav.store.putAwayScan": "上架掃碼", + "nav.store.stockIssue": "出倉問題", + "nav.store.stockRecord": "庫存記錄", + "nav.store.stockTake": "盤點管理", + "nav.storeManagement": "倉庫管理" +} diff --git a/src/i18n/zh/pickOrder.json b/src/i18n/zh/pickOrder.json index 5a410fd..846ddbd 100644 --- a/src/i18n/zh/pickOrder.json +++ b/src/i18n/zh/pickOrder.json @@ -1,532 +1,521 @@ - { - "Purchase Order": "採購訂單", - "Code": "編號", - "Pick Order Code": "提料單編號", - "Item Code": "貨品編號", - "OrderDate": "下單日期", - "Details": "詳情", - "Supplier": "供應商", - "Status": "來貨狀態", - "N/A": "不適用", - "Release Pick Orders": "放單", - "released": "已放單", - "Loading...": "載入中...", - "Suggestion success": "建議成功", - "Scan pick success": "掃描提料成功", - "Remark": "備註", - "Available Qty": "可用數量", - "Picked Qty": "已提料數量", - "Escalated": "上報狀態", - "NotEscalated": "無上報", - "Assigned To": "已分配", - "Progress": "進度", - "Select Remark": "選擇備註", - "Just Complete": "已完成", - "Skip": "跳過", - "if need just edit number, please scan the lot again": "如果需要只修改數量,請重新掃描批次。", - "Total qty (actual pick + miss + bad) cannot exceed available qty: {available}": "總數量(實際提料 + 遺失 + 不良)不能超過可用數量:{{available}}", - "Confirm Assignment": "確認分配", - "Required Date": "所需日期", - "Store": "位置", - "Available Orders": "可用訂單", - "This lot is rejected, please scan another lot.": "此批次已拒收,請掃描另一個批次。", - "Lane Code": "車線號碼", - "Fetching all matching records...": "正在獲取所有匹配的記錄...", - "Edit": "改數", - "Submit Qty": "提交數量", - "Suggestion success": "建議成功", - "Just Completed": "已完成", - "Just Completed (workbench): requires a valid lot number and quantity; expired rows must not use this button.": "已完成(工作台):需有效批號與可提交數量;過期列請勿使用此按鈕。", - "Do you want to start?": "確定開始嗎?", - "Start": "開始", - "Pick Order Code(s)": "提料單編號", - "Delivery Order Code(s)": "提料單編號", - "Start Success": "開始成功", - "Qty will submit": "提交數量", - "Truck Lance Code": "車線號碼", - "Pick Order Codes": "提料單編號", - "Pick Order Lines": "提料單行數", - "Delivery Order Codes": "提料單編號", - "Delivery Order Lines": "送貨單行數", - "Lines Per Pick Order": "每提料單行數", - "Pick Orders Details": "提料單詳情", - "Lines": "行數", - "Before Today": "以前", - "Truck X": "車線-X", - "Finsihed good items": "成品項目", - "kinds": "款", - "Completed Date": "完成日期", - "Completed Time": "完成時間", - "Delivery Order": "送貨單", - "items": "項目", - "Select Pick Order:": "選擇提料單:", - "No Stock Available": "沒有庫存", - "is expired. Please check around have available QR code or not.": "已過期。請檢查周圍是否有可用的 QR 碼。", - "Start Fail": "開始失敗", - "Start PO": "開始採購訂單", - "Do you want to complete?": "確定完成嗎?", - "Complete": "完成", - "Complete Success": "完成成功", - "Complete Fail": "完成失敗", - "Complete Pick Order": "完成提料單", - "General": "一般", - "Bind Storage": "綁定倉位", - "itemNo": "貨品編號", - "itemName": "貨品名稱", - "qty": "訂單數", - "Require Qty": "需求數", - "uom": "計量單位", - "total weight": "總重量", - "weight unit": "重量單位", - "price": "訂單貨值", - "processed": "已入倉", - "expiryDate": "到期日", - "acceptedQty": "是次訂單/來貨/巳來貨數", - "weight": "重量", - "start": "開始", - "qc": "質量控制", - "escalation": "上報", - "stock in": "入庫", - "putaway": "上架", - "delete": "刪除", - "qty cannot be greater than remaining qty": "數量不能大於剩餘數", - "Record pol": "記錄採購訂單", - "Add some entries!": "請添加條目", - "draft": "草稿", - "pending": "待處理", - "determine1": "上報1", - "determine2": "上報2", - "determine3": "上報3", - "receiving": "收貨中", - "received": "已收貨", - "completed": "已完成", - "rejected": "已拒絕", - "success": "成功", - "acceptedQty must not greater than": "接受數量不得大於", - "minimal value is 1": "最小值為1", - "value must be a number": "值必須是數字", - "qc Check": "質量控制檢查", - "Please select QC": "請選擇質量控制", - "failQty": "失敗數", - "select qc": "選擇質量控制", - "enter a failQty": "請輸入失敗數", - "qty too big": "數量過大", - "sampleRate": "抽樣率", - "sampleWeight": "樣本重量", - "totalWeight": "總重量", - - "Escalation": "上報", - "to be processed": "待處理", - - "Stock In Detail": "入庫詳情", - "productLotNo": "產品批號", - "receiptDate": "收貨日期", - "acceptedWeight": "接受重量", - "productionDate": "生產日期", - - "reportQty": "上報數", - - "Default Warehouse": "預設倉庫", - "Select warehouse": "選擇倉庫", - "Putaway Detail": "上架詳情", - "LotNo": "批號", - "Po Code": "採購訂單編號", - "No Warehouse": "沒有倉庫", - "Please scan warehouse qr code.": "請掃描倉庫 QR 碼。", - - "Reject": "拒絕", - "submit": "確認提交", - "print": "列印", - "bind": "綁定", - "Total must equal Required Qty. Missing: {diff}": "總數量必須等於所需數量。缺少:{{diff}}", - "Total must equal Required Qty. Exceeds by: {diff}": "總數量必須等於所需數量。超出:{{diff}}", - - "Batch": "批量", - "Single": "單量", - "Release Type": "放單類型", - "isExtra order": "加單", - "Etra": "加單", - "Exit Etra view": "離開加單檢視", - "Etra Pick Order Detail": "加單", - "Etra incomplete badge tooltip": "當日未完成加單票:{{count}} 張(待處理/已發佈,不含已結案)", - "Etra incomplete badge tooltip none": "目前無未完成加單票", - "Back to normal assign tab": "返回一般指派分頁", - "Enter isExtra workbench view?": "進入加單檢視?", - "Etra view groups all add-on tickets by shop and lane for the selected date.": "加單檢視會依選定日期,將 isExtra 票依店鋪與車線顯示。", - "Etra Ticket Notice": "目前是加單票,顯示與操作已切換為加單模式。", - - "Pick Order": "提料單", - "Type": "類型", - "Product Type": "貨品類型", - "Reset": "重置", - "Search": "搜索", - "Pick Orders": "提料單", - "Consolidated Pick Orders": "合併提料單", - "Pick Order No.": "提料單編號", - "Pick Order Date": "提料單日期", - "Pick Order Status": "提貨狀態", - "Pick Order Type": "提料單類型", - "Consolidated Code": "合併編號", - "type": "類型", - "Items": "項目", - "Target Date": "需求日期", - "Released By": "發佈者", - "Target Date From": "目標日期", - "Target Date To": "目標日期到", - "Consolidate": "合併", - "Stock Unit": "庫存單位", - "create": "新增", - "detail": "詳情", - "Pick Order Detail": "撳單/提料單詳情", - "item": "貨品", - "Unit": "單位", - "reset": "重置", - "targetDate": "目標日期", - "remove": "移除", - "release": "發佈", - "location": "位置", - "suggestedLotNo": "建議批次", - "lotNo": "批次", - "item name": "貨品名稱", - "Item Name": "貨品名稱", - "approval": "審核", - "lot change": "批次變更", - "checkout": "出庫", - "Search Items": "搜索貨品", - "Search Results": "可選擇貨品", - "Second Search Results": "第二搜索結果", - "Second Search Items": "第二搜索項目", - "Second Search": "第二搜索", - "Item": "貨品", - "Order Quantity": "貨品需求數", - "Current Stock": "現時可用庫存", - "Selected": "已選", - "Select Items": "選擇貨品", - "Assign": "分派提料單", - "Release": "放單", - "Pick Execution": "進行提料", - "Create Pick Order": "建立貨品提料單", - "Consumable": "消耗品", - "Material": "食材", - "Job Order": "工單", - "End Product": "成品", - "Lot Expiry Date": "到期日", - "Lot Location": "位置", - "Available Lot": "可用提料數", - "Lot Required Pick Qty": "所需數", - "Lot Actual Pick Qty": "此單將提數", - "Lot#": "批號", - "Submit": "提交", - "Created Items": "已建立貨品", - "Create New Group": "建立新提料分組", - "Group": "分組", - "Qty Already Picked": "已提料數", - "Select Job Order Items": "選擇工單貨品", - "failedQty": "不合格項目數", - "remarks": "備註", - "Qc items": "QC 項目", - "qcItem": "QC 項目", - "QC Info": "QC 資訊", - "qcResult": "QC 結果", - "acceptQty": "接受數", - "Escalation History": "上報歷史", - "Group Code": "分組編號", - "Job Order Code": "工單編號", - "QC Check": "QC 檢查", - "QR Code Scan": "QR Code掃描", - "Pick Order Details": "提料單詳情", - "Partial quantity submitted. Please submit more or complete the order.": "已提料部分數量。請提交更多或完成訂單。", - "Pick order completed successfully!": "提料單完成成功!", - "Lot has been rejected and marked as unavailable.": "批號已拒絕並標記為不可用。", - "This order is insufficient, please pick another lot.": "此訂單不足,請選擇其他批號。", - "Please finish QR code scan, QC check and pick order.": "請完成 QR 碼掃描、QC 檢查和提料。", - "No data available": "沒有資料", - "Please submit the pick order.": "請提交提料單。", - "Item lot to be Pick:": "批次貨品提料:", - "Report and Pick another lot": "上報並需重新選擇批號", - "Accept Stock Out": "接受出庫", - "Pick Another Lot": "欠數,並重新選擇批號", - "Delivery Note Code": "送貨單編號", - "A4 Printer": "A4 打印機", - "Label Printer": "標籤打印機", - "Please select a printer first": "請先選擇打印機", - "Please select a label printer first": "請先選擇標籤打印機", - - "Lot No": "批號", - "Expiry Date": "到期日", - "Location": "位置", - "All Pick Order Lots": "所有提料單批次", - "Completed": "已完成", - "Finished Good Order": "成品出倉", - "Assign and Release": "分派並放單", - "Original Available Qty": "原可用數", - "Remaining Available Qty": "剩餘可用數", - "Please submit pick order.": "請提交提料單。", - "Please finish QR code scan and pick order.": "請完成 QR 碼掃描和提料。", - "Please finish QR code scanand pick order.": "請完成 QR 碼掃描和提料。", - "First created group": "首次建立分組", - "Latest created group": "最新建立分組", - "Manual Input": "手動輸入", - "QR Code Scan for Lot": " QR 碼掃描批次", - "Processing QR code...": "處理 QR 碼...", - "The input is not the same as the expected lot number.": "輸入的批次號碼與預期的不符。", - "Verified successfully!": "驗證成功!", - "Cancel": "取消", - "storing": "待品檢入倉", - "pick successful": "提料成功", - "Suggestion success": "建議成功", - "Insufficient available quantity on lot (may have been picked by another user)": "掃描的批次已被其他用戶完全提料。請掃描其他批次。", - "Scan": "掃描", - "Before today": "今天之前", - "Scanned": "已掃描", - "Loading data...": "正在載入數據...", - "No available stock for this item": "沒有可用庫存", - "No lot details available for this item": "沒有批次詳情", - "Current stock is insufficient or unavailable": "現時可用庫存不足或不可用", - "Please check inventory status": "請檢查庫存狀態", - "Rows per page": "每頁行數", - "QR Scan Result:": "QR 掃描結果:", - "Action": "操作", - "Please finish pick order.": "請完成提料。", - "Lot": "批號", - "Assign Pick Orders": "分派提料單", - "Selected Pick Orders": "已選擇提料單數量", - "Please assgin/release the pickorders to picker": "請分派/放單提料單給提料員。", - "Assign To": "分派給", - "No Group": "沒有分組", - "Selected items will join above created group": "已選擇的貨品將加入以上建立的分組", - "Issue":"問題", - "Pick Execution Issue Form":"提料問題表單", - "Lot line is unavailable":"掃描批次不可用", - "This form is for reporting issues only. You must report either missing items or bad items.":"此表單僅用於報告問題。您必須報告缺少的貨品或不良貨品。", - "Bad item Qty":"不良貨品數量", - "Missing item Qty":"貨品遺失數量", - "Missing Item Qty":"貨品遺失數量", - "Bad Item Qty":"不良貨品數量", - "Bad Package Qty":"不良包裝數量", - "Lot line is not available (status=UNAVAILABLE)":"掃描批次不可用", - "Actual Pick Qty":"實際提料數量", - "Required Qty":"所需數量", - "Issue Remark":"問題描述", - "Handler":"提料員", - "Qty is required":"必需輸入數量", - "Qty is not allowed to be greater than remaining available qty":"輸入數量不能大於剩餘可用數量", - "Qty is not allowed to be greater than required qty":"輸入數量不能大於所需數量", - "At least one issue must be reported":"至少需要報告一個問題", - "issueRemark":"問題描述是必需的", - "handler":"提料員", - "Max":"最大值", - "Route":"路線", - "Index":"編號", - "No FG pick orders found":"沒有成品提料單", - "Finish Scan?":"完成掃描?", - "Delivery Code":"出倉單編號", - "Shop PO Code":"訂單編號", - "Shop ID":"商店編號", - "Truck No.":"車線編號", - "Departure Time":"車線出發時間", - "Shop Name":"商店名稱", - "Shop Address":"商店地址", - "Delivery Date":"目標日期", - "Pick Execution 2/F":"進行提料 2/F", - "Pick Execution 4/F":"進行提料 4/F", - "Pick Execution Detail":"進行提料詳情", - "Submit Required Pick Qty":"提交所需提料數量", - "Scan Result":"掃描結果", - "Ticket No.":"提票號碼", - "Start QR Scan":"開始QR掃描", - "Stop QR Scan":"停止QR掃描", - "Scanning...":"掃描中...", - "Print DN/Label":"列印送貨單/標籤", - "Store ID":"儲存編號", - "QR code does not match any item in current orders.":"QR 碼不符合當前訂單中的任何貨品。", - "Lot Number Mismatch":"批次號碼不符", - "The scanned item matches the expected item, but the lot number is different. Do you want to proceed with this different lot?":"掃描的貨品與預期的貨品相同,但批次號碼不同。您是否要繼續使用不同的批次?", - "The scanned item matches the expected item, but the lot number is different. Scan again to confirm: scan the expected lot QR to keep the suggested lot, or scan the other lot QR again to switch.":"掃描貨品相同但批次不同。請再掃描一次以確認:掃描「建議批次」的 QR 可沿用該批次;再掃描「另一批次」的 QR 則切換為該批次。", - "Expected Lot:":"預期批次:", - "Scanned Lot:":"掃描批次:", - "Confirm":"確認", - "Update your suggested lot to the this scanned lot":"更新您的建議批次為此掃描的批次", - "Print Draft":"列印草稿", - "Print Pick Order and DN Label":"列印提料單和送貨單標籤", - "Print Pick Order":"列印提料單", - "Print DN Label":"列印送貨單標籤", - "Print All Draft" : "列印全部草稿", - "If you confirm, the system will:":"如果您確認,系統將:", - "After you scan to choose, the system will update the pick line to the lot you confirmed.":"確認後,系統會將您選擇的批次套用到對應提料行。", - "Or use the Confirm button below if you cannot scan again (same as scanning the other lot again).":"若無法再掃描,可按下「確認」以切換為剛才掃描到的批次(與再掃一次該批次 QR 相同)。", - "Lot switch failed":"批次切換失敗", - "The system could not switch to the scanned lot. Review the lots below, then tap Confirm to retry.":"系統無法切換至掃描的批次。請核對下方批次後按「確認」重試。", - "You can also scan again: expected lot QR keeps the suggested line; scanned lot QR retries the switch.":"您也可以再掃描:掃描建議批次 QR 可保留該行;掃描欲切換批次 QR 可再次嘗試切換。", - "QR code verified.":"QR 碼驗證成功。", - "Order Finished":"訂單完成", - "Submitted Status":"提交狀態", - "Pick Execution Record":"提料執行記錄", - "Delivery No.":"送貨單編號", - "Total":"總數", - "Completed DO pick orders: ":"已完成送貨單提料單:", - "No completed DO pick orders found":"沒有已完成送貨單提料單", - - "Enter the number of cartons: ": "請輸入總箱數", - "Number of cartons": "箱數", - "Select an action for the assigned pick orders.": "選擇分配提料單的動作。", - "Detail": "詳情", - "consoCode": "合併編號", - "status": "狀態", - "Items Included": "貨品包括", - "Pick Order Included": "提料單包括", - "No created items": "沒有已建立的貨品", - "Please select item": "請選擇貨品", - "enter a qty": "請輸入數量", - "update qc info": "更新QC資訊", - "All lots must be completed before printing": "所有批次必須完成才能打印", - "Assigning pick order...": "分配提料單...", - "Enter the number of cartons:": "請輸入總箱數", - "Finished Good Detail": "成品提貨詳情", - "Finished Good Record": "成品提貨記錄", - "Finished Good Record (All)": "成品提貨記錄(全部)", - "All dates": "全部日期", - "Search date": "搜索日期", - "Hide Completed: OFF": "完成: OFF", - "Hide Completed: ON": "完成: ON", - "Number must be at least 1": "數量至少為1", - "Printed Successfully.": "成功列印", - "Product": "產品", - "You need to enter a number": "您需要輸入一個數字", - "Available in warehouse": "在倉庫中可用", - "Describe the issue with bad items": "描述不良貨品的問題", - "Enter pick qty or issue qty": "請輸入提料數量或不良數量", - "Invalid qty": "無效數量", - "Note:": "注意:", - "Qty is not allowed to be greater than required/available qty": "數量不能大於所需/可用數量", - "Still need to pick": "仍需提料", - "Total exceeds required qty": "總數超過所需數量", - "submitting": "提交中", - "Back to List": "返回列表", - "Delivery No": "送貨單編號", - "FG orders": "成品訂單", - "View Details": "查看詳情", - "No Item": "沒有貨品", - "None": "沒有", - "This form is for reporting issues only. You must report either missing items or bad items.": "此表單僅用於報告問題。您必須報告缺少的物品或不良物品。", - "Add Selected Items to Created Items": "將已選擇的貨品添加到已建立的貨品中", - "All pick orders created successfully": "所有提料單建立成功", - "Failed to create group": "建立分組失敗", - "Invalid date format": "日期格式無效", - "Item already exists in created items": "貨品已存在於已建立的貨品中", - "Job Order not found or has no items": "工單不存在或沒有貨品", - "Loading...": "加載中", - "No results found": "沒有結果", - "Please enter at least code or name": "請輸入至少編號或名稱", - "Please enter quantity for all selected items": "請輸入所有已選擇的貨品的數量", - "Please select at least one item to submit": "請選擇至少一個貨品提交", - "Please select group and enter quantity for all selected items": "請選擇分組並輸入所有已選擇的貨品的數量", - "Please select group for all selected items": "請選擇分組對所有已選擇的貨品", - "Please select product type": "請選擇產品類型", - "Please select target date": "請選擇目標日期", - "Please select type": "請選擇類型", - "Search Criteria": "搜索條件", - "Processing...": "處理中", - "Failed items must have failed quantity": "不合格的貨品必須有不合格數量", - "QC items without result": "QC項目沒有結果", - "confirm putaway": "確認上架", - "email supplier": "發送郵件給供應商", - "qc processing": "QC處理", - "submitStockIn": "提交入庫", - "not default warehosue": "不是默認倉庫", - "printQty": "打印數量", - "Shop": "商店名稱", - "warehouse": "倉庫", - "Add Record": "添加記錄", - "Clean Record": "清空記錄", - "Select": "選擇", - "Close": "關閉", - "Truck": "車線", - "Date": "日期", - "Delivery Order Code": "送貨單編號", - "Escalation Info": "升級信息", - "Escalation Result": "升級結果", - "acceptQty must not greater than": "接受數量不能大於", - "supervisor": "主管", - "No Qc": "沒有QC", - "receivedQty": "接收數量", - "stock in information": "入庫信息", - "No Uom": "沒有單位", - "Input quantity cannot exceed": "輸入數量不能超過", - "Quantity cannot be negative": "數量不能為負數", - "Enter bad item quantity (required if no missing items)": "請輸入不良數量(如果沒有缺少項目)", - "Enter missing quantity (required if no bad items)": "請輸入缺少數量(如果沒有不良項目)", - "Submit All Scanned": "提交所有已掃描項目", - "Submitting...": "提交中...", - "COMPLETED": "已完成", - "Confirm print: (": "確認列印全部草稿?(總數量:", - "piece(s))": "份)", - "Printing...": "列印中", - "Please wait...": "請稍後", - "No available pick order(s) for this floor.": "此樓層沒有可用的提料單", - "You already have a pick order in progess. Please complete it first before taking next pick order.": "請先完成目前的提料單,再提取下一張", - "Error occurred during assignment.": "提料單分配錯誤", - "Info": "消息", - "Warning": "警告", - "Error": "錯誤", - "Batch Print": "批量列印", - "No entries available": "該樓層未有需處理訂單", - "Today": "是日", - "Tomorrow": "翌日", - "packaging": "提料中", - "No Stock Available": "沒有庫存可用", - "This lot is not available, please scan another lot.": "此批號不可用,請掃描其他批號。", - "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.": "請檢查周圍是否有 QR 碼,可能有剛剛入庫、轉移入庫或轉移出庫的 QR 碼。", - "Lot is expired (expiry={{expiry}})": "掃描批號已過期(到期日={{expiry}})", - "Day After Tomorrow": "後日", - "Lot line is unavailable": "掃描批次不可用", - "Select Date": "請選擇日期", - "Suggest Lot No.": "推薦批號", - "Search by Shop": "搜索商店", - "Search by Truck": "搜索貨車", - "Print DN & Label": "列印提料單和送貨單標籤", - "Print Label": "列印送貨單標籤", - "Reprint Label(s)": "補印標籤", - "Reprint DN Label": "補印送貨單標籤", - "From carton": "起始箱號", - "To carton": "結束箱號", - "Total cartons on shipment": "總箱數", - "From carton must be at least 1": "起始箱號必須至少為 1", - "To carton must be greater than or equal to from carton": "結束箱號必須大於或等於起始箱號", - "Total cartons on shipment must be at least 1": "總箱數必須至少為 1", - "To carton cannot be greater than total cartons on shipment": "結束箱號不能大於總箱數", - "Not Yet Finished Released Do Pick Orders": "未完成提料單", - "Not yet finished released do pick orders": "未完成提料單", - "Released orders not yet completed - click lane to select and assign": "未完成提料單- 點擊貨車班次選擇並分配", - "Ticket Release Table": "查看提貨情況", - "Please take one pick order before printing the draft.": "請先從「撳單/提料單詳情」頁面下方選取提料單,再列印草稿。", - "No released pick order records found.": "目前沒有可用的提料單。", - "EDT - Lane Code (Unassigned/Total)": "預計出發時間 - 貨車班次(未撳數/總單數)", - "Floor ticket": "票別(樓層)", - "2F ticket": "2/F 票", - "4F ticket": "4/F 票", - "4F lane panel legend": "貨車班次 — 裝載序(未撳數/總單數)", - "Loading sequence n": "板{{n}}", - "lot QR code": "批號 QR 碼", - "label Printer" : "標籤打印機", - "A4 Printer" : "A4 打印機", - "Loading Sequence": "裝載序", - "Ticket No": "提票號碼", - "The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated.": "掃描的庫存批行為「不可用」,無法換批或綁定;揀貨行未更新。", - "is unavable. Please check around have available QR code or not.": "此批號不可用,請檢查周圍是否有可用的 QR 碼。", - "Lot switch failed; pick line was not marked as checked.": "換批失敗;揀貨行未標為已核對。", - "Lot confirmation failed. Please try again.": "確認批號失敗,請重試。", - "Powder Mixture": "箱料粉", - "This lot is not yet putaway": "此批次尚未上架", - "Cannot resolve new inventory lot line": "無法解析新批號庫存行(請確認已上架且資料正確)。", - "Pick order line item is null": "提料單行未關聯物料。", - "New lot line item does not match pick order line item": "新批號行的物料與提料單行不一致。", - "Pick order line {{id}} not found": "找不到有關提料單行的資料。", - "SuggestedPickLot not found for pickOrderLineId {{polId}}": "找不到該提料單行的建議揀貨批", - "SuggestedPickLot qty is invalid: {{qty}}": "建議揀貨數量無效:{{qty}}。", - "Reject switch lot: available {{available}} less than required {{required}}": "此批次貨品已被其他送貨單留起,請掃描其他批次。", - "Reject switch lot: picked {{picked}} already greater or equal required {{required}}": "換批被拒:已揀數量({{picked}})已達或超過建議量({{required}}),無法再拆分換批。", - "Lot status is unavailable. Cannot switch or bind; pick line was not updated.": "批號狀態為「不可用」,無法換批或綁定;揀貨行未更新。", - "No lot rows. Select a line in the table above.": "尚無批號資料。請在上方表格勾選一行提料單明細。", - "No stock out line for this lot": "此批號尚無出庫行,無法提交。" - } \ No newline at end of file +{ + "Purchase Order": "採購訂單", + "Code": "編號", + "Pick Order Code": "提料單編號", + "Item Code": "貨品編號", + "OrderDate": "下單日期", + "Details": "詳情", + "Supplier": "供應商", + "Status": "來貨狀態", + "N/A": "不適用", + "Release Pick Orders": "放單", + "released": "已放單", + "Loading...": "加載中", + "Suggestion success": "建議成功", + "Scan pick success": "掃描提料成功", + "Remark": "備註", + "Available Qty": "可用數量", + "Picked Qty": "已提料數量", + "Escalated": "上報狀態", + "NotEscalated": "無上報", + "Assigned To": "已分配", + "Progress": "進度", + "Select Remark": "選擇備註", + "Just Complete": "已完成", + "Skip": "跳過", + "if need just edit number, please scan the lot again": "如果需要只修改數量,請重新掃描批次。", + "Total qty (actual pick + miss + bad) cannot exceed available qty: {available}": "總數量(實際提料 + 遺失 + 不良)不能超過可用數量:{{available}}", + "Confirm Assignment": "確認分配", + "Required Date": "所需日期", + "Store": "位置", + "Available Orders": "可用訂單", + "This lot is rejected, please scan another lot.": "此批次已拒收,請掃描另一個批次。", + "Lane Code": "車線號碼", + "Fetching all matching records...": "正在獲取所有匹配的記錄...", + "Edit": "改數", + "Submit Qty": "提交數量", + "Just Completed": "已完成", + "Just Completed (workbench): requires a valid lot number and quantity; expired rows must not use this button.": "已完成(工作台):需有效批號與可提交數量;過期列請勿使用此按鈕。", + "Do you want to start?": "確定開始嗎?", + "Start": "開始", + "Pick Order Code(s)": "提料單編號", + "Delivery Order Code(s)": "提料單編號", + "Start Success": "開始成功", + "Qty will submit": "提交數量", + "Truck Lance Code": "車線號碼", + "Pick Order Codes": "提料單編號", + "Pick Order Lines": "提料單行數", + "Delivery Order Codes": "提料單編號", + "Delivery Order Lines": "送貨單行數", + "Lines Per Pick Order": "每提料單行數", + "Pick Orders Details": "提料單詳情", + "Lines": "行數", + "Before Today": "以前", + "Truck X": "車線-X", + "Finsihed good items": "成品項目", + "kinds": "款", + "Completed Date": "完成日期", + "Completed Time": "完成時間", + "Delivery Order": "送貨單", + "items": "項目", + "Select Pick Order:": "選擇提料單:", + "No Stock Available": "沒有庫存可用", + "is expired. Please check around have available QR code or not.": "已過期。請檢查周圍是否有可用的 QR 碼。", + "Start Fail": "開始失敗", + "Start PO": "開始採購訂單", + "Do you want to complete?": "確定完成嗎?", + "Complete": "完成", + "Complete Success": "完成成功", + "Complete Fail": "完成失敗", + "Complete Pick Order": "完成提料單", + "General": "一般", + "Bind Storage": "綁定倉位", + "itemNo": "貨品編號", + "itemName": "貨品名稱", + "qty": "訂單數", + "Require Qty": "需求數", + "uom": "計量單位", + "total weight": "總重量", + "weight unit": "重量單位", + "price": "訂單貨值", + "processed": "已入倉", + "expiryDate": "到期日", + "acceptedQty": "是次訂單/來貨/巳來貨數", + "weight": "重量", + "start": "開始", + "qc": "質量控制", + "escalation": "上報", + "stock in": "入庫", + "putaway": "上架", + "delete": "刪除", + "qty cannot be greater than remaining qty": "數量不能大於剩餘數", + "Record pol": "記錄採購訂單", + "Add some entries!": "請添加條目", + "draft": "草稿", + "pending": "待處理", + "determine1": "上報1", + "determine2": "上報2", + "determine3": "上報3", + "receiving": "收貨中", + "received": "已收貨", + "completed": "已完成", + "rejected": "已拒絕", + "success": "成功", + "acceptedQty must not greater than": "接受數量不得大於", + "minimal value is 1": "最小值為1", + "value must be a number": "值必須是數字", + "qc Check": "質量控制檢查", + "Please select QC": "請選擇質量控制", + "failQty": "失敗數", + "select qc": "選擇質量控制", + "enter a failQty": "請輸入失敗數", + "qty too big": "數量過大", + "sampleRate": "抽樣率", + "sampleWeight": "樣本重量", + "totalWeight": "總重量", + "Escalation": "上報", + "to be processed": "待處理", + "Stock In Detail": "入庫詳情", + "productLotNo": "產品批號", + "receiptDate": "收貨日期", + "acceptedWeight": "接受重量", + "productionDate": "生產日期", + "reportQty": "上報數", + "Default Warehouse": "預設倉庫", + "Select warehouse": "選擇倉庫", + "Putaway Detail": "上架詳情", + "LotNo": "批號", + "Po Code": "採購訂單編號", + "No Warehouse": "沒有倉庫", + "Please scan warehouse qr code.": "請掃描倉庫 QR 碼。", + "Reject": "拒絕", + "submit": "確認提交", + "print": "列印", + "bind": "綁定", + "Total must equal Required Qty. Missing: {diff}": "總數量必須等於所需數量。缺少:{{diff}}", + "Total must equal Required Qty. Exceeds by: {diff}": "總數量必須等於所需數量。超出:{{diff}}", + "Batch": "批量", + "Single": "單量", + "Release Type": "放單類型", + "isExtra order": "加單", + "Etra": "加單", + "Exit Etra view": "離開加單檢視", + "Etra Pick Order Detail": "加單", + "Etra incomplete badge tooltip": "當日未完成加單票:{{count}} 張(待處理/已發佈,不含已結案)", + "Etra incomplete badge tooltip none": "目前無未完成加單票", + "Back to normal assign tab": "返回一般指派分頁", + "Enter isExtra workbench view?": "進入加單檢視?", + "Etra view groups all add-on tickets by shop and lane for the selected date.": "加單檢視會依選定日期,將 isExtra 票依店鋪與車線顯示。", + "Etra Ticket Notice": "目前是加單票,顯示與操作已切換為加單模式。", + "Pick Order": "提料單", + "Type": "類型", + "Product Type": "貨品類型", + "Reset": "重置", + "Search": "搜索", + "Pick Orders": "提料單", + "Consolidated Pick Orders": "合併提料單", + "Pick Order No.": "提料單編號", + "Pick Order Date": "提料單日期", + "Pick Order Status": "提貨狀態", + "Pick Order Type": "提料單類型", + "Consolidated Code": "合併編號", + "type": "類型", + "Items": "項目", + "Target Date": "需求日期", + "Released By": "發佈者", + "Target Date From": "目標日期", + "Target Date To": "目標日期到", + "Consolidate": "合併", + "Stock Unit": "庫存單位", + "create": "新增", + "detail": "詳情", + "Pick Order Detail": "撳單/提料單詳情", + "item": "貨品", + "Unit": "單位", + "reset": "重置", + "targetDate": "目標日期", + "remove": "移除", + "release": "發佈", + "location": "位置", + "suggestedLotNo": "建議批次", + "lotNo": "批次", + "item name": "貨品名稱", + "Item Name": "貨品名稱", + "approval": "審核", + "lot change": "批次變更", + "checkout": "出庫", + "Search Items": "搜索貨品", + "Search Results": "可選擇貨品", + "Second Search Results": "第二搜索結果", + "Second Search Items": "第二搜索項目", + "Second Search": "第二搜索", + "Item": "貨品", + "Order Quantity": "貨品需求數", + "Current Stock": "現時可用庫存", + "Selected": "已選", + "Select Items": "選擇貨品", + "Assign": "分派提料單", + "Release": "放單", + "Pick Execution": "進行提料", + "Create Pick Order": "建立貨品提料單", + "Consumable": "消耗品", + "Material": "食材", + "Job Order": "工單", + "End Product": "成品", + "Lot Expiry Date": "到期日", + "Lot Location": "位置", + "Available Lot": "可用提料數", + "Lot Required Pick Qty": "所需數", + "Lot Actual Pick Qty": "此單將提數", + "Lot#": "批號", + "Submit": "提交", + "Created Items": "已建立貨品", + "Create New Group": "建立新提料分組", + "Group": "分組", + "Qty Already Picked": "已提料數", + "Select Job Order Items": "選擇工單貨品", + "failedQty": "不合格項目數", + "remarks": "備註", + "Qc items": "QC 項目", + "qcItem": "QC 項目", + "QC Info": "QC 資訊", + "qcResult": "QC 結果", + "acceptQty": "接受數", + "Escalation History": "上報歷史", + "Group Code": "分組編號", + "Job Order Code": "工單編號", + "QC Check": "QC 檢查", + "QR Code Scan": "QR Code掃描", + "Pick Order Details": "提料單詳情", + "Partial quantity submitted. Please submit more or complete the order.": "已提料部分數量。請提交更多或完成訂單。", + "Pick order completed successfully!": "提料單完成成功!", + "Lot has been rejected and marked as unavailable.": "批號已拒絕並標記為不可用。", + "This order is insufficient, please pick another lot.": "此訂單不足,請選擇其他批號。", + "Please finish QR code scan, QC check and pick order.": "請完成 QR 碼掃描、QC 檢查和提料。", + "No data available": "沒有資料", + "Please submit the pick order.": "請提交提料單。", + "Item lot to be Pick:": "批次貨品提料:", + "Report and Pick another lot": "上報並需重新選擇批號", + "Accept Stock Out": "接受出庫", + "Pick Another Lot": "欠數,並重新選擇批號", + "Delivery Note Code": "送貨單編號", + "A4 Printer": "A4 打印機", + "Label Printer": "標籤打印機", + "Please select a printer first": "請先選擇打印機", + "Please select a label printer first": "請先選擇標籤打印機", + "Lot No": "批號", + "Expiry Date": "到期日", + "Location": "位置", + "All Pick Order Lots": "所有提料單批次", + "Completed": "已完成", + "Finished Good Order": "成品出倉", + "Assign and Release": "分派並放單", + "Original Available Qty": "原可用數", + "Remaining Available Qty": "剩餘可用數", + "Please submit pick order.": "請提交提料單。", + "Please finish QR code scan and pick order.": "請完成 QR 碼掃描和提料。", + "Please finish QR code scanand pick order.": "請完成 QR 碼掃描和提料。", + "First created group": "首次建立分組", + "Latest created group": "最新建立分組", + "Manual Input": "手動輸入", + "QR Code Scan for Lot": " QR 碼掃描批次", + "Processing QR code...": "處理 QR 碼...", + "The input is not the same as the expected lot number.": "輸入的批次號碼與預期的不符。", + "Verified successfully!": "驗證成功!", + "Cancel": "取消", + "storing": "待品檢入倉", + "pick successful": "提料成功", + "Insufficient available quantity on lot (may have been picked by another user)": "掃描的批次已被其他用戶完全提料。請掃描其他批次。", + "Scan": "掃描", + "Before today": "今天之前", + "Scanned": "已掃描", + "Loading data...": "正在載入數據...", + "No available stock for this item": "沒有可用庫存", + "No lot details available for this item": "沒有批次詳情", + "Current stock is insufficient or unavailable": "現時可用庫存不足或不可用", + "Please check inventory status": "請檢查庫存狀態", + "Rows per page": "每頁行數", + "QR Scan Result:": "QR 掃描結果:", + "Action": "操作", + "Please finish pick order.": "請完成提料。", + "Lot": "批號", + "Assign Pick Orders": "分派提料單", + "Selected Pick Orders": "已選擇提料單數量", + "Please assgin/release the pickorders to picker": "請分派/放單提料單給提料員。", + "Assign To": "分派給", + "No Group": "沒有分組", + "Selected items will join above created group": "已選擇的貨品將加入以上建立的分組", + "Issue": "問題", + "Pick Execution Issue Form": "提料問題表單", + "Lot line is unavailable": "掃描批次不可用", + "This form is for reporting issues only. You must report either missing items or bad items.": "此表單僅用於報告問題。您必須報告缺少的物品或不良物品。", + "Bad item Qty": "不良貨品數量", + "Missing item Qty": "貨品遺失數量", + "Missing Item Qty": "貨品遺失數量", + "Bad Item Qty": "不良貨品數量", + "Bad Package Qty": "不良包裝數量", + "Lot line is not available (status=UNAVAILABLE)": "掃描批次不可用", + "Actual Pick Qty": "實際提料數量", + "Required Qty": "所需數量", + "Issue Remark": "問題描述", + "Handler": "提料員", + "Qty is required": "必需輸入數量", + "Qty is not allowed to be greater than remaining available qty": "輸入數量不能大於剩餘可用數量", + "Qty is not allowed to be greater than required qty": "輸入數量不能大於所需數量", + "At least one issue must be reported": "至少需要報告一個問題", + "issueRemark": "問題描述是必需的", + "handler": "提料員", + "Max": "最大值", + "Route": "路線", + "Index": "編號", + "No FG pick orders found": "沒有成品提料單", + "Finish Scan?": "完成掃描?", + "Delivery Code": "出倉單編號", + "Shop PO Code": "訂單編號", + "Shop ID": "商店編號", + "Truck No.": "車線編號", + "Departure Time": "車線出發時間", + "Shop Name": "商店名稱", + "Shop Address": "商店地址", + "Delivery Date": "目標日期", + "Pick Execution 2/F": "進行提料 2/F", + "Pick Execution 4/F": "進行提料 4/F", + "Pick Execution Detail": "進行提料詳情", + "Submit Required Pick Qty": "提交所需提料數量", + "Scan Result": "掃描結果", + "Ticket No.": "提票號碼", + "Start QR Scan": "開始QR掃描", + "Stop QR Scan": "停止QR掃描", + "Scanning...": "掃描中...", + "Print DN/Label": "列印送貨單/標籤", + "Store ID": "儲存編號", + "QR code does not match any item in current orders.": "QR 碼不符合當前訂單中的任何貨品。", + "Lot Number Mismatch": "批次號碼不符", + "The scanned item matches the expected item, but the lot number is different. Do you want to proceed with this different lot?": "掃描的貨品與預期的貨品相同,但批次號碼不同。您是否要繼續使用不同的批次?", + "The scanned item matches the expected item, but the lot number is different. Scan again to confirm: scan the expected lot QR to keep the suggested lot, or scan the other lot QR again to switch.": "掃描貨品相同但批次不同。請再掃描一次以確認:掃描「建議批次」的 QR 可沿用該批次;再掃描「另一批次」的 QR 則切換為該批次。", + "Expected Lot:": "預期批次:", + "Scanned Lot:": "掃描批次:", + "Confirm": "確認", + "Update your suggested lot to the this scanned lot": "更新您的建議批次為此掃描的批次", + "Print Draft": "列印草稿", + "Print Pick Order and DN Label": "列印提料單和送貨單標籤", + "Print Pick Order": "列印提料單", + "Print DN Label": "列印送貨單標籤", + "Print All Draft": "列印全部草稿", + "If you confirm, the system will:": "如果您確認,系統將:", + "After you scan to choose, the system will update the pick line to the lot you confirmed.": "確認後,系統會將您選擇的批次套用到對應提料行。", + "Or use the Confirm button below if you cannot scan again (same as scanning the other lot again).": "若無法再掃描,可按下「確認」以切換為剛才掃描到的批次(與再掃一次該批次 QR 相同)。", + "Lot switch failed": "批次切換失敗", + "The system could not switch to the scanned lot. Review the lots below, then tap Confirm to retry.": "系統無法切換至掃描的批次。請核對下方批次後按「確認」重試。", + "You can also scan again: expected lot QR keeps the suggested line; scanned lot QR retries the switch.": "您也可以再掃描:掃描建議批次 QR 可保留該行;掃描欲切換批次 QR 可再次嘗試切換。", + "QR code verified.": "QR 碼驗證成功。", + "Order Finished": "訂單完成", + "Submitted Status": "提交狀態", + "Pick Execution Record": "提料執行記錄", + "Delivery No.": "送貨單編號", + "Total": "總數", + "Completed DO pick orders: ": "已完成送貨單提料單:", + "No completed DO pick orders found": "沒有已完成送貨單提料單", + "Enter the number of cartons: ": "請輸入總箱數", + "Number of cartons": "箱數", + "Select an action for the assigned pick orders.": "選擇分配提料單的動作。", + "Detail": "詳情", + "consoCode": "合併編號", + "status": "狀態", + "Items Included": "貨品包括", + "Pick Order Included": "提料單包括", + "No created items": "沒有已建立的貨品", + "Please select item": "請選擇貨品", + "enter a qty": "請輸入數量", + "update qc info": "更新QC資訊", + "All lots must be completed before printing": "所有批次必須完成才能打印", + "Assigning pick order...": "分配提料單...", + "Enter the number of cartons:": "請輸入總箱數", + "Finished Good Detail": "成品提貨詳情", + "Finished Good Record": "成品提貨記錄", + "Finished Good Record (All)": "成品提貨記錄(全部)", + "All dates": "全部日期", + "Search date": "搜索日期", + "Hide Completed: OFF": "完成: OFF", + "Hide Completed: ON": "完成: ON", + "Number must be at least 1": "數量至少為1", + "Printed Successfully.": "成功列印", + "Product": "產品", + "You need to enter a number": "您需要輸入一個數字", + "Available in warehouse": "在倉庫中可用", + "Describe the issue with bad items": "描述不良貨品的問題", + "Enter pick qty or issue qty": "請輸入提料數量或不良數量", + "Invalid qty": "無效數量", + "Note:": "注意:", + "Qty is not allowed to be greater than required/available qty": "數量不能大於所需/可用數量", + "Still need to pick": "仍需提料", + "Total exceeds required qty": "總數超過所需數量", + "submitting": "提交中", + "Back to List": "返回列表", + "Delivery No": "送貨單編號", + "FG orders": "成品訂單", + "View Details": "查看詳情", + "No Item": "沒有貨品", + "None": "沒有", + "Add Selected Items to Created Items": "將已選擇的貨品添加到已建立的貨品中", + "All pick orders created successfully": "所有提料單建立成功", + "Failed to create group": "建立分組失敗", + "Invalid date format": "日期格式無效", + "Item already exists in created items": "貨品已存在於已建立的貨品中", + "Job Order not found or has no items": "工單不存在或沒有貨品", + "No results found": "沒有結果", + "Please enter at least code or name": "請輸入至少編號或名稱", + "Please enter quantity for all selected items": "請輸入所有已選擇的貨品的數量", + "Please select at least one item to submit": "請選擇至少一個貨品提交", + "Please select group and enter quantity for all selected items": "請選擇分組並輸入所有已選擇的貨品的數量", + "Please select group for all selected items": "請選擇分組對所有已選擇的貨品", + "Please select product type": "請選擇產品類型", + "Please select target date": "請選擇目標日期", + "Please select type": "請選擇類型", + "Search Criteria": "搜索條件", + "Processing...": "處理中", + "Failed items must have failed quantity": "不合格的貨品必須有不合格數量", + "QC items without result": "QC項目沒有結果", + "confirm putaway": "確認上架", + "email supplier": "發送郵件給供應商", + "qc processing": "QC處理", + "submitStockIn": "提交入庫", + "not default warehosue": "不是默認倉庫", + "printQty": "打印數量", + "Shop": "商店名稱", + "warehouse": "倉庫", + "Add Record": "添加記錄", + "Clean Record": "清空記錄", + "Select": "選擇", + "Close": "關閉", + "Truck": "車線", + "Date": "日期", + "Delivery Order Code": "送貨單編號", + "Escalation Info": "升級信息", + "Escalation Result": "升級結果", + "acceptQty must not greater than": "接受數量不能大於", + "supervisor": "主管", + "No Qc": "沒有QC", + "receivedQty": "接收數量", + "stock in information": "入庫信息", + "No Uom": "沒有單位", + "Input quantity cannot exceed": "輸入數量不能超過", + "Quantity cannot be negative": "數量不能為負數", + "Enter bad item quantity (required if no missing items)": "請輸入不良數量(如果沒有缺少項目)", + "Enter missing quantity (required if no bad items)": "請輸入缺少數量(如果沒有不良項目)", + "Submit All Scanned": "提交所有已掃描項目", + "Submitting...": "提交中...", + "COMPLETED": "已完成", + "Confirm print: (": "確認列印全部草稿?(總數量:", + "piece(s))": "份)", + "Printing...": "列印中", + "Please wait...": "請稍後", + "No available pick order(s) for this floor.": "此樓層沒有可用的提料單", + "You already have a pick order in progess. Please complete it first before taking next pick order.": "請先完成目前的提料單,再提取下一張", + "Error occurred during assignment.": "提料單分配錯誤", + "Info": "消息", + "Warning": "警告", + "Error": "錯誤", + "Batch Print": "批量列印", + "No entries available": "該樓層未有需處理訂單", + "Today": "是日", + "Tomorrow": "翌日", + "packaging": "提料中", + "This lot is not available, please scan another lot.": "此批號不可用,請掃描其他批號。", + "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.": "請檢查周圍是否有 QR 碼,可能有剛剛入庫、轉移入庫或轉移出庫的 QR 碼。", + "Lot is expired (expiry={{expiry}})": "掃描批號已過期(到期日={{expiry}})", + "Day After Tomorrow": "後日", + "Select Date": "請選擇日期", + "Suggest Lot No.": "推薦批號", + "Search by Shop": "搜索商店", + "Search by Truck": "搜索貨車", + "Print DN & Label": "列印提料單和送貨單標籤", + "Print Label": "列印送貨單標籤", + "Reprint Label(s)": "補印標籤", + "Reprint DN Label": "補印送貨單標籤", + "From carton": "起始箱號", + "To carton": "結束箱號", + "Total cartons on shipment": "總箱數", + "From carton must be at least 1": "起始箱號必須至少為 1", + "To carton must be greater than or equal to from carton": "結束箱號必須大於或等於起始箱號", + "Total cartons on shipment must be at least 1": "總箱數必須至少為 1", + "To carton cannot be greater than total cartons on shipment": "結束箱號不能大於總箱數", + "Not Yet Finished Released Do Pick Orders": "未完成提料單", + "Not yet finished released do pick orders": "未完成提料單", + "Released orders not yet completed - click lane to select and assign": "未完成提料單- 點擊貨車班次選擇並分配", + "Ticket Release Table": "查看提貨情況", + "Please take one pick order before printing the draft.": "請先從「撳單/提料單詳情」頁面下方選取提料單,再列印草稿。", + "No released pick order records found.": "目前沒有可用的提料單。", + "EDT - Lane Code (Unassigned/Total)": "預計出發時間 - 貨車班次(未撳數/總單數)", + "Floor ticket": "票別(樓層)", + "2F ticket": "2/F 票", + "4F ticket": "4/F 票", + "4F lane panel legend": "貨車班次 — 裝載序(未撳數/總單數)", + "Loading sequence n": "板{{n}}", + "lot QR code": "批號 QR 碼", + "label Printer": "標籤打印機", + "Loading Sequence": "裝載序", + "Ticket No": "提票號碼", + "The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated.": "掃描的庫存批行為「不可用」,無法換批或綁定;揀貨行未更新。", + "is unavable. Please check around have available QR code or not.": "此批號不可用,請檢查周圍是否有可用的 QR 碼。", + "Lot switch failed; pick line was not marked as checked.": "換批失敗;揀貨行未標為已核對。", + "Lot confirmation failed. Please try again.": "確認批號失敗,請重試。", + "Powder Mixture": "箱料粉", + "This lot is not yet putaway": "此批次尚未上架", + "Cannot resolve new inventory lot line": "無法解析新批號庫存行(請確認已上架且資料正確)。", + "Pick order line item is null": "提料單行未關聯物品。", + "New lot line item does not match pick order line item": "新批號行的物品與提料單行不一致。", + "Pick order line {{id}} not found": "找不到有關提料單行的資料。", + "SuggestedPickLot not found for pickOrderLineId {{polId}}": "找不到該提料單行的建議揀貨批", + "SuggestedPickLot qty is invalid: {{qty}}": "建議揀貨數量無效:{{qty}}。", + "Reject switch lot: available {{available}} less than required {{required}}": "此批次貨品已被其他送貨單留起,請掃描其他批次。", + "Reject switch lot: picked {{picked}} already greater or equal required {{required}}": "換批被拒:已揀數量({{picked}})已達或超過建議量({{required}}),無法再拆分換批。", + "Lot status is unavailable. Cannot switch or bind; pick line was not updated.": "批號狀態為「不可用」,無法換批或綁定;揀貨行未更新。", + "No lot rows. Select a line in the table above.": "尚無批號資料。請在上方表格勾選一行提料單明細。", + "No stock out line for this lot": "此批號尚無出庫行,無法提交。", + "No data available for this pick order.": "此提料單沒有可用資料。", + "Report missing or bad items": "報告缺失或不良物品", + "passed": "合格", + "failed": "不合格", + "confirm_accept_with_fail": "有不合格檢查項目,確認接受出庫?" +} diff --git a/src/i18n/zh/po.json b/src/i18n/zh/po.json index a13c857..b16d4d9 100644 --- a/src/i18n/zh/po.json +++ b/src/i18n/zh/po.json @@ -1,16 +1,16 @@ { - "code": "代码", - "status": "來貨狀態", - "escalated": "已升级", - "notEscalated": "未升级", - "All": "全部", - "Pending": "待处理", - "Receiving": "接收中", - "Completed": "已完成", - "Purchase Order": "采购订单", - "Details": "详情", - "OrderDate": "订单日期", - "Supplier": "供应商", - "Escalated": "已升级", - "NotEscalated": "未升级" -} \ No newline at end of file + "code": "代码", + "status": "來貨狀態", + "escalated": "已升级", + "notEscalated": "未升级", + "All": "全部", + "Pending": "待处理", + "Receiving": "接收中", + "Completed": "已完成", + "Purchase Order": "采购订单", + "Details": "详情", + "OrderDate": "订单日期", + "Supplier": "供应商", + "Escalated": "已升级", + "NotEscalated": "未升级" +} diff --git a/src/i18n/zh/poWorkbench.json b/src/i18n/zh/poWorkbench.json index 7c656ae..6f84e8e 100644 --- a/src/i18n/zh/poWorkbench.json +++ b/src/i18n/zh/poWorkbench.json @@ -1,9 +1,10 @@ { + "PO Workbench": "採購單工作台", "searchCriteria": { "poPlaceholder": "請掃描PO二維碼或輸入單號", - "ariaPoSearch": "PO number search", - "ariaClearPo": "Clear PO number", - "ariaToggleAdvanced": "Toggle advanced search" + "ariaPoSearch": "搜索採購單號", + "ariaClearPo": "清除採購單號", + "ariaToggleAdvanced": "切換進階搜索" }, "advanced": { "title": "進階搜索", diff --git a/src/i18n/zh/printer.json b/src/i18n/zh/printer.json new file mode 100644 index 0000000..8f15cce --- /dev/null +++ b/src/i18n/zh/printer.json @@ -0,0 +1,8 @@ +{ + "Create Printer": "新增列印機", + "Edit": "編輯", + "PDF Preview": "PDF 預覽", + "Print failed": "列印失敗", + "Print job sent successfully": "列印作業已成功送出", + "Printer": "列印機" +} diff --git a/src/i18n/zh/production.json b/src/i18n/zh/production.json new file mode 100644 index 0000000..446f0e9 --- /dev/null +++ b/src/i18n/zh/production.json @@ -0,0 +1,3 @@ +{ + "Production Process": "生產流程" +} diff --git a/src/i18n/zh/productionProcess.json b/src/i18n/zh/productionProcess.json new file mode 100644 index 0000000..1ded5c0 --- /dev/null +++ b/src/i18n/zh/productionProcess.json @@ -0,0 +1,221 @@ +{ + "Action": "操作", + "All": "全部", + "An error has occurred. Please try again later.": "發生錯誤,請稍後再試。", + "Are you sure you want to delete this process?": "您確定要刪除此工序嗎?", + "Assignment successful": "分配成功", + "Assume End Time": "預計完成時間", + "Assume Time Need": "預計所需時間", + "Auto-refresh every 1 minute": "每1分鐘自動刷新", + "Auto-refresh every 10 minutes": "每10分鐘自動刷新", + "Back": "返回", + "Back to List": "返回列表", + "Bag": "包裝袋", + "Bag Consumption": "包裝袋消耗", + "Balance": "可用數量", + "Base Qty": "基本數量", + "Base UOM": "基本單位", + "Batch Count": "批數", + "BoM Material": "BOM 材料", + "Bom Req. Qty": "BOM", + "Bom Uom": "BOM 單位", + "By-product": "副產品", + "Cancel": "取消", + "Cancel Job Order": "取消工單", + "Cancel job order confirm message": "確定要取消此工單嗎?工單將從列表中隱藏。", + "Changeover Time": "生產後轉換時間", + "Changeover Time (mins)": "生產後轉換時間(分鐘)", + "Code": "編號", + "Complete Step": "完成步驟", + "Completed": "完成", + "Completed Step": "完成步驟", + "Confirm": "確認", + "Confirm cancel job order": "確認取消工單", + "Confirm delete job order": "確認刪除工單", + "Confirm to Pass this Process?": "確認要通過此工序嗎?", + "Confirm to update this Job Order?": "確認要完成此工單嗎?", + "Consumed Qty": "消耗數量", + "Continue": "繼續", + "Count of Job Orders": "已處理工單", + "Date": "日期", + "Defect": "不良品", + "Delete job order confirm message": "確定要刪除此工單嗎?此操作無法復原。", + "Description": "描述", + "Duration hours": "{{count}} 小時", + "Duration minutes": "{{count}} 分鐘", + "Duration seconds": "{{count}} 秒", + "End Time": "完成時間", + "Enter any additional production notes...": "輸入其他生產備註...", + "Equipment": "設備", + "Equipment Code": "設備編號", + "Equipment Name and Code": "設備名稱及編號", + "Equipment Type": "設備類型", + "EquipmentType-EquipmentName-Code": "設備類型-設備名稱-編號", + "Estimated Completion Time": "預計完成時間", + "Executing": "執行中", + "FG / WIP Item": "成品/半成品", + "Filtered": "已過濾", + "Finished Time": "完成時間", + "Finished lines": "已完成流程", + "In Progress": "進行中", + "In progress": "進行中", + "Invalid Job Order Id": "無效工單編號", + "Invalid Stock In Line Id": "無效庫存行ID", + "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原 | 時間次序 | 複雜度", + "Item": "物品", + "Item Code": "物品編號", + "Item Name": "物品名稱", + "Job Details": "工單編號及生產產品", + "Job Order": "工單", + "Job Order Code": "工單編號", + "Job Order Info": "工單信息", + "Job Order No.": "工單編號", + "Job Order and Product": "工單及貨品", + "Job Order Production Process": "工單生產流程", + "Job Process Status Dashboard": "儀表板 - 工單狀態", + "Job Type": "工單類型", + "Job process detail mode label": "工序格顯示", + "Job process detail: equipment": "設備", + "Job process detail: handler": "員工", + "Job process detail: process name": "工序", + "Job process detail: time": "時間", + "Just Pass": "已完成", + "Last updated": "最後更新", + "Lines with insufficient stock: ": "未能提料項目數量: ", + "Lines with sufficient stock: ": "可提料項目數量: ", + "Lot No": "批號", + "Matching Stock": "工單對料", + "Material Code": "材料清單", + "N/A": "不適用", + "Name": "名稱", + "Next page": "下一頁", + "No data available": "沒有資料", + "No data found": "沒有找到資料", + "No.": "編號", + "Now": "現時", + "Operator": "員工資訊", + "Operator KPI Dashboard": "儀表板 - 操作員KPI概覽", + "Operator Name & No.": "操作員名稱及編號", + "Order Complete": "訂單完成", + "Output from Process": "工序產出", + "Output Quantity": "產出數量", + "Over Time": "超時", + "Overall Time Remaining": "總剩餘時間", + "Passed Step": "通過步驟", + "Pause": "暫停", + "Pause Reason": "暫停原因", + "Paused": "已暫停", + "Pending": "待處理", + "Plan start (from)": "開始日期(從)", + "Plan start (to)": "開始日期(至)", + "Please scan equipment code": "請掃描設備編號", + "Please scan operator code first": "請先掃描操作員編號", + "Please scan staff no": "請掃描員工編號", + "Post Prod Time (Minutes)": "收尾時間(分鐘)", + "Prep Time (Minutes)": "準備時間(分鐘)", + "Previous page": "上一頁", + "Printer": "列印機", + "Process": "工序", + "Process & Equipment": "製程與設備", + "Process Description": "工序說明", + "Process Name": "工序名稱", + "Process Start Time": "工序開始時間", + "Process Type": "工序類型", + "Process page summary": "工序 {{from}}–{{to}} / 共 {{total}} 道", + "Processing Time": "生產時間", + "Processing Time (mins)": "步驟時間(分鐘)", + "Product process status": "生產流程狀態", + "Production Date": "生產日期", + "Production Equipment Status Dashboard": "儀表板 - 生產設備最新狀態", + "Production Output Data": "生產輸出數據", + "Production Output Data Entry": "生產輸出數據輸入", + "Production Priority": "生產優先序", + "Production Process": "工藝流程", + "Production Process Line Remark": "工藝明細", + "Production Process Steps": "生產流程步驟", + "Production Time Remaining": "生產剩餘時間", + "Production date": "生產日期", + "Put Awayed Job Orders": "已上架工單", + "Qty": "數量", + "Quality Check": "品質檢查", + "Quantity": "數量", + "Reason": "原因", + "Release": "放單", + "Remaining Time": "剩餘時間", + "Remaining Time (min)": "剩餘時間(分鐘)", + "Remark": "備註", + "Req. Qty": "需求數量", + "Required Qty": "需求數量", + "Required Time": "所需時間", + "SEQ": "步驟", + "Save": "儲存", + "Scan Operator & Equipment": "掃描操作員和設備", + "Scrap": "損耗", + "Scrap Qty": "損耗數量", + "Search date": "搜索日期", + "Searched Item": "已搜索物品", + "Select Another Bag Lot": "選擇另一個包裝袋", + "Select Bag": "選擇包裝袋", + "Select Date": "選擇日期", + "Select Printer": "選擇打印機", + "Select Unit": "選擇單位", + "Seq": "步驟", + "Seq No": "加入步驟", + "Setup Time": "生產前預備時間", + "Setup Time (mins)": "生產前預備時間(分鐘)", + "Staff No": "員工編號", + "Start": "開始", + "Start QR Scan": "開始掃碼", + "Start Time": "開始時間", + "Status": "狀態", + "Step": "步驟", + "Step Information": "步驟信息", + "Step Name": "名稱", + "Step Start Time": "步驟開始時間", + "Stock Available": "庫存數", + "Stock Req. Qty": "需求數", + "Stock Status": "庫存狀態", + "Stock UOM": "庫存單位", + "Stop QR Scan": "停止掃碼", + "Submit & Start": "提交並開始", + "Submit Bag Consumption": "提交包裝袋消耗", + "Submitting...": "提交中...", + "Target Production Date": "目標生產日期", + "Time Information(mins)": "時間信息(分鐘)", + "Time Remaining": "剩餘時間", + "Timer Paused": "計時器已暫停", + "Total Processing Time": "總工時", + "Total Time": "總時間", + "Total finished QC job orders": "總完成QC工單數量", + "Total job orders": "總工單數量", + "Total lines: ": "總數量:", + "Type": "類型", + "Unable to get user ID": "無法獲取用戶ID", + "Unit": "單位", + "Unknown": "未知", + "Update Job Order": "完成工單", + "Update Production Priority": "更新生產優先序", + "Update Target Production Date": "更新目標生產日期", + "Update Required Quantity": "更新需求數量", + "Update Time Information": "更新時間信息", + "View": "查看", + "View Details": "查看詳情", + "Wait Time": "等待時間", + "Waiting QC Put Away Job Orders": "待QC上架工單", + "all": "全部", + "drink": "飲料", + "id": "ID", + "mins": "分鐘", + "minutes": "分鐘", + "FG": "成品", + "No processes found for this job order": "找不到此工單的工序", + "SFG": "半成品", + "WIP": "半成品", + "fg": "成品", + "other": "其他", + "processing": "生產中", + "productionProcess": "生產流程", + "sfg": "半成品", + "view stockin": "品檢", + "wip": "半成品" +} diff --git a/src/i18n/zh/project.json b/src/i18n/zh/project.json index f5d7a11..937afa8 100644 --- a/src/i18n/zh/project.json +++ b/src/i18n/zh/project.json @@ -1,16 +1,16 @@ { - "code": "編號", - "FG & Material Demand Forecast Detail": "FG 及材料需求預測詳情", - "Release": "發佈", - "Actions": "操作", - "Product": "物品", - "Details": "詳情", - "View BoM": "查看 BoM", - "description": "描述", - "details": "詳情" - - - - - -} \ No newline at end of file + "code": "編號", + "FG & Material Demand Forecast Detail": "FG 及材料需求預測詳情", + "Release": "發佈", + "Actions": "操作", + "Product": "物品", + "Details": "詳情", + "View BoM": "查看 BOM", + "description": "描述", + "details": "詳情", + "Task": "任務", + "Create Project": "新增專案", + "Projects": "專案", + "Project Code": "專案代碼", + "Project Code and Name": "專案代碼與名稱" +} diff --git a/src/i18n/zh/purchaseOrder.json b/src/i18n/zh/purchaseOrder.json index c64703d..bcff183 100644 --- a/src/i18n/zh/purchaseOrder.json +++ b/src/i18n/zh/purchaseOrder.json @@ -10,8 +10,8 @@ "ETA": "預計到貨日期", "ETA To": "預計到貨日期至", "Details": "詳情", - "Supplier": "供應商", - "Status": "來貨狀態", + "Supplier": "供應商", + "Status": "來貨狀態", "escalateFrom": "上報來源", "Escalated": "上報狀態", "NotEscalated": "無上報", @@ -174,5 +174,11 @@ "submitting": "提交中...", "Submit": "提交", "Total must equal Required Qty. Missing": "總數量必須等於所需數量。缺少:{{diff}}", - "Total must equal Required Qty. Exceeds by": "總數量必須等於所需數量。超出:{{diff}}" + "Total must equal Required Qty. Exceeds by": "總數量必須等於所需數量。超出:{{diff}}", + "Add Record": "新增", + "Clean Record": "重置", + "Will start binding procedure after scanning item qr code.": "掃描物品二維碼後將開始綁定流程。", + "Quantity": "數量", + "Enter quantity": "請輸入數量", + "Enter your remark": "請輸入您的備註" } diff --git a/src/i18n/zh/qcCategory.json b/src/i18n/zh/qcCategory.json index b608a76..8628af9 100644 --- a/src/i18n/zh/qcCategory.json +++ b/src/i18n/zh/qcCategory.json @@ -1,20 +1,20 @@ { - "Qc Category": "品檢模板", - "Qc Category List": "QC 類別列表", - "Qc Category Name": "QC 類別名稱", - "Qc Category Description": "QC 類別描述", - "Qc Category Status": "QC 類別狀態", - "Qc Category Created At": "QC 類別創建時間", - "Qc Category Updated At": "QC 類別更新時間", - "Name": "名稱", - "Code": "編號", - "Description": "描述", - "Details": "詳情", - "Delete": "刪除", - "Qc Item": "QC 項目", - "Create Qc Category": "新增品檢模板", - "Edit Qc Item": "編輯品檢項目", - "Qc Item Details": "品檢項目詳情", - "Cancel": "取消", - "Submit": "儲存" -} \ No newline at end of file + "Qc Category": "品檢模板", + "Qc Category List": "QC 類別列表", + "Qc Category Name": "QC 類別名稱", + "Qc Category Description": "QC 類別描述", + "Qc Category Status": "QC 類別狀態", + "Qc Category Created At": "QC 類別創建時間", + "Qc Category Updated At": "QC 類別更新時間", + "Name": "名稱", + "Code": "編號", + "Description": "描述", + "Details": "詳情", + "Delete": "刪除", + "Qc Item": "QC 項目", + "Create Qc Category": "新增品檢模板", + "Edit Qc Item": "編輯品檢項目", + "Qc Item Details": "品檢項目詳情", + "Cancel": "取消", + "Submit": "儲存" +} diff --git a/src/i18n/zh/qcItemAll.json b/src/i18n/zh/qcItemAll.json index 94b9526..7fbc0e0 100644 --- a/src/i18n/zh/qcItemAll.json +++ b/src/i18n/zh/qcItemAll.json @@ -1,27 +1,27 @@ { "Qc Item All": "QC 綜合管理", - "Item and Qc Category Mapping": "物料與品檢模板映射", + "Item and Qc Category Mapping": "物品與品檢模板映射", "Qc Category and Qc Item Mapping": "品檢模板與品檢項目映射", "Qc Category Management": "品檢模板管理", "Do you want to submit?": "您確定要提交嗎?", "Do you want to delete?": "您確定要刪除嗎?", "Confirm": "確認", - "Item already has type \"{{type}}\" in QcCategory \"{{category}}\". One item can only have each type in one QcCategory.": "物料已於模板「{{category}}」設為類型「{{type}}」,每個物料同一類型只能對應一個模板。", + "Item already has type \"{{type}}\" in QcCategory \"{{category}}\". One item can only have each type in one QcCategory.": "物品已於模板「{{category}}」設為類型「{{type}}」,每個物品同一類型只能對應一個模板。", "Qc Item Management": "品檢項目管理", "Qc Category": "品檢模板", "Qc Item": "品檢項目", - "Item": "物料", - "Item code not found": "物料不存在", - "Error validating item code": "驗證物料編號時發生錯誤", + "Item": "物品", + "Item code not found": "物品不存在", + "Error validating item code": "驗證物品編號時發生錯誤", "Category": "模板", "Category Type": "模板類型", - "Enter item code to validate": "請輸入物料編號以驗證", + "Enter item code to validate": "請輸入物品編號以驗證", "Code": "編號", "Name": "名稱", "Description": "描述", "Type": "類型", "Order": "順序", - "Item Count": "關聯物料數量", + "Item Count": "關聯物品數量", "Qc Item Count": "關聯品檢項目數量", "Qc Category Count": "關聯品檢模板數量", "Actions": "操作", @@ -44,13 +44,13 @@ "Submit Success": "提交成功", "Submit Error": "提交失敗", "Cannot Delete": "無法刪除", - "Cannot delete QcCategory. It has {itemCount} item(s) and {qcItemCount} qc item(s) linked to it.": "無法刪除品檢模板。它有 {itemCount} 個物料和 {qcItemCount} 個品檢項目與其關聯。", + "Cannot delete QcCategory. It has {itemCount} item(s) and {qcItemCount} qc item(s) linked to it.": "無法刪除品檢模板。它有 {itemCount} 個物品和 {qcItemCount} 個品檢項目與其關聯。", "Cannot delete QcItem. It is linked to one or more QcCategories.": "無法刪除品檢項目。它與一個或多個品檢模板關聯。", - "Select Item": "選擇物料", + "Select Item": "選擇物品", "Select Qc Category": "選擇品檢模板", "Select Qc Item": "選擇品檢項目", "Select Type": "選擇類型", - "Item Code": "物料編號", + "Item Code": "物品編號", "Item Name": "產品名稱", "Qc Category Code": "品檢模板編號", "Qc Category Name": "品檢模板名稱", @@ -64,4 +64,3 @@ "Confirm Delete": "確認刪除", "Are you sure you want to delete this item?": "您確定要刪除此項目嗎?" } - diff --git a/src/i18n/zh/qrCodeHandle.json b/src/i18n/zh/qrCodeHandle.json new file mode 100644 index 0000000..7779211 --- /dev/null +++ b/src/i18n/zh/qrCodeHandle.json @@ -0,0 +1,5 @@ +{ + "PDF Preview": "PDF 預覽", + "QR Code Handle": "二維碼列印及下載", + "Equipment": "設備" +} diff --git a/src/i18n/zh/report.json b/src/i18n/zh/report.json new file mode 100644 index 0000000..b3d5b8e --- /dev/null +++ b/src/i18n/zh/report.json @@ -0,0 +1,13 @@ +{ + "Report": "報告", + "title": "報告管理", + "selectReport": "選擇報告", + "reportList": "報告列表", + "selectReportHelper": "選擇報告", + "searchCriteria": "搜索條件", + "downloadPdf": "下載報告 (PDF)", + "downloadExcel": "下載報告 (Excel)", + "generatingPdf": "生成 PDF...", + "generatingExcel": "生成 Excel...", + "generatingReport": "生成報告..." +} diff --git a/src/i18n/zh/schedule.json b/src/i18n/zh/schedule.json index 114cf15..a4b110a 100644 --- a/src/i18n/zh/schedule.json +++ b/src/i18n/zh/schedule.json @@ -1,98 +1,121 @@ -{ "Demand Forecast": "需求預測", - "Total Demand Qty": "總需求量", - "Total Job Orders": "總工單量", - "Demand Qty (Day1)": "需求數量(第 1 天)", - "Demand Qty (Day2)": "需求數量(第 2 天)", - "Demand Qty (Day3)": "需求數量(第 3 天)", - "Demand Qty (Day4)": "需求數量(第 4 天)", - "Demand Qty (Day5)": "需求數量(第 5 天)", - "Demand Qty (Day6)": "需求數量(第 6 天)", - "Demand Qty (Day7)": "需求數量(第 7 天)", - "Demand Forecast Detail": "需求預測詳情", - "Details": "詳情", - "Schedule": "排程", - "Schedule Period": "排程時期", - "Schedule Period To": "排程時期至", - "Schedule Detail": "排程詳情", - "Schedule At": "排程時間", - "Search": "搜索", - "Reset": "重置", - "name": "名稱", - "Name": "名稱", - "type": "類型", - "code": "編號", - "Code": "編號", - "CODE": "編號", - "Product Count": "產品數量", - "Scheduled At": "排程時間", - "Demand Forecast Period": "需求預測時期", - "FG & Material Demand Forecast Detail": "成品及物料需求預測詳情", - "FG & Material Demand Forecast": "成品及物料需求預測", - "Total Estimated Demand Qty": "總預估需求量", - "View By FG": "依成品分類", - "View By Material": "依物料分類", - " (Selected)": " (已選擇)", - "Total FG Item": "總成品項目", - "Release": "發佈", - "Actions": "操作", - "FG Demand List (7 Days)": "成品需求列表(7 天)", - "FG Demand Date": "成品需求日期", - "FG Demand Qty": "成品需求數量", - "Material Demand Date": "物料需求日期", - "Material Demand List": "物料需求列表", - "Available Qty": "可用數量", - "Demand Qty": "需求數量", - "Confirm": "確認", - "Cancel": "取消", - "Edit": "編輯", - "Delete": "刪除", - "Save": "儲存", - "Close": "關閉", - "Add": "新增", - "Selected": "已選擇", - "Unselected": "未選擇", - "Status": "來貨狀態", - "Material Demand List (7 Days)": "物料需求列表(7 天)", - "Mon": "週一", - "Tue": "週二", - "Wed": "週三", - "Thu": "週四", - "Fri": "週五", - "Sat": "週六", - "Sun": "週日", - "Last Month Average Stock": "上個月平均庫存", - "Last Month Average Sales": "上個月平均銷售", - "Safety Stock": "安全庫存", - "Demand Qty (7 Days)": "需求數量(7 天)", - "Estimated Production Time": "預估生產時間", - "Production Priority": "生產優先順序", - "View BoM": "查看物料清單", - "Date": "日期", - "Detail Scheduling": "詳細排程", - "FG Production Schedule": "成品生產排程", - "Production Date": "生產日期", - "Total Job Order": "總工單數量", - "Total Production Qty": "總生產數量", - "Job No.": "工單編號", - "Job Name": "工單名稱", - "Job Type": "工單類型", - "Job Status": "工單狀態", - "Job Priority": "工單優先順序", - "Job Date": "工單日期", - "Job Qty": "工單數量", - "mat": "物料", - "consumables": "消耗品", - "non-consumables": "非消耗品", - "fg": "成品", - "sfg": "半成品", - "item": "物品", - "UoM": "單位", - "Type": "類型", - "Test Detailed Schedule": "測試細排", - "Test Rough Schedule": "測試粗排", - "detailed": "細排", - "Detailed": "細排", - "Product Count(s)": "產品數量", - "Overall": "總計", - "Back": "返回" +{ + "Demand Forecast": "需求預測", + "Total Demand Qty": "總需求量", + "Total Job Orders": "總工單量", + "Demand Qty (Day1)": "需求數量(第 1 天)", + "Demand Qty (Day2)": "需求數量(第 2 天)", + "Demand Qty (Day3)": "需求數量(第 3 天)", + "Demand Qty (Day4)": "需求數量(第 4 天)", + "Demand Qty (Day5)": "需求數量(第 5 天)", + "Demand Qty (Day6)": "需求數量(第 6 天)", + "Demand Qty (Day7)": "需求數量(第 7 天)", + "Demand Forecast Detail": "需求預測詳情", + "Details": "詳情", + "Schedule": "排程", + "Schedule Period": "排程時期", + "Schedule Period To": "排程時期至", + "Schedule Detail": "排程詳情", + "Schedule At": "排程時間", + "Search": "搜索", + "Reset": "重置", + "name": "名稱", + "Name": "名稱", + "type": "類型", + "code": "編號", + "Code": "編號", + "CODE": "編號", + "Product Count": "產品數量", + "Sales Qty": "銷售量", + "Sales UOM": "銷售單位", + "Scheduled At": "排程時間", + "Demand Forecast Period": "需求預測時期", + "FG & Material Demand Forecast Detail": "成品及物料需求預測詳情", + "FG & Material Demand Forecast": "成品及物料需求預測", + "Total Estimated Demand Qty": "總預估需求量", + "View By FG": "依成品分類", + "View By Material": "依物料分類", + " (Selected)": " (已選擇)", + "Total FG Item": "總成品項目", + "Release": "發佈", + "Actions": "操作", + "FG Demand List (7 Days)": "成品需求列表(7 天)", + "FG Demand Date": "成品需求日期", + "FG Demand Qty": "成品需求數量", + "Material Demand Date": "物料需求日期", + "Material Demand List": "物料需求列表", + "Available Qty": "可用數量", + "Demand Qty": "需求數量", + "Confirm": "確認", + "Cancel": "取消", + "Edit": "編輯", + "Delete": "刪除", + "Save": "儲存", + "Close": "關閉", + "Add": "新增", + "Selected": "已選擇", + "Unselected": "未選擇", + "Status": "來貨狀態", + "Stock Qty": "存貨量", + "Material Demand List (7 Days)": "物料需求列表(7 天)", + "Mon": "週一", + "Tue": "週二", + "Wed": "週三", + "Thu": "週四", + "Fri": "週五", + "Sat": "週六", + "Sun": "週日", + "Last Month Average Stock": "上個月平均庫存", + "Last Month Average Sales": "上個月平均銷售", + "Safety Stock": "安全庫存", + "Demand Qty (7 Days)": "需求數量(7 天)", + "Estimated Production Time": "預估生產時間", + "Export Schedule": "匯出排程", + "Production Priority": "生產優先順序", + "View BoM": "查看 BOM", + "Date": "日期", + "Detail Scheduling": "詳細排程", + "Detailed Schedule": "詳細排程", + "FG Production Schedule": "成品生產排程", + "Production Date": "生產日期", + "Total Job Order": "總工單數量", + "Total Production Qty": "總生產數量", + "Job No.": "工單編號", + "Job Name": "工單名稱", + "Job Type": "工單類型", + "Job Status": "工單狀態", + "Job Priority": "工單優先順序", + "Job Date": "工單日期", + "Job Qty": "工單數量", + "mat": "物料", + "consumables": "消耗品", + "non-consumables": "非消耗品", + "fg": "成品", + "sfg": "半成品", + "item": "物品", + "UoM": "單位", + "Type": "類型", + "Test Detailed Schedule": "測試細排", + "Test Rough Schedule": "測試粗排", + "detailed": "細排", + "Detailed": "細排", + "Product Count(s)": "產品數量", + "Overall": "總計", + "Back": "返回", + "Avg Qty Last Month": "最近每日用量", + "Batch Need": "生產批次", + "Days Left": "可用日", + "Output Qty": "每批次生產數", + "Item Name": "物料名稱", + "Priority": "優先順序", + "Mat Code": "物料編號", + "Mat Name": "物料名稱", + "Required Qty": "所需數量", + "Total Qty Need": "所需總數量", + "Purchased Qty": "已採購數量", + "On Hand Qty": "現有庫存量", + "Unavailable Qty": "不可用數量", + "Related Item Code": "相關物料編號", + "Related Item Name": "相關物料名稱", + "Material Summary": "物料摘要", + "Generate Job Order": "生成工單" } diff --git a/src/i18n/zh/scheduling.json b/src/i18n/zh/scheduling.json new file mode 100644 index 0000000..7d8af61 --- /dev/null +++ b/src/i18n/zh/scheduling.json @@ -0,0 +1,5 @@ +{ + "title": "排程", + "Scheduling": "排程", + "ps": "排程" +} diff --git a/src/i18n/zh/settings.json b/src/i18n/zh/settings.json index febef27..8ab1f97 100644 --- a/src/i18n/zh/settings.json +++ b/src/i18n/zh/settings.json @@ -1,14 +1,14 @@ { - "Demand Forecast Setting": "需求預測設定", - "QC Check Template": "QC 檢查模板", - "QC Check Template Details": "QC 檢查模板詳情", - "QC Check Template Name": "QC 檢查模板名稱", - "QC Check Template Description": "QC 檢查模板描述", - "QC Check Template Status": "QC 檢查模板狀態", - "QC Check Template Created At": "QC 檢查模板建立時間", - "QC Check Template Updated At": "QC 檢查模板更新時間", - "Ready to import": "準備匯入", - "Import Po": "匯入PO", - "Import Master Data": "匯入主資料" - -} \ No newline at end of file + "settings": "設定", + "Demand Forecast Setting": "需求預測設定", + "QC Check Template": "QC 檢查模板", + "QC Check Template Details": "QC 檢查模板詳情", + "QC Check Template Name": "QC 檢查模板名稱", + "QC Check Template Description": "QC 檢查模板描述", + "QC Check Template Status": "QC 檢查模板狀態", + "QC Check Template Created At": "QC 檢查模板建立時間", + "QC Check Template Updated At": "QC 檢查模板更新時間", + "Ready to import": "準備匯入", + "Import Po": "匯入PO", + "Import Master Data": "匯入主資料" +} diff --git a/src/i18n/zh/shop.json b/src/i18n/zh/shop.json new file mode 100644 index 0000000..6f34ecd --- /dev/null +++ b/src/i18n/zh/shop.json @@ -0,0 +1,471 @@ +{ + "mtmsRouteWarn_title": "車線資料警示", + "mtmsRouteWarn_tooltipHas": "有 {{count}} 筆潛在衝突", + "mtmsRouteWarn_tooltipNone": "目前無警示", + "mtmsRouteWarn_refresh": "重新整理資料", + "mtmsRouteWarn_refreshing": "載入中…", + "mtmsRouteWarn_copyAll": "複製全部", + "mtmsRouteWarn_parseHint": "有 {{count}} 筆 4F 車線無法判斷星期(未列入警示)", + "mtmsRouteWarn_empty": "目前沒有資料衝突。", + "mtmsRouteWarn_conflict4f": "4F 同店跨車線 · 星期 {{weekday}}", + "mtmsRouteWarn_conflictDep": "非 4F 同店跨車線 · 出車 {{time}}", + "mtmsRouteWarn_shop": "店鋪", + "mtmsRouteWarn_postAddConflict": "新增店鋪後與其他車線資料衝突(請開啟右上角鈴鐺查看明細)。", + "No changes": "沒有變更", + "Saved": "已儲存", + "Failed to save": "儲存失敗", + "Changed": "已變更", + "Logistic": "物流商", + "Driver": "司機", + "Plate": "車牌", + "Departure": "出車", + "Shops": "店鋪", + "Current version": "目前版本", + "new arrangement": "新編排", + "Submitting...": "提交中…", + "saveChanges": "儲存更改", + "warnExpand": "展開", + "warnCollapse": "收合", + "warnClipboardStore": "樓層", + "warnClipboardDep": "出車", + "warnClipboardWeekday": "星期", + "pageTitle": "車線店鋪管理", + "importRoutes": "匯入車線", + "exportRoutes": "匯出車線", + "routeReport": "車線報告", + "departureTooltipNeedShops": "先新增店鋪才能設定出車時間", + "departureTooltipEditSave": "編輯出車時間(按「儲存更改」寫回)", + "departureEditAria": "編輯出車時間", + "saveDisabledTooltip": "請先拖曳、編輯出車時間/裝車順序/物流商等變更後再儲存", + "cancel": "取消", + "drawerClose": "關閉", + "tabBoard": "車線看板", + "tabLogistics": "物流商管理", + "quickIndex": "快速索引", + "versionLogDialogTitle": "版本變更紀錄", + "emDash": "—", + "val_logisticsRequired": "請填寫物流公司、車牌、司機姓名", + "val_logisticsDuplicateName": "已有同名物流公司或暫存新增(請換名稱)", + "val_phoneInvalid": "請輸入有效聯絡電話(數字)", + "err_save": "儲存失敗", + "err_invalidMasterId": "無效的主檔 id", + "err_exportNeedSelection": "請先於左側勾選要匯出的車線", + "err_export": "匯出失敗", + "err_noLanes": "目前無車線資料", + "err_import": "匯入失敗", + "err_dragDuplicateShop": "目標車線已有相同店鋪(同一 shop / 同一 shopCode),無法拖入", + "district_err_name": "請輸入地區名稱", + "district_err_reserved": "「未分類」已內建,請勿重複新增", + "district_err_exists": "此地區已存在", + "route_err_code": "請填車線編號", + "route_err_departure": "請選擇或輸入出車時間", + "route_err_duplicate": "此車線(含備註組合)已存在", + "route_err_create": "新增車線失敗", + "confirm_addShopConflict": "偵測到 {{count}} 筆可能與其他車線衝突(見右上角鈴鐺)。將先加入畫面,按「儲存更改」才寫入後端。仍要加入?", + "confirm_discardDraftShop": "捨棄尚未儲存的「新增店鋪」?", + "confirm_removeShop": "從此車線移除此店鋪?(按「儲存更改」才會寫入)", + "confirm_schedule_removeShop": "從此車線移除此店鋪?此操作將列入預約排程的刪除項目。", + "confirm_clearLane": "確定清空車線「{{laneLabel}}」的 {{count}} 筆店鋪?(按「儲存更改」才會從後端刪除)", + "confirm_departureConflict": "變更出車時間後,偵測到 {{count}} 筆可能衝突(見鈴鐺)。仍要套用?", + "drag_blockDraftShop": "尚未儲存的「新增店鋪」請先按「儲存更改」寫入,或從卡片刪除草稿後再拖曳。", + "nav_unsavedLeave": "有未儲存的更改,確定要離開?", + "save_clearedEmptyDistricts": "僅有空地區區塊(尚無店鋪),已清除暫存", + "api_fail_createLane": "新增車線失敗", + "api_fail_addShop": "新增店鋪失敗", + "api_fail_updateLane": "更新車線失敗", + "api_fail_deleteShop": "刪除店鋪失敗", + "api_fail_updateLogistics": "更新物流商失敗", + "diff_loadFail": "載入版本變更失敗", + "versionNote_saveFail": "備註儲存失敗", + "diff_restoreFail": "恢復失敗", + "confirm_restoreDiscardsEdits": "排程版本還原會捨棄目前看板上其他未儲存變更(拖曳、刪除、新增店鋪/車線、物流欄等)。確定繼續?", + "diff_restoreScheduled": "已排程還原至版本 #{{versionId}};請按「儲存更改」寫入後端。", + "diff_restoreAlreadyPending": "此版本已在排程還原中;請按「儲存更改」套用。", + "restore_applied": "已從版本還原並重新載入看板。", + "restore_appliedDroppedStaging": "已套用版本還原;本次儲存略過其他暫存變更(請重新編輯)。", + "confirm_restoreSaveWillDropStaging": "儲存時將先套用版本還原,本次其他暫存變更會被略過。確定繼續?", + "diff_noOlderCompare": "沒有上一筆版本可比較(請選擇較新的版本)", + "logistic_needMasterTpl": "「{{name}}」尚無對應物流公司,請先用「新增物流商」建立。", + "diffField_logisticsCompany": "物流公司", + "diffLogistic_unassigned": "未分配", + "diff_onLane": "車線 {{lane}}", + "diff_moveTo": "移至 {{lane}}", + "diff_addedToLane": "新加入車線 {{lane}}", + "diff_removedFromLane": "自 {{lane}} 移除", + "diff_editedCaption": "欄位調整(順序 / 分店名 / 時段等)", + "diff_restoreToHead": "排程還原至最新版本(須儲存)", + "diff_restoreToSelected": "排程還原至此版本(須儲存)", + "dialog_close": "關閉", + "btn_addLogistics": "新增物流商", + "logistics_sidebarEmpty": "無車線(請重新整理或放寬篩選)", + "lane_companyChip": "{{count}} 車線", + "lane_selectTitle": "車線選擇", + "lane_selectedNone": "未選擇車線", + "lane_selectedCount": "已選 {{count}} 條", + "lane_searchPh": "搜索…", + "lane_selectAll": "全選", + "floor_label": "樓層", + "floor_all": "全部", + "filter_clear": "清除", + "filter_apply": "確定", + "btn_addLane": "新增車線", + "tools_title": "操作工具", + "shop_searchPh": "搜索店鋪名稱/編號/地區...", + "btn_openVersionLog": "查看版本變更", + "btn_loading": "載入中…", + "btn_refresh": "重新整理", + "logistics_overviewTitle": "物流供應商總覽", + "version_ui_historyTitle": "版本歷史列表", + "version_ui_filterAria": "版本列表篩選", + "version_ui_listAria": "版本歷史列表", + "version_ui_snapshotBadge": "目前版本", + "version_ui_id": "版本#{{id}}", + "version_ui_none": "尚無版本", + "version_ui_editedBy": "編輯者:{{name}}", + "version_note_placeholder": "備註(離開欄位即儲存)", + "version_note_saving": "儲存中…", + "version_search_label": "搜索", + "version_search_placeholder": "版本號 / 備註 / 編輯者", + "version_date_label": "日期", + "version_empty_filtered": "沒有符合篩選條件的版本", + "version_empty_list": "暫時無版本(請先按「儲存更改」)", + "diff_clickLeft": "請點擊左側版本查看變更", + "diff_oldestSnapshot": "此為當前版本。", + "diff_summary_title": "版本摘要", + "diff_export_reportBtn": "匯出版本車線報告", + "diff_summary_added": "新增", + "diff_summary_moved": "移動", + "diff_summary_deleted": "刪除", + "diff_summary_fieldChange": "欄位變更", + "diff_shopList_title": "店鋪變更清單", + "diff_staged_boardPendingLine": "看板另有 {{count}} 筆未儲存/已排程項(見下方清單)。", + "diff_staged_section_title": "看板未儲存/已排程(尚未寫入後端)", + "diff_staged_section_subtitle": "下列與「儲存更改」後才會落庫的狀態一致;與上方版本 diff 分開列,避免與 Excel(僅後端版本)混淆。", + "diff_staged_tag_unsaved": "未儲存", + "diff_staged_tag_scheduled": "已排程", + "diff_staged_restoreScheduled": "已排程還原至版本 #{{versionId}}(須按「儲存更改」才會呼叫 restore)。", + "diff_staged_deleteUnknown": "刪除 truck id={{id}}(未儲存;明細請先儲存或取消後重試)", + "diff_staged_newLane": "新增車線(未儲存):{{lane}}", + "diff_staged_laneLogistic": "整線物流(未儲存):{{lane}} → {{company}}", + "diff_staged_emptyDistricts": "空地區區塊(未儲存):{{lane}} — {{names}}", + "diff_staged_shopDistrictHint": " · 地區:{{from}}→{{to}}", + "diff_staged_shopPendingOnLane": "{{name}}({{code}})— 車線 {{lane}}:有未儲存變更(拖曳/出車/裝載順序等;按「儲存更改」寫入){{districtPart}}", + "diff_staged_shopDistrictOnly": "{{name}}({{code}})— 車線 {{lane}}:地區 {{from}}→{{to}}(未儲存;按「儲存更改」寫入)", + "diff_staged_pendingLogisticMaster": "新增物流公司(未落庫):{{name}}(車牌 {{plate}});將與路線一併於「儲存更改」寫入", + "diff_staged_editLogisticMaster": "修改物流公司(未落庫):{{fromName}}({{fromPlate}})→ {{name}}({{plate}})", + "diff_staged_importPending": "匯入 Excel(未落庫):{{file}} — {{sheets}} 個工作表、{{rows}} 列(按「儲存更改」寫入)", + "confirm_importDiscardEdits": "匯入將取代目前看板上未儲存的變更,是否繼續?", + "import_staged_preview": "已載入匯入預覽:{{file}}({{sheets}} 表 / {{rows}} 列)。按「儲存更改」才會寫入後端。", + "err_importEmpty": "匯入檔案無有效車線資料列", + "diff_logisticMaster_section": "物流公司變更", + "diff_logisticMaster_added": "新增", + "diff_logisticMaster_edited": "修改", + "diff_noShopDiffHasBoardStaged": "與上一版本相比,店鋪列無差異;下列為看板上尚未按「儲存更改」寫入的變更(含新增物流公司)。", + "diff_export_blockedTooltip": "匯出檔為後端兩版本比對,不含看板未儲存變更。請先按「儲存更改」或取消變更後再匯出。", + "diff_export_blockedError": "有看板未儲存變更時無法匯出(Excel 僅含已落庫版本)。", + "diff_markedCount": "{{count}} 筆變更", + "diff_noDiffFromPrev": "與上一版無差異", + "diff_loadingEllipsis": "…", + "addShop_dialogTitle": "新增店鋪到車線", + "addRoute_dialogTitle": "新增配送車線", + "addRoute_hint": "確認後先加入看板暫存;須按頂部「儲存更改」才會在後端建立車線(不建立假店鋪列)。", + "addRoute_confirm": "確認新增車線", + "addRoute_submitting": "新增中…", + "district_dialog_add": "新增地區", + "district_dialog_edit": "編輯地區", + "district_name_label": "地區顯示名稱", + "district_name_ph": "空白表示「未分類」", + "district_help_null": "未分類對應後端 districtReference 為 null", + "district_help_mapped": "顯示名稱經 toDistrictRawValue 寫入各店鋪 districtReference;按「儲存更改」才打 API", + "seq_edit_departureLabel": "出車時間", + "seq_edit_seqLabel": "裝車順序 (Seq)", + "route_new_code_label": "車線編號", + "route_new_time_label": "出車時間", + "route_new_logistic_label": "物流公司", + "route_new_store_label": "樓層", + "route_new_remark_label": "車線備註 (4F)", + "logistic_companyName": "物流公司名稱", + "logistic_plate": "車牌", + "logistic_driver": "司機姓名", + "logistic_phone": "聯絡電話", + "logistic_btn_save": "儲存", + "logistic_btn_saveDb": "儲存至資料庫", + "shop_autocomplete_label": "選擇店鋪", + "shop_autocomplete_ph": "輸入名稱或代碼篩選", + "shop_autocomplete_loading": "尚未載入店鋪主檔", + "shop_autocomplete_noOptions": "此車線已含所有可選店鋪或無可選項", + "dialog_addLogisticsTitle": "新增物流商", + "btn_cancelBack": "取消返回", + "quickPick_noLanes": "目前無車線(請放寬樓層篩選或重新整理)", + "quickPick_noKeyword": "無符合關鍵字的車線", + "route_logisticUnspecified": "(未指定——稍後於物流商管理指派)", + "dialog_editLogisticsTitle": "編輯物流公司", + "btn_apply": "套用", + "addShop_confirm": "確認", + "addShop_listHint": "同車線內已存在的店鋪代碼不會出現在清單。新增後若要改順序可拖曳;與其他編輯相同,需按「儲存更改」才會寫入後端 truck。", + "departureDialog_title": "編輯出車時間", + "departureDialog_hint": "套用至此車線所有店鋪列;按上方「儲存更改」寫入後端。", + "seqDialog_title": "編輯裝車順序", + "seqDialog_hint": "按「儲存更改」後寫入 truck 列。", + "logistics_colLaneCount": "{{count}} 條車線", + "tooltip_openLaneBoard": "在車線看板開此車線", + "aria_openLaneBoard": "開啟車線看板", + "tooltip_removeFromLane": "從此車線移除", + "tooltip_clearLaneShops": "清空此車線所有店鋪(按「儲存更改」才寫入後端)", + "tooltip_pickLane": "選擇車線(加入看板勾選並捲動到該欄)", + "aria_pickLane": "選擇車線", + "aria_searchLanes": "搜索車線", + "logistics_colShopCount": "{{count}} 家店鋪", + "tooltip_editLogisticsDb": "編輯物流公司(須按「儲存更改」寫入)", + "tooltip_deleteLogistics": "刪除物流公司(須按「儲存更改」寫入)", + "aria_editLogistics": "編輯物流公司", + "aria_deleteLogistics": "刪除物流公司", + "confirm_deleteLogistic": "確定刪除物流公司「{{name}}」?須按「儲存更改」寫入。", + "err_logisticDeleteHasLanes": "此物流公司尚有 {{count}} 條車線,請先移走或改派後再刪除。", + "diff_staged_deleteLogisticMaster": "刪除物流公司(未落庫):{{name}}", + "logistic_btn_apply": "套用", + "tooltip_editDistrict": "編輯地區名稱(按「儲存更改」才寫入)", + "aria_editDistrict": "編輯地區", + "tooltip_removeEmptyDistrict": "移除此暫存區塊(未儲存前可刪)", + "aria_removeEmptyDistrict": "移除空區", + "tooltip_editSeq": "編輯裝車順序(按「儲存更改」寫回)", + "aria_editSeq": "編輯裝車順序", + "diff_moveFrom": "從 {{lane}}", + "logistics_dirtyColumnBadge": "有未儲存物流更改", + "logistics_dirtyLaneBadge": "未儲存物流更改", + "lane_shopCountInline": "{{count}} 間店鋪", + "btn_addDistrict": "新增地區", + "empty_lane_noShops": "無分配店鋪", + "btn_addShopToLane": "新增店鋪", + "err_loadLanes": "載入車線失敗", + "btn_scheduleChange": "預約變更", + "schedule_modal_title": "預約變更", + "schedule_modal_subtitle": "設定排程變更的生效時間", + "schedule_step_time": "1. 設定執行時間", + "schedule_exec_date": "執行日期", + "schedule_exec_time": "執行時間", + "schedule_step_method": "2. 排程與預定變更", + "schedule_tab_manual": "拖拽排程", + "schedule_tab_import": "匯入車線Excel", + "schedule_search_ph": "搜索店舖名稱 / 編號...", + "schedule_bulk_target": "批次設定目標:", + "schedule_pick_lane": "選擇車線...", + "schedule_col_select": "選取", + "schedule_col_shop": "店舖資訊", + "schedule_col_current_lane": "目前車線", + "schedule_col_target_lane": "目標車線", + "schedule_col_target_district": "地區", + "schedule_col_target_seq": "序號", + "schedule_district_original": "沿用店舖原始地區", + "schedule_seq_hint": "目標車線內同地區區段的最大序號 +1 為預設值", + "schedule_bulk_seq": "統一序號", + "schedule_history_line_placement": "目標地區 {{district}} · 序號 {{seq}}", + "schedule_line_on_lane": "車線 {{lane}}", + "schedule_line_lane_change": "{{from}} → {{to}}", + "schedule_line_seq": "序號 {{value}}", + "schedule_line_district_change": "地區 {{from}} → {{to}}", + "schedule_line_district_target": "地區 {{district}}", + "schedule_line_departure_change": "出車 {{from}} → {{to}}", + "schedule_line_departure_target": "出車 {{time}}", + "schedule_line_remove_from": "自 {{lane}} 移除", + "schedule_line_add_to": "新增至 {{lane}}", + "schedule_line_ensure_lane": "建立車線 {{lane}}", + "schedule_target_unset": "未設定", + "schedule_no_shops": "無符合條件的店舖", + "schedule_import_title": "匯入車線配置(與看板相同格式)", + "schedule_import_hint": "請使用與看板「匯入車線」相同的 車線Excel", + "schedule_import_pick": "選擇檔案", + "schedule_import_need_datetime": "請先設定執行日期與時間", + "schedule_import_no_valid_moves": "檔案中沒有可排程的變更(未偵測到移動、新增、刪除或新建車線)", + "schedule_import_preview_summary": "已載入 {{file}}:{{sheets}} 個工作表 / {{rows}} 列 · 可排程 {{valid}} 筆 · 略過 {{errors}} 筆", + "schedule_import_plan_summary": "已載入 {{file}}:{{sheets}} 個工作表 / {{rows}} 列 · 移動 {{moves}} · 新增 {{creates}} · 刪除 {{deletes}} · 新建車線 {{ensureLanes}} · 略過 {{errors}} 筆", + "schedule_import_col_action": "動作", + "schedule_import_col_shop": "店鋪編號", + "schedule_import_col_name": "店鋪名稱", + "schedule_import_col_lane": "目標車線", + "schedule_import_col_seq": "序號", + "schedule_import_skipped_title": "略過 {{count}} 筆", + "schedule_import_row_error": "{{shop}}({{name}}):{{reason}}", + "schedule_import_err_no_truck_row": "店鋪 {{shop}} 尚無看板列,請先於看板匯入或新增", + "schedule_import_err_no_target_lane": "店鋪 {{shop}} 缺少目標車線", + "schedule_import_err_invalid_departure": "店鋪 {{shop}} 出車時間無效", + "schedule_import_err_truck_not_found": "找不到店鋪 {{shop}} 的看板列", + "schedule_import_err_placeholder_row": "店鋪 {{shop}} 在空白占位列上", + "schedule_action_move": "移動", + "schedule_action_create": "新增店鋪", + "schedule_action_delete": "刪除", + "schedule_action_ensure_lane": "新建車線", + "schedule_summary_ready": "所有設定已就緒", + "schedule_summary_pending": "待完成目標設定", + "schedule_summary_counts": "已選 {{selected}} 個店舖,其中 {{ready}} 個已指派目標車線。", + "schedule_summary_changes": "共有 {{count}} 項變更", + "schedule_lane_left": "左側車線", + "schedule_lane_right": "右側車線", + "schedule_review_queue": "變更預覽佇列", + "schedule_review_count": "{{count}} 項變更", + "schedule_review_empty": "暫無待排程變更", + "schedule_review_empty_hint": "請拖曳店鋪以調整其車線分配或裝載排程順序,或使用刪除按鈕移除店鋪", + "schedule_review_revert": "復原", + "schedule_review_delete_action": "刪除自:", + "schedule_review_lane_change": "車線變更:", + "schedule_review_district_change": "區域參考:", + "schedule_review_seq": "裝載順序:", + "schedule_drop_hint": "請拖曳店鋪卡片至此車線", + "schedule_moved_badge": "移入", + "schedule_drag_seq": "裝載順序 Seq: {{seq}}", + "schedule_seq_edit_btn": "編輯裝載順序", + "schedule_seq_dialog_hint": "變更會加入右側預覽佇列,確認排程後才套用。", + "schedule_planned_label": "執行預定", + "schedule_confirm_btn": "確認多項預約變更", + "schedule_applied_snackbar": "已登記 {{count}} 筆預約變更並更新看板,請按「儲存更改」寫入後端。", + "schedule_err_conflict": "部分店舖無法移至目標車線(重複店鋪或草稿列)。", + "schedule_err_execute_at_past": "排程執行時間已過去,請選擇未來的日期與時間。", + "schedule_err_open_pending": "店舖列 #{{id}} 已有待執行的排程。", + "schedule_err_duplicate_shop": "目標車線上已有店舖 {{shop}}。", + "schedule_err_target_lane_missing": "找不到目標車線 {{lane}}。", + "schedule_err_target_lane_empty": "目標車線 {{lane}} 尚無店舖,請先加入店舖。", + "schedule_err_no_moves": "沒有可排程的店舖移動。", + "schedule_err_generic": "排程請求失敗,請稍後再試。", + "schedule_reschedule_time_adjusted": "原執行時間已過去,已自動調整為 {{at}}。", + "schedule_shop_badge": "已排程變更", + "schedule_shop_locked": "排程執行中,此店鋪暫不可手改", + "schedule_retry_rejects_partial": "PARTIAL 排程不可重試,請先還原看板後重新建立排程", + "schedule_registered_snackbar": "已登記 {{count}} 筆預約變更,將於執行時間由伺服器自動套用。", + "schedule_import_parse_summary": "解析完成:有效 {{valid}} 筆,錯誤 {{errors}} 筆", + "schedule_import_confirm_btn": "確認匯入並建立排程", + "schedule_history_title": "預約任務狀態與歷程中心", + "schedule_history_subtitle": "檢視、執行或排查已排程的店舖/車線變更", + "schedule_history_filter_all": "全部任務", + "schedule_history_filter_pending": "排程中", + "schedule_history_filter_success": "已成功", + "schedule_history_filter_failed": "已失敗", + "schedule_history_empty": "無符合篩選條件的預約任務", + "schedule_history_status_pending": "排程中", + "schedule_history_status_success": "執行成功", + "schedule_history_status_failed": "執行失敗", + "schedule_history_created": "創建者:{{by}}", + "schedule_history_unknown_user": "系統", + "schedule_history_line_counts": "明細 {{total}} 筆 · 成功 {{applied}} · 失敗 {{failed}} · 待執行 {{pending}}", + "schedule_history_applied_at": "成功執行時間:{{at}}", + "schedule_history_view_lines": "檢視店舖明細", + "schedule_history_no_lines": "尚無明細資料", + "schedule_history_failed_lines": "{{count}} 筆店舖移線失敗", + "schedule_history_success_lines": "{{count}} 筆店舖已成功移線", + "schedule_history_apply_now": "立即執行", + "schedule_history_cancel": "取消排程", + "schedule_history_archived": "已執行", + "schedule_history_needs_attention": "需處理", + "schedule_history_reschedule": "重新排程", + "schedule_history_ignore": "忽略", + "schedule_history_status_ignored": "已忽略", + "schedule_history_close": "關閉視窗", + "schedule_log_failed_hint": "有 {{count}} 個預約任務執行失敗,請查看 Log", + "btn_scheduleHistory": "查看預約變更 Log", + "id": "ID", + "code": "編號", + "Shop Name": "店鋪名稱", + "Code": "編號", + "Name": "名稱", + "Addr3": "地址", + "TruckLance Status": "車線狀態", + "Complete": "完成", + "Missing Data": "缺少資料", + "No TruckLance": "無車線", + "Actions": "操作", + "View Detail": "查看詳情", + "ShopAndTruck": "車線店鋪管理", + "Shop": "店鋪", + "Truck Lane": "車線", + "Filter by Status": "按狀態篩選", + "All": "全部", + "Please log in to view shop details": "請登入以查看店鋪詳情", + "Shop ID is required": "需要店鋪ID", + "Invalid Shop ID": "無效的店鋪ID", + "Shop not found": "找不到店鋪", + "Failed to load shop details": "載入店鋪詳情失敗", + "Failed to delete truck lane": "刪除車線失敗", + "Failed to load truck lanes": "載入車線失敗", + "Shop Information": "店鋪資訊", + "TruckLance Code": "車線編號", + "Departure Time": "出發時間", + "Please fill in the following required fields:": "請填寫以下必填欄位:", + "Failed to create truck": "建立卡車失敗", + "Back": "返回", + "Addr1": "地址1", + "Addr2": "地址2", + "Contact No": "聯絡電話", + "Contact Email": "聯絡郵箱", + "Contact Name": "聯絡人", + "Truck Information": "車線資訊", + "Add Truck Lane": "新增車線", + "Loading Sequence": "裝載順序", + "District Reference": "區域參考", + "Store ID": "樓層", + "Remark": "備註", + "No Truck data available": "沒有卡車資料", + "Cancel": "取消", + "Delete truck lane": "刪除車線", + "Add New Truck Lane": "新增車線", + "Save": "儲存", + "Truck lane code already exists. Please use a different code.": "車線編號已存在,請使用其他編號。", + "No Truck Lane data available": "沒有車線資料", + "TruckLance Code is required": "需要車線編號", + "Truck lane not found": "找不到車線", + "Failed to load truck lane detail": "載入車線詳情失敗", + "Failed to load shops": "載入店鋪失敗", + "Truck lane information is required": "需要車線資訊", + "Invalid departure time": "無效的出發時間", + "No trucks found for this truck lane": "此車線沒有可更新的資料", + "Departure time updated for all shops on this truck lane": "已將出發時間更新至此車線下的所有店鋪", + "Failed to save truck data": "儲存卡車資料失敗", + "Invalid shop data": "無效的店鋪資料", + "Truck shop details updated successfully": "卡車店鋪詳情更新成功", + "Failed to save truck shop details": "儲存卡車店鋪詳情失敗", + "All shops updated successfully": "所有店鋪已成功更新", + "Truck lane deleted successfully": "車線刪除成功", + "Shop added to truck lane successfully": "店鋪已成功新增至車線", + "Failed to create shop in truck lane": "新增店鋪至車線失敗", + "Back to Truck Lane List": "返回車線列表", + "No truck lane data available": "沒有車線資料", + "Truck Lane Detail": "車線詳情", + "Edit departure time": "編輯出發時間", + "Cancel editing": "取消編輯", + "Shops Using This Truck Lane": "使用此車線的店鋪", + "Save All": "全部儲存", + "Mass Edit": "批量編輯", + "Add Shop": "新增店鋪", + "Shop Branch": "店鋪分店", + "Shop Code": "店鋪編號", + "No shops found using this truck lane": "沒有找到使用此車線的店鋪", + "Search or select remark": "搜索或選擇備註", + "Not editable for this Store ID": "此樓層不可編輯", + "Save changes": "儲存變更", + "Edit shop details": "編輯店鋪詳情", + "Add Shop to Truck Lane": "新增店鋪至車線", + "Search or select shop name": "搜索或選擇店鋪名稱", + "Applies to all shops using this truck lane": "將套用至所有使用此車線的店鋪", + "Edit loading sequence": "編輯裝載順序", + "Edit shop truck lane": "編輯店鋪車線", + "Edit truck lane": "編輯車線", + "Invalid truck data": "無效的卡車資料", + "Loading sequence updated successfully": "裝載順序更新成功", + "Failed to load shop detail": "載入店鋪詳情失敗", + "Failed to save loading sequence": "儲存裝載順序失敗", + "Route Board": "車線看板", + "Select a shop first": "請先選擇店鋪", + "Select items without order to append to bottom": "只會顯示尚未設定順序的物品", + "Shop Detail": "店鋪詳情", + "Truck ID is required": "需要卡車ID", + "Are you sure you want to delete this truck lane?": "您確定要刪除此車線嗎?", + "Search or select branch": "搜索或選擇分店", + "Search or select shop code": "搜索或選擇店鋪編號", + "versionLogField_departureTime": "發車時段", + "versionLogField_loadingSequence": "裝載順序", + "versionLogField_branchName": "分店名稱", + "versionLogField_districtReference": "區域", + "versionLogField_shopCode": "店鋪代碼", + "versionLogField_storeId": "樓層/店別", + "versionLogField_remark": "備註", + "versionLogField_truckLanceCode": "車線代碼", + "versionLogField_logisticId": "物流公司" +} diff --git a/src/i18n/zh/stockIssue.json b/src/i18n/zh/stockIssue.json new file mode 100644 index 0000000..ca77898 --- /dev/null +++ b/src/i18n/zh/stockIssue.json @@ -0,0 +1,78 @@ +{ + "Action": "操作", + "Available Qty": "可用數量", + "Bad Item": "不良", + "Bad Item Handle": "不良品處理", + "Bad Item Qty": "不良品數量", + "Bad Item Records": "不良品處理紀錄", + "Batch Disposed All": "批量處理完成", + "Book Qty": "帳面庫存", + "Cancel": "取消", + "Code": "編號", + "Defective Qty": "不良數量", + "Disposed": "已處置", + "Disposing...": "處理中...", + "DO Order Code": "送貨單編號", + "End Date": "結束日期", + "Expiry Date": "到期日", + "Expiry End Date": "到期日(結束)", + "Expiry Item": "過期", + "Expiry Item Handle": "過期品處理", + "Expiry Item Qty": "過期品數量", + "Expiry Item Records": "過期品處理紀錄", + "Expiry Start Date": "到期日(開始)", + "Failed to load expiry items": "載入過期品失敗", + "Failed to submit": "提交失敗", + "Failed to submit expiry item": "提交過期品失敗", + "Handled Date": "處理日期", + "Handler": "處理人", + "Issue Qty": "問題數量", + "Item": "貨品", + "Item Code": "貨品編號", + "Item not found": "找不到貨品", + "Item selected": "已選擇貨品", + "JO Order Code": "工單編號", + "Loading": "加載中", + "Location": "位置", + "Looked": "已查看", + "Lot No": "批號", + "Lot No.": "批號", + "Miss Item": "缺貨", + "Miss Qty": "缺貨數量", + "Name": "名稱", + "No changes to submit": "沒有可提交的變更", + "No items are selected yet.": "未選擇貨品", + "No record found": "沒有記錄", + "Picker Name": "提料員", + "Pick Order Code": "提料單編號", + "Please enter a valid quantity": "請輸入有效數量", + "Please set at least one search criterion": "請至少設定一項搜索條件", + "Processing...": "處理中...", + "Quantity exceeds available quantity": "數量超過可用數量", + "Remain available Quantity": "剩餘可用數量", + "Remaining Qty": "剩餘數量", + "Remark": "備註", + "Remarks": "備註", + "Reset": "重置", + "Rows per page": "每頁行數", + "Saved successfully": "儲存成功", + "Search": "搜索", + "Search Criteria": "搜索條件", + "Search to load lot lines": "請按搜索以載入批號", + "Start Date": "開始日期", + "Status": "狀態", + "Stock UoM": "庫存單位", + "Submit": "確認", + "Submit Bad Item": "提交不良品", + "Submit Miss Item": "提交缺貨", + "Submit Quantity": "實際問題數量", + "Submitting...": "提交中...", + "Type": "類型", + "Unknown error": "未知錯誤", + "UoM": "單位", + "User ID is required": "需要用戶ID", + "Warehouse": "倉庫", + "available": "可用", + "unavailable": "不可用", + "Stock Issue": "出倉問題" +} diff --git a/src/i18n/zh/stockRecord.json b/src/i18n/zh/stockRecord.json new file mode 100644 index 0000000..204e11f --- /dev/null +++ b/src/i18n/zh/stockRecord.json @@ -0,0 +1,32 @@ +{ + "title": "庫存記錄", + "tke": "盤點", + "adj": "調整", + "nor": "正常", + "trf": "轉倉", + "open": "開倉", + "miss": "缺貨", + "bad": "不良", + "Start Date": "開始日期", + "End Date": "結束日期", + "Date": "日期", + "Item-lotNo": "貨品-批號", + "UOM": "單位", + "In Qty": "入庫數量", + "Out Qty": "出庫數量", + "Balance Qty": "庫存數量", + "Loading...": "載入中...", + "Type": "類別", + "Status": "狀態", + "pending": "待處理", + "completed": "已完成", + "partially_completed": "已部分完成", + "receiving": "收貨中", + "rejected": "已拒絕", + "checked": "已檢查", + "determine1": "上報1", + "lot-change": "批次更換審批", + "qc1": "質檢1", + "qc2": "質檢2", + "qc3": "質檢3" +} diff --git a/src/i18n/zh/stockTake.json b/src/i18n/zh/stockTake.json new file mode 100644 index 0000000..1929dd1 --- /dev/null +++ b/src/i18n/zh/stockTake.json @@ -0,0 +1,179 @@ +{ + "View Details": "查看詳細", + "Back to List": "返回列表", + "Stock Take Section": "盤點區域", + "Warehouse": "倉庫", + "All": "全部", + "Rows per page": "每頁行數", + "Remark": "備註", + "Action": "操作", + "Save": "保存", + "Selected Qty": "選擇數量", + "Not Match": "要求重點", + "Search Criteria": "搜索條件", + "Stock Take Section (can use , to search multiple sections)": "盤點區域(可使用逗號搜索多個區域)", + "Item": "貨品", + "Store ID": "樓層", + "Pending": "待處理", + "Pass": "已盤點", + "Reset": "重置", + "Search": "搜索", + "Cancel": "取消", + "Confirm": "確認", + "Type": "類型", + "Status": "來貨狀態", + "Lot No": "批號", + "Location": "位置", + "Reason": "原因", + "Date": "日期", + "Pick Order Code": "提料單編號", + "Item Code": "貨品編號", + "Pick Order": "提貨單", + "Required Qty": "所需數量", + "Issue Remark": "問題備註", + "Stock Take Management": "盤點管理", + "Stock Take Round": "盤點輪次", + "Plan Start Date": "計劃開始日期", + "Total Items": "總貨品數量", + "Total Lots": "總批號數量", + "Total Sections": "總區域數量", + "Section": "區域", + "Last Stock Take Date": "上次盤點日期", + "Stock Taker": "盤點員", + "Approver": "審核員", + "start time": "開始時間", + "end time": "結束時間", + "Control Time": "操作時間", + "Approver stock take record saved successfully": "審核員盤點記錄保存成功", + "Stock take record status updated to not match": "盤點狀態更新為要求重點", + "Batch approver save completed: {{success}} success, {{errors}} errors": "批次審核儲存完成:成功 {{success}} 筆,錯誤 {{errors}} 筆", + "Variance %": "差異百分比", + "Only Variance": "僅差異", + "Batch Save All": "批量保存所有", + "Warehouse Location": "倉庫位置", + "Item-lotNo-ExpiryDate": "貨品-批號-到期日", + "UOM": "單位", + "Stock Take Qty(include Bad Qty)= Available Qty": "盤點數= 可用數", + "Record Status": "盤點狀態", + "No data": "沒有數據", + "Difference": "差異", + "First": "第一次", + "Second": "第二次", + "Approver Input": "審核員輸入", + "Stock Take Qty": "盤點數", + "Bad Qty": "不良數量", + "selected stock take qty": "已選擇盤點數量", + "book qty": "帳面庫存", + "ReStockTake": "重新盤點", + "Approver input empty; save skipped, row remains pending": "審核員盤點數與不良數皆未輸入,已略過儲存,該列維持待審核", + "No rows loaded; set search criteria and search first": "尚未載入資料,請設定搜索條件並按搜索", + "Approver Time": "審核時間", + "Stock Take Qty Data and Variance Analysis": "盤點數數據與差異分析", + "Book Qty": "帳面庫存", + "Inventory Difference": "庫存差異", + "variance Percentage": "差異百分比", + "Variance": "差異", + "Picker": "盤點員", + "Variance filter inclusive only": "反選", + "Variance filter inclusive range hint": "只顯示 -{{value}}% {{op}} 差異% {{op}} {{value}}% 的列(範圍內)", + "Variance filter exclusive range hint": "只顯示 -{{value}}% {{op}} 差異% {{op}} {{value}}% 的列(範圍外)", + "Total": "總數", + "Shown": "顯示", + "Confirm batch save approver": "確認批次儲存審核", + "Batch save confirm message": "將對目前列表中的 {{count}} 筆盤點記錄執行批次儲存,是否繼續?", + "Stock take sections in current list": "目前列表涉及 {{count}} 個盤點區域", + "Stock Take": "盤點", + "Manual": "手動", + "Damage": "損壞", + "Return": "退貨", + "Approved": "已批准", + "Rejected": "已拒絕", + "Adjustment No, Item, Lot...": "處理編號, 貨品, 批號...", + "Total Adjustments": "總調整數量", + "Adjustment No": "處理編號", + "Before Qty": "處理前數量", + "After Qty": "處理後數量", + "Adjustment": "處理", + "Adjusted By": "處理者", + "No adjustments found": "未找到調整", + "Floor unassigned": "未設定樓層", + "Area": "倉位", + "Start Stock Take Date": "盤點日期", + "Create Stock Take (Select Sections)": "建立盤點(選擇區域)", + "Total Item Kind Number": "貨品種類數量", + "View ReStockTake": "查看重新盤點", + "Warehouse missing stock take section tooltip has": "有 {{count}} 個倉庫未設定盤點區域,點擊查看", + "Warehouse missing stock take section tooltip none": "所有倉庫均已設定盤點區域", + "Warehouse missing stock take section warn title": "未設定盤點區域的倉庫", + "Stock take round name": "盤點輪次名稱", + "Stock take round name placeholder": "選填,例如:五月全廠盤點", + "Creation date": "建立日期", + "No stock take sections from warehouse": "目前沒有盤點區域資料", + "Select all sections all floors": "全選區域 (所有樓層)", + "Clear selection all floors": "清除已選 (所有樓層)", + "Total selected sections label": "總計已選擇 :", + "sections unit": "個區域", + "Floor area selection header": "{{floor}} 區域選擇 ({{count}} 區域)", + "Deselect all on this floor": "取消全選此樓層 ({{floor}})", + "Select all on this floor": "全選此樓層 ({{floor}})", + "Search section code or name": "搜索代碼或名稱 (例如 ST-042 或 飲品)", + "No sections match search": "沒有符合搜索條件的區域", + "Confirm create stock take": "確認建立盤點", + "Not filled": "(未填寫)", + "Warehouse missing stock take section drawer hint": "以下倉庫位置尚未設定盤點區域(ST-xxx),無法納入盤點區域選擇。請至倉庫設定補上。", + "Warehouse missing stock take section showing": "顯示前 {{shown}} 筆,共 {{count}} 筆", + "Warehouse missing stock take section empty": "沒有未設定盤點區域的倉庫", + "Warehouse missing stock take section go settings": "前往倉庫設定", + "Available QTY cannot be negative": "可用數量不能為負數", + "Stock take record saved successfully": "盤點記錄保存成功", + "Batch save completed: {{success}} success, {{errors}} errors": "批量保存完成:{{success}} 成功,{{errors}} 失敗", + "No valid input to submit": "請先填寫盤點數量", + "Submit All Inputted": "提交所有輸入", + "Issue No": "問題編號", + "Actual Qty": "實際數量", + "Miss Qty": "缺貨數量", + "Category": "類型", + "Detail": "詳細", + "Issue Detail": "問題詳細", + "Handle Remark": "處理備註", + "Close": "關閉", + "Mark as Resolved": "標記為已解決", + "Pick Execution Issues": "揀貨執行問題", + "Inventory Adjustments": "存貨調整", + "Approver Pending": "審核待處理", + "Approver Approved": "審核通過", + "Created": "已建立", + "Skipped": "已略過", + "Errors": "錯誤", + "Failed to batch save approver stock take records": "批次儲存審核盤點記錄失敗", + "Failed to batch save stock take records": "批次儲存盤點記錄失敗", + "Failed to save approver stock take record": "儲存審核盤點記錄失敗", + "Failed to save stock take record": "儲存盤點記錄失敗", + "Failed to update stock take record status": "更新盤點記錄狀態失敗", + "First QTY is not available": "第一次盤點數不可用", + "Second QTY is not available": "第二次盤點數不可用", + "No valid records to batch save": "沒有可批次儲存的有效記錄", + "Please enter Approver QTY": "請輸入審核盤點數", + "Please enter Approver Bad QTY": "請輸入審核不良數", + "Please enter QTY": "請輸入盤點數", + "Please enter Second QTY": "請輸入第二次盤點數", + "Shortcut Input": "快捷輸入", + "Stock take record ID is required": "需要盤點記錄 ID", + "Invalid QTY": "無效的數量", + "Stock take qty exceeds maximum": "盤點數量不可超過 999,999,999,999", + "pass": "已盤點", + "notMatch": "要求重點", + "notmatch": "要求重點", + "completed": "已完成", + "stockTaking": "盤點中", + "approving": "審核中", + "resolved": "已解決", + "pending": "待處理", + "jo": "工單", + "do": "出貨單", + "material": "物料", + "lot_issue": "批號問題", + "quality_issue": "品質問題", + "quantity_mismatch": "數量不符", + "stock_take": "盤點" +} diff --git a/src/i18n/zh/ticketReleaseTable.json b/src/i18n/zh/ticketReleaseTable.json index aa93d61..fcdaa61 100644 --- a/src/i18n/zh/ticketReleaseTable.json +++ b/src/i18n/zh/ticketReleaseTable.json @@ -4,7 +4,7 @@ "Today": "是日", "Tomorrow": "翌日", "Day After Tomorrow": "後日", - "Now": "現時", + "Now": "現時", "Last updated": "最後更新", "Auto-refresh every 5 minutes": "每5分鐘自動刷新", "Auto-refresh every 10 minutes": "每10分鐘自動刷新", @@ -45,4 +45,4 @@ "Operation succeeded": "操作成功", "Confirm": "確認", "Cancel": "取消" -} \ No newline at end of file +} diff --git a/src/i18n/zh/translation.json b/src/i18n/zh/translation.json index 1453320..af187b8 100644 --- a/src/i18n/zh/translation.json +++ b/src/i18n/zh/translation.json @@ -1,4 +1,4 @@ { - "Release": "發佈", - "Actions": "操作" -} \ No newline at end of file + "Release": "發佈", + "Actions": "操作" +} diff --git a/src/i18n/zh/user.json b/src/i18n/zh/user.json index b19e88e..618a3da 100644 --- a/src/i18n/zh/user.json +++ b/src/i18n/zh/user.json @@ -1,40 +1,48 @@ { - "Create User": "新增用戶", - "Edit User": "編輯用戶資料", - "User Detail": "用戶詳細資料", - "User Authority": "用戶權限", - "Authority Pool": "權限池", - "Allocated Authority": "已分配權限", - "username": "用戶名稱", - "Username": "用戶名稱", - "password": "密碼", - "Confirm Password": "確認密碼", - "Reset": "重置", - "Cancel": "取消", - "Confirm": "確認", - "name": "姓名", - "User ID": "用戶ID", - "User Name": "用戶名稱", - "User Group": "用戶群組", - "Authority": "權限", - "Edit": "編輯", - "Delete": "刪除", - "Add": "新增", - "authority": "權限", - "description": "描述", - "Search by Authority or description or position.": "搜索權限、描述或職位。", - "Remove": "移除", - "User": "用戶", - "user": "用戶", - "qrcode": "二維碼", - "staffNo": "員工編號", - "Rows per page": "每頁行數", - "Delete Success": "刪除成功", - "Do you want to delete?": "您確定要刪除嗎?", - "Maintain User": "維護用戶", - "Maintain group": "維護群組", - "view user": "查看用戶", - "view group": "查看群組", - "Approval": "審批", - "Testing": "測試" -} \ No newline at end of file + "profile": "個人資料", + "Create User": "新增用戶", + "Edit User": "編輯用戶資料", + "Failed to fetch staff list": "無法取得員工列表", + "User Detail": "用戶詳細資料", + "User Authority": "用戶權限", + "Authority Pool": "權限池", + "Allocated Authority": "已分配權限", + "username": "用戶名稱", + "Username": "用戶名稱", + "password": "密碼", + "Confirm Password": "確認密碼", + "Reset": "重置", + "Cancel": "取消", + "Confirm": "確認", + "name": "姓名", + "User ID": "用戶ID", + "User Name": "用戶名稱", + "User Group": "用戶群組", + "Authority": "權限", + "Edit": "編輯", + "Delete": "刪除", + "Add": "新增", + "authority": "權限", + "description": "描述", + "Search by Authority or description or position.": "搜索權限、描述或職位。", + "Remove": "移除", + "User": "用戶", + "user": "用戶", + "qrcode": "二維碼", + "staffNo": "員工編號", + "Rows per page": "每頁行數", + "Delete Success": "刪除成功", + "Do you want to delete?": "您確定要刪除嗎?", + "Maintain User": "維護用戶", + "Maintain group": "維護群組", + "view user": "查看用戶", + "view group": "查看群組", + "Approval": "審批", + "Testing": "測試", + "An error has occurred. Please try again later.": "發生錯誤,請稍後再試。", + "Please input correct password": "請輸入正確密碼", + "Failed to search by name": "依名稱搜尋失敗", + "Failed to search by username": "依使用者名稱搜尋失敗", + "Staff No is required": "員工編號必填", + "User Not Found": "用戶不存在" +} diff --git a/src/i18n/zh/warehouse.json b/src/i18n/zh/warehouse.json index 07df83c..4c825d0 100644 --- a/src/i18n/zh/warehouse.json +++ b/src/i18n/zh/warehouse.json @@ -1,45 +1,44 @@ { - "Create Warehouse": "新增倉庫", - "Edit Warehouse": "編輯倉庫資料", - "Warehouse Detail": "倉庫詳細資料", - "code": "編號", - "name": "名稱", - "description": "描述", - "Edit": "編輯", - "Delete": "刪除", - "Delete Success": "刪除成功", - "Actions": "操作", - "Add": "新增", - "Store ID": "樓層", - - "Saved": "已儲存", - "Add Success": "新增成功", - "Saved Successfully": "儲存成功", - "Stock Take Section": "盤點區域", - "Add Warehouse": "新增倉庫", - "Save": "儲存", - "Stock Take Section Description": "盤點區域描述", - "Mapping Details": "對應詳細資料", - "Warehouses in this section": "此區域內的倉庫", - "No warehouses": "此區域內沒有倉庫", - "Remove": "移除", - "stockTakeSectionDescription": "盤點區域描述", - "Warehouse List": "倉庫列表", - "Stock Take Section & Warehouse Mapping": "盤點區域 & 倉庫對應", - "Warehouse": "倉庫", - "warehouse": "倉庫", - "Rows per page": "每頁行數", - "capacity": "容量", - "store_id": "樓層", - "area": "區域", - "slot": "位置", - "order": "提料單次序", - "stockTakeSection": "盤點區域", - "Do you want to delete?": "您確定要刪除嗎?", - "Cancel": "取消", - "Reset": "重置", - "Confirm": "確認", - "is required": "必填", - "Search Criteria": "搜索條件", - "Search": "搜索" + "Create Warehouse": "新增倉庫", + "Edit Warehouse": "編輯倉庫資料", + "Warehouse Detail": "倉庫詳細資料", + "code": "編號", + "name": "名稱", + "description": "描述", + "Edit": "編輯", + "Delete": "刪除", + "Delete Success": "刪除成功", + "Actions": "操作", + "Add": "新增", + "Store ID": "樓層", + "Saved": "已儲存", + "Add Success": "新增成功", + "Saved Successfully": "儲存成功", + "Stock Take Section": "盤點區域", + "Add Warehouse": "新增倉庫", + "Save": "儲存", + "Stock Take Section Description": "盤點區域描述", + "Mapping Details": "對應詳細資料", + "Warehouses in this section": "此區域內的倉庫", + "No warehouses": "此區域內沒有倉庫", + "Remove": "移除", + "stockTakeSectionDescription": "盤點區域描述", + "Warehouse List": "倉庫列表", + "Stock Take Section & Warehouse Mapping": "盤點區域 & 倉庫對應", + "Warehouse": "倉庫", + "warehouse": "倉庫", + "Rows per page": "每頁行數", + "capacity": "容量", + "store_id": "樓層", + "area": "區域", + "slot": "位置", + "order": "提料單次序", + "stockTakeSection": "盤點區域", + "Do you want to delete?": "您確定要刪除嗎?", + "Cancel": "取消", + "Reset": "重置", + "Confirm": "確認", + "is required": "必填", + "Search Criteria": "搜索條件", + "Search": "搜索" }