Suggest doing "Remove-Item -Recurse -Force .next" in frontendproduction
| @@ -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'); | |||
| @@ -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")} | |||
| </Typography> | |||
| </Stack> | |||
| <I18nProvider namespaces={["jo", "common"]}> | |||
| <I18nProvider namespaces={["bagUsage","navigation","common","jo"]}> | |||
| <Suspense fallback={<BagSearchWrapper.Loading />}> | |||
| <BagSearchWrapper /> | |||
| </Suspense> | |||
| @@ -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 ( | |||
| <> | |||
| <I18nProvider namespaces={["bagPrint", "navigation", "common"]}> | |||
| <Stack direction="row" justifyContent="space-between" flexWrap="wrap" rowGap={2}> | |||
| <Typography variant="h4" marginInlineEnd={2}> | |||
| 打袋機 | |||
| </Typography> | |||
| </Stack> | |||
| <BagPrintSearch /> | |||
| </> | |||
| </I18nProvider> | |||
| ); | |||
| }; | |||
| export default BagPrintPage; | |||
| } | |||
| @@ -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 ( | |||
| <I18nProvider namespaces={["chart", "navigation", "common"]}> | |||
| {children} | |||
| </I18nProvider> | |||
| ); | |||
| } | |||
| @@ -18,7 +18,7 @@ const Dashboard: React.FC<Props> = async ({ searchParams }) => { | |||
| fetchEscalationLogsByUser() | |||
| return ( | |||
| <I18nProvider namespaces={["dashboard", "common", "purchaseOrder"]}> | |||
| <I18nProvider namespaces={["dashboard","navigation","common","purchaseOrder"]}> | |||
| <Suspense fallback={<DashboardPage.Loading />}> | |||
| <DashboardPage searchParams={searchParams} /> | |||
| </Suspense> | |||
| @@ -24,7 +24,7 @@ const DoEdit: React.FC<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| <PageTitleBar title={t("Edit Delivery Order Detail")} className="mb-4" /> | |||
| <I18nProvider namespaces={["do", "common"]}> | |||
| <I18nProvider namespaces={["do","navigation","common"]}> | |||
| <Suspense fallback={<DoDetail.Loading />}> | |||
| <DoDetail id={parseInt(id)} /> | |||
| </Suspense> | |||
| @@ -17,7 +17,7 @@ const DeliveryOrder: React.FC = async () => { | |||
| return ( | |||
| <> | |||
| <PageTitleBar title={t("Delivery Order")} className="mb-4" /> | |||
| <I18nProvider namespaces={["do", "common"]}> | |||
| <I18nProvider namespaces={["do","navigation","common"]}> | |||
| <Suspense fallback={<DoSearch.Loading />}> | |||
| <DoSearch /> | |||
| </Suspense> | |||
| @@ -15,7 +15,7 @@ export const metadata: Metadata = { | |||
| type Props = SearchParams; | |||
| const Page: React.FC<Props> = 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<Props> = async ({ searchParams }) => { | |||
| {t("DO Workbench Search", { defaultValue: "DO Workbench Search" })} | |||
| </Link> | |||
| </p> | |||
| <I18nProvider namespaces={["do", "common"]}> | |||
| <I18nProvider namespaces={["doWorkbench","navigation","common","do"]}> | |||
| <Suspense fallback={<DoDetail.Loading />}> | |||
| <DoDetail id={parseInt(id)} workbenchRelease /> | |||
| </Suspense> | |||
| @@ -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 ( | |||
| <> | |||
| <PageTitleBar title={t("DO Workbench", { defaultValue: "DO Workbench" })} className="mb-4" /> | |||
| <I18nProvider namespaces={["pickOrder", "common", "ticketReleaseTable", "do"]}> | |||
| <I18nProvider namespaces={["doWorkbench","navigation","common","pickOrder","ticketReleaseTable","do"]}> | |||
| <DoWorkbenchTabs printerCombo={printerCombo ?? []} /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -19,7 +19,7 @@ const DoWorkbenchSearchPage: React.FC = async () => { | |||
| title={t("DO Workbench Search", { defaultValue: "DO Workbench Search" })} | |||
| className="mb-4" | |||
| /> | |||
| <I18nProvider namespaces={["do", "common"]}> | |||
| <I18nProvider namespaces={["doWorkbench","navigation","common","do"]}> | |||
| <Suspense fallback={<GeneralLoading />}> | |||
| <DoSearchWorkbench /> | |||
| </Suspense> | |||
| @@ -12,13 +12,13 @@ export const metadata: Metadata = { | |||
| type Props = {} & SearchParams; | |||
| const PickOrder: React.FC<Props> = async ({ searchParams }) => { | |||
| const { t } = await getServerI18n("pickOrder"); | |||
| const { t } = await getServerI18n("finishedGood"); | |||
| return ( | |||
| <> | |||
| <I18nProvider namespaces={["pickOrder", "common", "ticketReleaseTable"]}> | |||
| <I18nProvider namespaces={["finishedGood","navigation","common","pickOrder","ticketReleaseTable"]}> | |||
| <Suspense fallback={<FinishedGoodSearchWrapper.Loading />}> | |||
| <FinishedGoodSearchWrapper /> | |||
| </Suspense> | |||
| @@ -18,7 +18,7 @@ const Page = async () => { | |||
| redirect("/dashboard"); | |||
| } | |||
| return ( | |||
| <I18nProvider namespaces={["common"]}> | |||
| <I18nProvider namespaces={["finishedgoodmanagement", "navigation"]}> | |||
| <Suspense fallback={<FinishedGoodManagement.Loading />}> | |||
| <FinishedGoodManagement /> | |||
| </Suspense> | |||
| @@ -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 ( | |||
| <> | |||
| <I18nProvider namespaces={["pickOrder", "common", "ticketReleaseTable"]}> | |||
| <I18nProvider namespaces={["finishedGood","navigation","common","pickOrder","ticketReleaseTable"]}> | |||
| <Suspense fallback={<FinishedGoodSearch.Loading />}> | |||
| <FinishedGoodSearch /> | |||
| </Suspense> | |||
| @@ -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")} | |||
| </Typography> | |||
| </Stack> | |||
| <I18nProvider namespaces={["common", "inventory"]}> | |||
| <I18nProvider namespaces={["inventory","navigation","common"]}> | |||
| <Suspense fallback={<InventorySearch.Loading />}> | |||
| <InventorySearch /> | |||
| </Suspense> | |||
| @@ -39,7 +39,7 @@ const JoEdit: React.FC<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| <PageTitleBar title={t("Edit Job Order Detail")} className="mb-4" /> | |||
| <I18nProvider namespaces={["jo", "common"]}> | |||
| <I18nProvider namespaces={["jo","navigation","common"]}> | |||
| <Suspense fallback={<JoSave.Loading />}> | |||
| <JoSave id={parseInt(id)} /> | |||
| </Suspense> | |||
| @@ -32,7 +32,7 @@ const Jo: React.FC = async () => { | |||
| return ( | |||
| <> | |||
| <PageTitleBar title={t("Search Job Order/ Create Job Order")} className="mb-4" /> | |||
| <I18nProvider namespaces={["jo", "common", "purchaseOrder", "dashboard"]}> | |||
| <I18nProvider namespaces={["jo","navigation","common","purchaseOrder","dashboard"]}> | |||
| <Suspense fallback={<GeneralLoading />}> | |||
| <JoWorkbenchSearch | |||
| defaultInputs={defaultInputs} | |||
| @@ -18,7 +18,7 @@ const JoWorkbenchPage = async () => { | |||
| return ( | |||
| <> | |||
| <PageTitleBar title={t("Job Order Pickexcution", { defaultValue: "Job Order Pickexcution" })} className="mb-4" /> | |||
| <I18nProvider namespaces={["jo", "common", "pickOrder", "purchaseOrder", "dashboard"]}> | |||
| <I18nProvider namespaces={["jo","navigation","common","pickOrder","purchaseOrder","dashboard"]}> | |||
| <Suspense fallback={<GeneralLoading />}> | |||
| <JoPickOrderList printerCombo={printerCombo ?? []} /> | |||
| </Suspense> | |||
| @@ -38,7 +38,7 @@ const JodetailEdit: React.FC<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| <PageTitleBar title={t("Edit Job Order Detail")} className="mb-4" /> | |||
| <I18nProvider namespaces={["jo", "common"]}> | |||
| <I18nProvider namespaces={["jo","navigation","common"]}> | |||
| <Suspense fallback={<GeneralLoading />}> | |||
| <JoSave id={parseInt(id)} defaultValues={undefined} /> | |||
| </Suspense> | |||
| @@ -18,7 +18,7 @@ const Jodetail: React.FC = async () => { | |||
| return ( | |||
| <> | |||
| <PageTitleBar title={t("Job Order Pick Execution")} className="mb-4" /> | |||
| <I18nProvider namespaces={["jo", "common", "pickOrder"]}> | |||
| <I18nProvider namespaces={["jo","navigation","common","pickOrder"]}> | |||
| <Suspense fallback={<GeneralLoading />}> | |||
| <JodetailSearchWrapper /> | |||
| </Suspense> | |||
| @@ -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 ( | |||
| <> | |||
| <I18nProvider namespaces={["laserPrint", "navigation", "common"]}> | |||
| <Stack direction="row" justifyContent="space-between" flexWrap="wrap" rowGap={2}> | |||
| <Typography variant="h4" marginInlineEnd={2}> | |||
| 檸檬機(激光機) | |||
| </Typography> | |||
| </Stack> | |||
| <LaserPrintSearch /> | |||
| </> | |||
| </I18nProvider> | |||
| ); | |||
| }; | |||
| export default LaserPrintPage; | |||
| } | |||
| @@ -48,7 +48,7 @@ export default async function MainLayout({ | |||
| /> | |||
| } | |||
| mainContent={ | |||
| <I18nProvider namespaces={["common"]}> | |||
| <I18nProvider namespaces={["navigation", "common"]}> | |||
| <MainContentArea>{children}</MainContentArea> | |||
| </I18nProvider> | |||
| } | |||
| @@ -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 ( | |||
| <I18nProvider namespaces={["m18Sync", "navigation", "common"]}> | |||
| {children} | |||
| </I18nProvider> | |||
| ); | |||
| } | |||
| @@ -18,7 +18,7 @@ const PickOrder: React.FC<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| <I18nProvider namespaces={["pickOrder"]}> | |||
| <I18nProvider namespaces={["pickOrder","navigation","common"]}> | |||
| <Suspense fallback={<PickOrderDetail.Loading />}> | |||
| <PickOrderDetail consoCode={`${searchParams["consoCode"]}`} /> | |||
| </Suspense> | |||
| @@ -17,7 +17,7 @@ const PickOrder: React.FC = async () => { | |||
| return ( | |||
| <> | |||
| <I18nProvider namespaces={["pickOrder", "common"]}> | |||
| <I18nProvider namespaces={["pickOrder","navigation","common"]}> | |||
| <Suspense fallback={<PickOrderSearch.Loading />}> | |||
| <PickOrderSearch /> | |||
| </Suspense> | |||
| @@ -31,7 +31,7 @@ const PoEdit: React.FC<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={[type, "dashboard"]}> | |||
| <I18nProvider namespaces={[type, "navigation", "common", "dashboard"]}> | |||
| <Suspense fallback={<PoDetail.Loading />}> | |||
| <PoDetail id={id} /> | |||
| </Suspense> | |||
| @@ -17,7 +17,7 @@ const PurchaseOrder: React.FC = async () => { | |||
| // preloadClaims(); | |||
| return ( | |||
| <> | |||
| <I18nProvider namespaces={["purchaseOrder", "common", "items","dashboard"]}> | |||
| <I18nProvider namespaces={["purchaseOrder","navigation","common","dashboard"]}> | |||
| <Stack | |||
| direction="row" | |||
| justifyContent="space-between" | |||
| @@ -17,7 +17,7 @@ export default function PoWorkbenchPage() { | |||
| flexDirection: "column", | |||
| }} | |||
| > | |||
| <I18nProvider namespaces={["poWorkbench", "purchaseOrder", "common"]}> | |||
| <I18nProvider namespaces={["poWorkbench","navigation","common","purchaseOrder"]}> | |||
| <PoWorkbenchPageClient /> | |||
| </I18nProvider> | |||
| </Box> | |||
| @@ -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")} | |||
| </Button> */} | |||
| </Stack> | |||
| <I18nProvider namespaces={["common", "production","purchaseOrder","jo"]}> | |||
| <I18nProvider namespaces={["production","productionProcess","navigation","common","purchaseOrder","jo"]}> | |||
| <ProductionProcessPage printerCombo={printerCombo} /> {/* Use new component */} | |||
| </I18nProvider> | |||
| </> | |||
| @@ -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")} | |||
| </Button> */} | |||
| </Stack> | |||
| <I18nProvider namespaces={["common", "production","purchaseOrder","jo","dashboard"]}> | |||
| <I18nProvider namespaces={["productionProcess","navigation","common","purchaseOrder","jo","dashboard"]}> | |||
| <Suspense fallback={<ProductionProcessLoading />}> | |||
| <ProductionProcessPage printerCombo={printerCombo} /> | |||
| </Suspense> | |||
| @@ -11,12 +11,12 @@ export const metadata: Metadata = { | |||
| }; | |||
| const Projects: React.FC = async () => { | |||
| const { t } = await getServerI18n("projects"); | |||
| const { t } = await getServerI18n("project"); | |||
| return ( | |||
| <> | |||
| <Typography variant="h4">{t("Create Project")}</Typography> | |||
| <I18nProvider namespaces={["projects"]}> | |||
| <I18nProvider namespaces={["project","navigation","common"]}> | |||
| <CreateProject /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -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")} | |||
| </Button> | |||
| </Stack> | |||
| <I18nProvider namespaces={["project"]}> | |||
| <I18nProvider namespaces={["project","navigation","common"]}> | |||
| <Suspense fallback={<ProjectSearch.Loading />}> | |||
| <ProjectSearch /> | |||
| </Suspense> | |||
| @@ -0,0 +1,13 @@ | |||
| import { I18nProvider } from "@/i18n"; | |||
| export default function PsLayout({ | |||
| children, | |||
| }: { | |||
| children: React.ReactNode; | |||
| }) { | |||
| return ( | |||
| <I18nProvider namespaces={["scheduling", "navigation", "common"]}> | |||
| {children} | |||
| </I18nProvider> | |||
| ); | |||
| } | |||
| @@ -619,7 +619,7 @@ export default function ProductionSchedulePage() { | |||
| return ( | |||
| <div className="space-y-4"> | |||
| <PageTitleBar | |||
| title="排程" | |||
| title="生產排程" | |||
| actions={ | |||
| <> | |||
| <button | |||
| @@ -28,7 +28,7 @@ const PutAway: React.FC = async () => { | |||
| {t("Put Away")} | |||
| </Typography> | |||
| </Stack> | |||
| <I18nProvider namespaces={["putAway", "purchaseOrder", "common"]}> | |||
| <I18nProvider namespaces={["putAway","navigation","common","purchaseOrder"]}> | |||
| <Suspense fallback={<PutAwayScan.Loading />}> | |||
| <PutAwayScan /> | |||
| </Suspense> | |||
| @@ -24,7 +24,7 @@ const PutAwayCamPage: React.FC = async () => { | |||
| {t("Put Away")} | |||
| </Typography> | |||
| </Stack> | |||
| <I18nProvider namespaces={["putAway", "purchaseOrder", "common"]}> | |||
| <I18nProvider namespaces={["putAway","navigation","common","purchaseOrder"]}> | |||
| <Suspense fallback={<PutAwayCamScanWrapper.Loading />}> | |||
| <PutAwayCamScanWrapper /> | |||
| </Suspense> | |||
| @@ -0,0 +1,13 @@ | |||
| import { I18nProvider } from "@/i18n"; | |||
| export default function ReportLayout({ | |||
| children, | |||
| }: { | |||
| children: React.ReactNode; | |||
| }) { | |||
| return ( | |||
| <I18nProvider namespaces={["report", "navigation", "common"]}> | |||
| {children} | |||
| </I18nProvider> | |||
| ); | |||
| } | |||
| @@ -45,7 +45,7 @@ const DetailScheduling: React.FC<Props> = async ({ searchParams }) => { | |||
| <Typography variant="h4" marginInlineEnd={2}> | |||
| {t("FG Production Schedule")} | |||
| </Typography> | |||
| <I18nProvider namespaces={["schedule", "common"]}> | |||
| <I18nProvider namespaces={["schedule","navigation","common"]}> | |||
| <Suspense fallback={<DetailedScheduleDetail.Loading />}> | |||
| <DetailedScheduleDetail type={type} id={parseInt(id)} /> | |||
| </Suspense> | |||
| @@ -31,7 +31,7 @@ const DetailScheduling: React.FC = async () => { | |||
| {t("Detail Scheduling")} | |||
| </Typography> | |||
| </Stack> | |||
| <I18nProvider namespaces={["schedule", "common"]}> | |||
| <I18nProvider namespaces={["schedule","navigation","common"]}> | |||
| <Suspense fallback={<DetailedSchedule.Loading />}> | |||
| <DetailedSchedule type={type} /> | |||
| </Suspense> | |||
| @@ -62,7 +62,7 @@ const roughSchedulingDetail: React.FC<Props> = async ({ searchParams }) => { | |||
| {t("Create product")} | |||
| </Button> */} | |||
| </Stack> | |||
| <I18nProvider namespaces={["schedule", "common"]}> | |||
| <I18nProvider namespaces={["schedule","navigation","common"]}> | |||
| <Suspense fallback={<RoughScheduleDetailView.Loading />}> | |||
| <RoughScheduleDetailView type={type} id={parseInt(id)} /> | |||
| </Suspense> | |||
| @@ -47,7 +47,7 @@ const roughScheduling: React.FC = async () => { | |||
| {t("Test Rough Scheduling")} | |||
| </Button> */} | |||
| </Stack> | |||
| <I18nProvider namespaces={["schedule", "common"]}> | |||
| <I18nProvider namespaces={["schedule","navigation","common"]}> | |||
| <Suspense fallback={<RoughSchedule.Loading />}> | |||
| <RoughSchedule type={type} /> | |||
| </Suspense> | |||
| @@ -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 ( | |||
| <> | |||
| <PageTitleBar title={t("BOM Weighting Score List")} className="mb-4" /> | |||
| <I18nProvider namespaces={["common"]}> | |||
| <I18nProvider namespaces={["bomWeighting","navigation","common"]}> | |||
| <BomWeightingTabs bomWeightingScores={bomWeightingScores} /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -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 <ClientMonitorPage />; | |||
| return ( | |||
| <I18nProvider namespaces={["clientMonitor", "navigation", "common"]}> | |||
| <ClientMonitorPage /> | |||
| </I18nProvider> | |||
| ); | |||
| } | |||
| @@ -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 ( | |||
| <I18nProvider namespaces={["deliveryOrderFloor", "common"]}> | |||
| <I18nProvider namespaces={["deliveryOrderFloor","navigation"]}> | |||
| <Stack spacing={2}> | |||
| <Typography variant="h4">{t("title")}</Typography> | |||
| <DeliveryOrderFloorSettings /> | |||
| @@ -13,7 +13,7 @@ type EquipmentTabsProps = { | |||
| const EquipmentTabs: React.FC<EquipmentTabsProps> = ({ 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; | |||
| @@ -9,8 +9,8 @@ import UpdateMaintenanceForm from "@/components/UpdateMaintenance/UpdateMaintena | |||
| type Props = {} & SearchParams; | |||
| const MaintenanceEditPage: React.FC<Props> = 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<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| <Typography variant="h4">{t("Update Equipment Maintenance and Repair")}</Typography> | |||
| <I18nProvider namespaces={[type]}> | |||
| <I18nProvider namespaces={[type, "navigation", "common"]}> | |||
| <UpdateMaintenanceForm id={id} /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -9,11 +9,11 @@ type Props = {} & SearchParams; | |||
| const materialSetting: React.FC<Props> = async ({ searchParams }) => { | |||
| // const type = TypeEnum.PRODUCT; | |||
| const { t } = await getServerI18n("common"); | |||
| const { t } = await getServerI18n("equipment", "navigation", "common"); | |||
| return ( | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={["common"]}> | |||
| <I18nProvider namespaces={["equipment","navigation","common"]}> | |||
| <CreateEquipmentType /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -9,8 +9,8 @@ import { notFound } from "next/navigation"; | |||
| type Props = {} & SearchParams; | |||
| const productSetting: React.FC<Props> = 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<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={[type]}> | |||
| <I18nProvider namespaces={[type, "navigation", "common"]}> | |||
| <CreateEquipmentType id={id} /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -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 () => { | |||
| </Typography> | |||
| </Stack> | |||
| <Suspense fallback={<EquipmentSearchLoading />}> | |||
| <I18nProvider namespaces={["common", "project"]}> | |||
| <I18nProvider namespaces={["equipment","navigation","common","project"]}> | |||
| <EquipmentSearchWrapper /> | |||
| </I18nProvider> | |||
| </Suspense> | |||
| @@ -9,11 +9,11 @@ type Props = {} & SearchParams; | |||
| const materialSetting: React.FC<Props> = async ({ searchParams }) => { | |||
| // const type = TypeEnum.PRODUCT; | |||
| const { t } = await getServerI18n("common"); | |||
| const { t } = await getServerI18n("equipment", "navigation", "common"); | |||
| return ( | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={["common"]}> | |||
| <I18nProvider namespaces={["equipment","navigation","common"]}> | |||
| <CreateEquipmentType /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -9,8 +9,8 @@ import { notFound } from "next/navigation"; | |||
| type Props = {} & SearchParams; | |||
| const productSetting: React.FC<Props> = 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<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={[type]}> | |||
| <I18nProvider namespaces={[type, "navigation", "common"]}> | |||
| <CreateEquipmentType id={id} /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -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 () => { | |||
| </Button> */} | |||
| </Stack> | |||
| <Suspense fallback={<EquipmentTypeSearch.Loading />}> | |||
| <I18nProvider namespaces={["common", "project"]}> | |||
| <I18nProvider namespaces={["equipment","navigation","common","project"]}> | |||
| <EquipmentTypeSearch /> | |||
| </I18nProvider> | |||
| </Suspense> | |||
| @@ -9,8 +9,8 @@ import UpdateMaintenanceForm from "@/components/UpdateMaintenance/UpdateMaintena | |||
| type Props = {} & SearchParams; | |||
| const MaintenanceEditPage: React.FC<Props> = 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<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| <Typography variant="h4">{t("Update Equipment Maintenance and Repair")}</Typography> | |||
| <I18nProvider namespaces={[type]}> | |||
| <I18nProvider namespaces={[type, "navigation", "common"]}> | |||
| <UpdateMaintenanceForm id={id} /> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| }; | |||
| export default MaintenanceEditPage; | |||
| export default MaintenanceEditPage; | |||
| @@ -13,7 +13,7 @@ const materialSetting: React.FC<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={["common"]}> | |||
| <I18nProvider namespaces={["importBom","navigation","common"]}> | |||
| <CreateEquipmentType /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -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<Props> = 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<Props> = async ({ searchParams }) => { | |||
| } | |||
| return ( | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={[type]}> | |||
| <I18nProvider namespaces={[type, "navigation", "common"]}> | |||
| <CreateEquipmentType id={id} /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -21,7 +21,7 @@ export default async function ImportBomPage() { | |||
| Import BOM | |||
| </Typography> | |||
| </Stack> | |||
| <I18nProvider namespaces={["common"]}> | |||
| <I18nProvider namespaces={["importBom","navigation","common","importExcel"]}> | |||
| <ImportBomWrapper /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -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 ( | |||
| <> | |||
| <Stack | |||
| direction="row" | |||
| justifyContent="space-between" | |||
| flexWrap="wrap" | |||
| rowGap={2} | |||
| > | |||
| <Typography variant="h4" marginInlineEnd={2}> | |||
| {t("Excel File Import")} | |||
| </Typography> | |||
| </Stack> | |||
| <Suspense> | |||
| <ExcelFileImport /> | |||
| </Suspense> | |||
| </> | |||
| ) | |||
| return ( | |||
| <I18nProvider namespaces={["importExcel", "navigation", "common"]}> | |||
| <Stack | |||
| direction="row" | |||
| justifyContent="space-between" | |||
| flexWrap="wrap" | |||
| rowGap={2} | |||
| > | |||
| <Typography variant="h4" marginInlineEnd={2}> | |||
| {t("title")} | |||
| </Typography> | |||
| </Stack> | |||
| <Suspense> | |||
| <ExcelFileImport /> | |||
| </Suspense> | |||
| </I18nProvider> | |||
| ); | |||
| }; | |||
| export default ImportExcel; | |||
| @@ -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 ( | |||
| <> | |||
| <PageTitleBar title={t("Price Inquiry", { ns: "common" })} className="mb-4" /> | |||
| <PageTitleBar title={t("Price Inquiry")} className="mb-4" /> | |||
| <I18nProvider namespaces={["common", "inventory"]}> | |||
| <I18nProvider namespaces={["itemPrice","navigation","common","inventory","importExcel"]}> | |||
| <Suspense fallback={<ItemPriceSearch.Loading />}> | |||
| <ItemPriceSearch /> | |||
| </Suspense> | |||
| @@ -13,7 +13,7 @@ const materialSetting: React.FC<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={["items"]}> | |||
| <I18nProvider namespaces={["items","navigation","common"]}> | |||
| <CreateItem /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -24,7 +24,7 @@ const productSetting: React.FC<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={[type]}> | |||
| <I18nProvider namespaces={[type, "navigation", "common"]}> | |||
| <CreateProductMaterial id={id} /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -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 () => { | |||
| </Button> */} | |||
| </Stack> | |||
| <I18nProvider namespaces={["project", "common", "items"]}> | |||
| <I18nProvider namespaces={["items","navigation","common"]}> | |||
| <Suspense fallback={<ItemsSearch.Loading />}> | |||
| <ItemsSearch /> | |||
| </Suspense> | |||
| @@ -21,7 +21,7 @@ const M18ImportTestingPage: React.FC = async () => { | |||
| rowGap={2} | |||
| ></Stack> | |||
| <Suspense fallback={<M18ImportTesting.Loading />}> | |||
| <I18nProvider namespaces={["common", "m18ImportTesting"]}> | |||
| <I18nProvider namespaces={["m18ImportTesting","navigation","common"]}> | |||
| <M18ImportTesting /> | |||
| </I18nProvider> | |||
| </Suspense> | |||
| @@ -29,7 +29,7 @@ const Customer: React.FC = async () => { | |||
| {t("Mail")} | |||
| </Typography> | |||
| </Stack> | |||
| <I18nProvider namespaces={["mail", "common"]}> | |||
| <I18nProvider namespaces={["mail","navigation","common"]}> | |||
| <Suspense fallback={<MailSetting.Loading />}> | |||
| <MailSetting /> | |||
| </Suspense> | |||
| @@ -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 ( | |||
| <> | |||
| <Typography variant="h4">{t("Create Printer") || "新增列印機"}</Typography> | |||
| <I18nProvider namespaces={["common"]}> | |||
| <I18nProvider namespaces={["printer","navigation","common"]}> | |||
| <Suspense fallback={<CreatePrinter.Loading />}> | |||
| <CreatePrinter /> | |||
| </Suspense> | |||
| @@ -10,7 +10,7 @@ import { fetchPrinterDetails } from "@/app/api/settings/printer/actions"; | |||
| type Props = {} & SearchParams; | |||
| const EditPrinterPage: React.FC<Props> = 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<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| <Typography variant="h4">{t("Edit")} {t("Printer")}</Typography> | |||
| <I18nProvider namespaces={["common"]}> | |||
| <I18nProvider namespaces={["printer","navigation","common"]}> | |||
| <Suspense fallback={<div>Loading...</div>}> | |||
| <EditPrinter printer={printer} /> | |||
| </Suspense> | |||
| @@ -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") || "新增列印機"} | |||
| </Button> | |||
| </Stack> | |||
| <I18nProvider namespaces={["common", "dashboard"]}> | |||
| <I18nProvider namespaces={["printer","navigation","common","dashboard"]}> | |||
| <Suspense fallback={<PrinterSearch.Loading />}> | |||
| <PrinterSearch /> | |||
| </Suspense> | |||
| @@ -43,7 +43,7 @@ const qcCategory: React.FC<Props> = async ({ searchParams }) => { | |||
| <Typography variant="h4" marginInlineEnd={2}> | |||
| {t("Edit Qc Category")} | |||
| </Typography> | |||
| <I18nProvider namespaces={["qcCategory"]}> | |||
| <I18nProvider namespaces={["qcCategory","navigation","common"]}> | |||
| <QcCategorySave id={id} /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -16,7 +16,7 @@ const qcItem: React.FC = async () => { | |||
| <Typography variant="h4" marginInlineEnd={2}> | |||
| {t("Create Qc Item")} | |||
| </Typography> | |||
| <I18nProvider namespaces={["qcItem"]}> | |||
| <I18nProvider namespaces={["qcItem","navigation","common"]}> | |||
| <QcItemSave /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -43,7 +43,7 @@ const qcItem: React.FC<Props> = async ({ searchParams }) => { | |||
| <Typography variant="h4" marginInlineEnd={2}> | |||
| {t("Edit Qc Item")} | |||
| </Typography> | |||
| <I18nProvider namespaces={["qcItem"]}> | |||
| <I18nProvider namespaces={["qcItem","navigation","common"]}> | |||
| <QcItemSave id={id} /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -37,7 +37,7 @@ const qcItem: React.FC = async () => { | |||
| </Button> | |||
| </Stack> | |||
| <Suspense fallback={<QcItemSearch.Loading />}> | |||
| <I18nProvider namespaces={["common", "qcItem"]}> | |||
| <I18nProvider namespaces={["qcItem","navigation","common"]}> | |||
| <QcItemSearch /> | |||
| </I18nProvider> | |||
| </Suspense> | |||
| @@ -30,7 +30,7 @@ const qcItemAll: React.FC = async () => { | |||
| </Typography> | |||
| </Stack> | |||
| <Suspense fallback={<div>Loading...</div>}> | |||
| <I18nProvider namespaces={["common", "qcItemAll", "qcCategory", "qcItem"]}> | |||
| <I18nProvider namespaces={["qcItemAll","navigation","common","qcCategory","qcItem"]}> | |||
| <QcItemAllTabs | |||
| tab0Content={<Tab0ItemQcCategoryMapping />} | |||
| tab1Content={<Tab1QcCategoryQcItemMapping />} | |||
| @@ -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 ( | |||
| <Box sx={{ width: "100%" }}> | |||
| @@ -20,25 +20,25 @@ const QrCodeHandlePage: React.FC = async () => { | |||
| {t("QR Code Handle")} | |||
| </Typography> | |||
| <I18nProvider namespaces={["common", "user", "warehouse"]}> | |||
| <I18nProvider namespaces={["qrCodeHandle","navigation","common","user","warehouse"]}> | |||
| <QrCodeHandleTabs | |||
| userTabContent={ | |||
| <Suspense fallback={<QrCodeHandleSearchWrapper.Loading />}> | |||
| <I18nProvider namespaces={["user", "common", "dashboard"]}> | |||
| <I18nProvider namespaces={["qrCodeHandle","navigation","common","user","dashboard"]}> | |||
| <QrCodeHandleSearchWrapper /> | |||
| </I18nProvider> | |||
| </Suspense> | |||
| } | |||
| equipmentTabContent={ | |||
| <Suspense fallback={<QrCodeHandleEquipmentSearchWrapper.Loading />}> | |||
| <I18nProvider namespaces={["common", "project", "dashboard"]}> | |||
| <I18nProvider namespaces={["qrCodeHandle","navigation","common","project","dashboard"]}> | |||
| <QrCodeHandleEquipmentSearchWrapper /> | |||
| </I18nProvider> | |||
| </Suspense> | |||
| } | |||
| warehouseTabContent={ | |||
| <Suspense fallback={<QrCodeHandleWarehouseSearchWrapper.Loading />}> | |||
| <I18nProvider namespaces={["warehouse", "common", "dashboard"]}> | |||
| <I18nProvider namespaces={["qrCodeHandle","navigation","common","warehouse","dashboard"]}> | |||
| <QrCodeHandleWarehouseSearchWrapper /> | |||
| </I18nProvider> | |||
| </Suspense> | |||
| @@ -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 () => { | |||
| </Button> */} | |||
| </Stack> | |||
| <Suspense fallback={<RoughScheduleSettingWrapper.Loading />}> | |||
| <I18nProvider namespaces={[ "common", "project",]}> | |||
| <I18nProvider namespaces={["demandForecast", "navigation", "common", "project"]}> | |||
| <RoughScheduleSetting items={[]} /> | |||
| </I18nProvider> | |||
| </Suspense> | |||
| @@ -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 ( | |||
| <Box | |||
| sx={{ | |||
| @@ -16,7 +16,7 @@ export default async function ShopRouteBoardPage() { | |||
| flexDirection: "column", | |||
| }} | |||
| > | |||
| <I18nProvider namespaces={["shop", "common", "routeboard"]}> | |||
| <I18nProvider namespaces={["shop","navigation"]}> | |||
| <Suspense fallback={<GeneralLoading />}> | |||
| <RouteBoard /> | |||
| </Suspense> | |||
| @@ -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 ( | |||
| <I18nProvider namespaces={["shop", "common"]}> | |||
| <I18nProvider namespaces={["shop","navigation"]}> | |||
| <Suspense fallback={<GeneralLoading />}> | |||
| <ShopDetail /> | |||
| </Suspense> | |||
| @@ -8,9 +8,9 @@ import { notFound } from "next/navigation"; | |||
| export default async function ShopPage() { | |||
| const { t } = await getServerI18n("shop", "common"); | |||
| await getServerI18n("shop"); | |||
| return ( | |||
| <I18nProvider namespaces={["shop", "common"]}> | |||
| <I18nProvider namespaces={["shop","navigation"]}> | |||
| <Suspense fallback={<ShopWrapper.Loading />}> | |||
| <ShopWrapper /> | |||
| </Suspense> | |||
| @@ -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 ( | |||
| <I18nProvider namespaces={["shop", "common"]}> | |||
| <I18nProvider namespaces={["shop","navigation"]}> | |||
| <Suspense fallback={<GeneralLoading />}> | |||
| <TruckLaneDetail /> | |||
| </Suspense> | |||
| @@ -17,7 +17,7 @@ const CreateStaffPage: React.FC = async () => { | |||
| return ( | |||
| <> | |||
| <Typography variant="h4">{t("Create User")}</Typography> | |||
| <I18nProvider namespaces={["user", "common"]}> | |||
| <I18nProvider namespaces={["user","navigation","common"]}> | |||
| <Suspense fallback={<CreateUser.Loading />}> | |||
| <CreateUser /> | |||
| </Suspense> | |||
| @@ -18,7 +18,7 @@ const User: React.FC<searchParamsProps> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| <Typography variant="h4">{t("Edit User")}</Typography> | |||
| <I18nProvider namespaces={["user", "common"]}> | |||
| <I18nProvider namespaces={["user","navigation","common"]}> | |||
| <Suspense fallback={<EditUser.Loading />}> | |||
| <EditUser searchParams={searchParams} /> | |||
| </Suspense> | |||
| @@ -47,7 +47,7 @@ const User: React.FC = async () => { | |||
| {t("Create User")} | |||
| </Button> | |||
| </Stack> | |||
| <I18nProvider namespaces={["user", "common", "dashboard"]}> | |||
| <I18nProvider namespaces={["user","navigation","common","dashboard"]}> | |||
| <UserExcelSheetView users={usersWithDetails} /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -9,7 +9,7 @@ const CreateWarehousePage: React.FC = async () => { | |||
| return ( | |||
| <> | |||
| <Typography variant="h4">{t("Create Warehouse")}</Typography> | |||
| <I18nProvider namespaces={["warehouse", "common"]}> | |||
| <I18nProvider namespaces={["warehouse","navigation","common"]}> | |||
| <Suspense fallback={<CreateWarehouse.Loading />}> | |||
| <CreateWarehouse /> | |||
| </Suspense> | |||
| @@ -31,7 +31,7 @@ const Warehouse: React.FC = async () => { | |||
| {t("Create Warehouse")} | |||
| </Button> | |||
| </Stack> | |||
| <I18nProvider namespaces={["warehouse", "common", "dashboard"]}> | |||
| <I18nProvider namespaces={["warehouse","navigation","common","dashboard"]}> | |||
| <Suspense fallback={null}> | |||
| <WarehouseTabs | |||
| tab0Content={<WarehouseHandleWrapper />} | |||
| @@ -11,7 +11,7 @@ export const metadata: Metadata = { | |||
| const SearchView: React.FC = async () => { | |||
| return ( | |||
| <> | |||
| <I18nProvider namespaces={["inventory", "common"]}> | |||
| <I18nProvider namespaces={["stockIssue", "navigation", "common"]}> | |||
| <Suspense fallback={<SearchPage.Loading />}> | |||
| <SearchPage /> | |||
| </Suspense> | |||
| @@ -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<Props> = async ({ searchParams }) => { | |||
| const { t } = await getServerI18n("pickOrder"); | |||
| const StockOutIssueRecordDetail: React.FC<Props> = async ({ searchParams }) => { | |||
| return ( | |||
| <> | |||
| <I18nProvider namespaces={["pickOrder"]}> | |||
| <I18nProvider namespaces={["pickOrder", "navigation", "common"]}> | |||
| <Suspense fallback={<PickOrderDetail.Loading />}> | |||
| <PickOrderDetail consoCode={`${searchParams["consoCode"]}`} /> | |||
| </Suspense> | |||
| @@ -27,4 +21,4 @@ const PickOrder: React.FC<Props> = async ({ searchParams }) => { | |||
| ); | |||
| }; | |||
| export default PickOrder; | |||
| export default StockOutIssueRecordDetail; | |||
| @@ -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 ( | |||
| <> | |||
| <I18nProvider namespaces={["pickOrder", "common"]}> | |||
| <I18nProvider namespaces={["pickOrder", "navigation", "common"]}> | |||
| <Suspense fallback={<PickOrderSearch.Loading />}> | |||
| <PickOrderSearch /> | |||
| </Suspense> | |||
| @@ -26,4 +19,4 @@ const PickOrder: React.FC = async () => { | |||
| ); | |||
| }; | |||
| export default PickOrder; | |||
| export default StockOutIssueRecord; | |||
| @@ -9,11 +9,11 @@ export const metadata: Metadata = { | |||
| }; | |||
| const SearchView: React.FC = async () => { | |||
| const { t } = await getServerI18n("inventory"); | |||
| const { t } = await getServerI18n("stockRecord"); | |||
| return ( | |||
| <> | |||
| <I18nProvider namespaces={["inventory", "common"]}> | |||
| <I18nProvider namespaces={["stockRecord", "navigation", "common"]}> | |||
| <Suspense fallback={<SearchPage.Loading />}> | |||
| <SearchPage /> | |||
| </Suspense> | |||
| @@ -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 ( | |||
| <I18nProvider namespaces={["inventory","common"]}> | |||
| <I18nProvider namespaces={["stockTake", "navigation", "common"]}> | |||
| <Suspense fallback={<StockTakeManagementWrapper.Loading />}> | |||
| <StockTakeManagementWrapper /> | |||
| </Suspense> | |||
| @@ -14,7 +14,7 @@ export const metadata: Metadata = { | |||
| }; | |||
| const TaskTemplates: React.FC = async () => { | |||
| const { t } = await getServerI18n("projects"); | |||
| const { t } = await getServerI18n("project"); | |||
| preloadTaskTemplates(); | |||
| return ( | |||
| @@ -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<TruckLaneScheduleResponse> => { | |||
| const endpoint = `${BASE_API_URL}/truckLaneSchedule`; | |||
| return serverFetchJson<TruckLaneScheduleResponse>(endpoint, { | |||
| method: "POST", | |||
| body: JSON.stringify(data), | |||
| headers: { "Content-Type": "application/json" }, | |||
| }); | |||
| }; | |||
| export const planTruckLaneScheduleFromRouteExcelAction = async ( | |||
| formData: FormData, | |||
| ): Promise<RouteExcelSchedulePlanResponse> => { | |||
| 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<TruckLaneScheduleResponse[]> => { | |||
| 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<TruckLaneScheduleResponse[]>( | |||
| `${base}${qs ? `?${qs}` : ""}`, | |||
| { | |||
| method: "GET", | |||
| headers: { "Content-Type": "application/json" }, | |||
| }, | |||
| ); | |||
| }; | |||
| export const getTruckLaneScheduleAction = async ( | |||
| id: number, | |||
| ): Promise<TruckLaneScheduleResponse> => { | |||
| const endpoint = `${BASE_API_URL}/truckLaneSchedule/${id}`; | |||
| return serverFetchJson<TruckLaneScheduleResponse>(endpoint, { | |||
| method: "GET", | |||
| headers: { "Content-Type": "application/json" }, | |||
| }); | |||
| }; | |||
| export const pendingTruckLaneScheduleShopIdsAction = | |||
| async (): Promise<PendingTruckRowIdsResponse> => { | |||
| const endpoint = `${BASE_API_URL}/truckLaneSchedule/pendingShopIds`; | |||
| return serverFetchJson<PendingTruckRowIdsResponse>(endpoint, { | |||
| method: "GET", | |||
| headers: { "Content-Type": "application/json" }, | |||
| }); | |||
| }; | |||
| export const cancelTruckLaneScheduleAction = async ( | |||
| id: number, | |||
| ): Promise<TruckLaneScheduleResponse> => { | |||
| const endpoint = `${BASE_API_URL}/truckLaneSchedule/${id}/cancel`; | |||
| return serverFetchJson<TruckLaneScheduleResponse>(endpoint, { | |||
| method: "POST", | |||
| headers: { "Content-Type": "application/json" }, | |||
| }); | |||
| }; | |||
| export const applyNowTruckLaneScheduleAction = async ( | |||
| id: number, | |||
| ): Promise<TruckLaneScheduleResponse> => { | |||
| const endpoint = `${BASE_API_URL}/truckLaneSchedule/${id}/applyNow`; | |||
| return serverFetchJson<TruckLaneScheduleResponse>(endpoint, { | |||
| method: "POST", | |||
| headers: { "Content-Type": "application/json" }, | |||
| }); | |||
| }; | |||
| export type RetryFailedTruckLaneScheduleRequest = { | |||
| executeAt?: string | null; | |||
| }; | |||
| export const retryFailedTruckLaneScheduleAction = async ( | |||
| id: number, | |||
| body?: RetryFailedTruckLaneScheduleRequest, | |||
| ): Promise<TruckLaneScheduleResponse> => { | |||
| const endpoint = `${BASE_API_URL}/truckLaneSchedule/${id}/retry-failed`; | |||
| return serverFetchJson<TruckLaneScheduleResponse>(endpoint, { | |||
| method: "POST", | |||
| body: JSON.stringify(body ?? {}), | |||
| headers: { "Content-Type": "application/json" }, | |||
| }); | |||
| }; | |||
| export const ignoreTruckLaneScheduleAction = async ( | |||
| id: number, | |||
| ): Promise<TruckLaneScheduleResponse> => { | |||
| const endpoint = `${BASE_API_URL}/truckLaneSchedule/${id}/ignore`; | |||
| return serverFetchJson<TruckLaneScheduleResponse>(endpoint, { | |||
| method: "POST", | |||
| headers: { "Content-Type": "application/json" }, | |||
| }); | |||
| }; | |||
| export const parseTruckLaneScheduleExcelAction = async ( | |||
| formData: FormData, | |||
| defaultExecuteAt?: string | null, | |||
| ): Promise<ParseTruckLaneScheduleExcelResponse> => { | |||
| 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<TruckLaneScheduleResponse[]> => { | |||
| 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[]; | |||
| }; | |||
| @@ -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<RouteExcelSchedulePlanResponse> => { | |||
| return await planTruckLaneScheduleFromRouteExcelAction(formData); | |||
| }; | |||
| export const createTruckLaneScheduleClient = async ( | |||
| data: CreateTruckLaneScheduleRequest, | |||
| ): Promise<TruckLaneScheduleResponse> => { | |||
| return await createTruckLaneScheduleAction(data); | |||
| }; | |||
| export const listTruckLaneSchedulesClient = async ( | |||
| status?: string[], | |||
| limit: number = 200, | |||
| ): Promise<TruckLaneScheduleResponse[]> => { | |||
| return await listTruckLaneSchedulesAction(status, limit); | |||
| }; | |||
| export const getTruckLaneScheduleClient = async ( | |||
| id: number, | |||
| ): Promise<TruckLaneScheduleResponse> => { | |||
| return await getTruckLaneScheduleAction(id); | |||
| }; | |||
| export const pendingTruckLaneScheduleShopIdsClient = | |||
| async (): Promise<PendingTruckRowIdsResponse> => { | |||
| return await pendingTruckLaneScheduleShopIdsAction(); | |||
| }; | |||
| export const cancelTruckLaneScheduleClient = async ( | |||
| id: number, | |||
| ): Promise<TruckLaneScheduleResponse> => { | |||
| return await cancelTruckLaneScheduleAction(id); | |||
| }; | |||
| export const applyNowTruckLaneScheduleClient = async ( | |||
| id: number, | |||
| ): Promise<TruckLaneScheduleResponse> => { | |||
| return await applyNowTruckLaneScheduleAction(id); | |||
| }; | |||
| export const retryFailedTruckLaneScheduleClient = async ( | |||
| id: number, | |||
| body?: RetryFailedTruckLaneScheduleRequest, | |||
| ): Promise<TruckLaneScheduleResponse> => { | |||
| return await retryFailedTruckLaneScheduleAction(id, body); | |||
| }; | |||
| export const ignoreTruckLaneScheduleClient = async ( | |||
| id: number, | |||
| ): Promise<TruckLaneScheduleResponse> => { | |||
| return await ignoreTruckLaneScheduleAction(id); | |||
| }; | |||
| export const parseTruckLaneScheduleExcelClient = async ( | |||
| formData: FormData, | |||
| defaultExecuteAt?: string | null, | |||
| ): Promise<ParseTruckLaneScheduleExcelResponse> => { | |||
| return await parseTruckLaneScheduleExcelAction(formData, defaultExecuteAt); | |||
| }; | |||
| export const importTruckLaneScheduleExcelClient = async ( | |||
| formData: FormData, | |||
| defaultExecuteAt?: string | null, | |||
| note?: string | null, | |||
| ): Promise<TruckLaneScheduleResponse[]> => { | |||
| return await importTruckLaneScheduleExcelAction( | |||
| formData, | |||
| defaultExecuteAt, | |||
| note, | |||
| ); | |||
| }; | |||
| export type { | |||
| TruckLaneScheduleResponse, | |||
| TruckLaneScheduleLineResponse, | |||
| TruckLaneMoveTargetRequest, | |||
| TruckLaneScheduleLineRequest, | |||
| CreateTruckLaneScheduleRequest, | |||
| PendingTruckRowIdsResponse, | |||
| ParseTruckLaneScheduleExcelResponse, | |||
| RouteExcelSchedulePlanResponse, | |||
| RouteExcelSchedulePlanPreviewRow, | |||
| RouteExcelSchedulePlanError, | |||
| RouteExcelSchedulePlanCounts, | |||
| } from "./actions"; | |||
| export default fetchAllShopsClient; | |||
| @@ -16,7 +16,7 @@ export interface AppBarProps { | |||
| const AppBar: React.FC<AppBarProps> = ({ avatarImageSrc, profileName }) => { | |||
| return ( | |||
| <I18nProvider namespaces={["common"]}> | |||
| <I18nProvider namespaces={["navigation", "common"]}> | |||
| <MUIAppBar position="sticky" color="default" elevation={0}> | |||
| <Toolbar sx={{ minHeight: { xs: 56, sm: 64 }, px: 2 }}> | |||
| <NavigationToggle /> | |||
| @@ -29,7 +29,7 @@ const Profile: React.FC<Props> = ({ avatarImageSrc, profileName }) => { | |||
| return ( | |||
| <> | |||
| <IconButton aria-label="profile" onClick={openProfileMenu}> | |||
| <IconButton aria-label={t("profile")} onClick={openProfileMenu}> | |||
| <Avatar src={avatarImageSrc} /> | |||
| </IconButton> | |||
| <Menu | |||
| @@ -12,19 +12,19 @@ interface Props { | |||
| } | |||
| const BomScoreTable: React.FC<Props> = ({ boms }) => { | |||
| const { t } = useTranslation("common"); | |||
| const { t } = useTranslation(["bomWeighting", "common"]); | |||
| const columns = useMemo<GridColDef<BomScoreResult>[]>( | |||
| () => [ | |||
| { | |||
| 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, | |||
| }, | |||
| @@ -20,7 +20,7 @@ interface Props { | |||
| } | |||
| const BomWeightingScoreTable: React.FC<Props> & { 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<Props> & { Loading?: React.FC } = ({ bomW | |||
| () => [ | |||
| { | |||
| field: "name", | |||
| headerName: t("Name"), | |||
| headerName: t("Scoring Item"), | |||
| flex: 1, | |||
| }, | |||
| { | |||
| @@ -18,7 +18,7 @@ interface Props { | |||
| } | |||
| const BomWeightingTabs: React.FC<Props> = ({ bomWeightingScores: initialBomWeightingScores }) => { | |||
| const { t } = useTranslation("common"); | |||
| const { t } = useTranslation(["bomWeighting", "common"]); | |||
| const [tab, setTab] = useState(0); | |||
| const [bomWeightingScores, setBomWeightingScores] = useState<BomWeightingScoreResult[]>(initialBomWeightingScores); | |||
| const [bomScores, setBomScores] = useState<BomScoreResult[] | null>(null); | |||
| @@ -7,75 +7,86 @@ 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/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 ( | |||
| <Breadcrumbs> | |||
| {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 ( | |||
| <Typography key={index} color="text.primary"> | |||
| {t(label)} | |||
| {label} | |||
| </Typography> | |||
| ); | |||
| } else { | |||
| @@ -87,7 +98,7 @@ const Breadcrumb = () => { | |||
| component={Link} | |||
| href={href || "/"} | |||
| > | |||
| {t(label)} | |||
| {label} | |||
| </MUILink> | |||
| ); | |||
| } | |||
| @@ -271,8 +271,8 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => { | |||
| <GridActionsCellItem | |||
| key={`actions-${id}-save`} | |||
| icon={<SaveIcon />} | |||
| title="Save" | |||
| label="Save" | |||
| title={t("Save")} | |||
| label={t("Save")} | |||
| sx={{ | |||
| color: "primary.main", | |||
| }} | |||
| @@ -281,8 +281,8 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => { | |||
| <GridActionsCellItem | |||
| key={`actions-${id}-cancel`} | |||
| icon={<CancelIcon />} | |||
| 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<ClaimInputGridProps> = ({ ...props }) => { | |||
| <GridActionsCellItem | |||
| key={`actions-${id}-edit`} | |||
| icon={<EditIcon />} | |||
| title="Edit" | |||
| label="Edit" | |||
| title={t("Edit")} | |||
| label={t("Edit")} | |||
| className="textPrimary" | |||
| onClick={handleEditClick(id)} | |||
| color="inherit" | |||
| />, | |||
| <GridActionsCellItem | |||
| key={`actions-${id}-delete`} | |||
| title="Delete" | |||
| label="Delete" | |||
| title={t("Delete")} | |||
| label={t("Delete")} | |||
| icon={<DeleteIcon />} | |||
| onClick={handleDeleteClick(id)} | |||
| sx={{ color: "red" }} | |||
| @@ -313,7 +313,7 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => { | |||
| }, | |||
| { | |||
| field: "date", | |||
| headerName: "Invoice Date", | |||
| headerName: t("Invoice Date"), | |||
| // width: 220, | |||
| flex: 1, | |||
| editable: true, | |||
| @@ -321,7 +321,7 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => { | |||
| }, | |||
| { | |||
| field: "description", | |||
| headerName: "Description", | |||
| headerName: t("Description"), | |||
| // width: 220, | |||
| flex: 2, | |||
| editable: true, | |||
| @@ -329,7 +329,7 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => { | |||
| }, | |||
| { | |||
| field: "cost", | |||
| headerName: "Cost (HKD)", | |||
| headerName: t("Cost (HKD)"), | |||
| editable: true, | |||
| type: "number", | |||
| valueFormatter: (params) => { | |||
| @@ -338,7 +338,7 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => { | |||
| }, | |||
| { | |||
| field: "document", | |||
| headerName: "Supporting Document", | |||
| headerName: t("Supporting Document"), | |||
| type: "string", | |||
| editable: true, | |||
| flex: 2, | |||
| @@ -360,7 +360,7 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => { | |||
| {params.value} | |||
| </a> | |||
| <Button | |||
| title="Remove Document" | |||
| title={t("Remove Document")} | |||
| onClick={(event) => console.log(event)} | |||
| > | |||
| <ImageNotSupportedOutlinedIcon | |||
| @@ -369,7 +369,7 @@ const ClaimInputGrid: React.FC<ClaimInputGridProps> = ({ ...props }) => { | |||
| </Button> | |||
| </span> | |||
| ) : ( | |||
| <Button title="Add Document"> | |||
| <Button title={t("Add Document")}> | |||
| <AddPhotoAlternateOutlinedIcon | |||
| sx={{ fontSize: "25px", color: "green" }} | |||
| /> | |||
| @@ -49,7 +49,7 @@ const CreateItem: React.FC<Props> = ({ | |||
| console.log(params.get("id")); | |||
| const [serverError, setServerError] = useState(""); | |||
| const [tabIndex, setTabIndex] = useState(0); | |||
| const { t } = useTranslation("common"); | |||
| const { t } = useTranslation(["equipment", "common"]); | |||
| const router = useRouter(); | |||
| const title = "Equipment"; | |||
| const [mode, redirPath] = useMemo(() => { | |||
| @@ -33,7 +33,7 @@ const ProductDetails: React.FC<Props> = ({ isEditMode }) => { | |||
| const { | |||
| t, | |||
| i18n: { language }, | |||
| } = useTranslation(); | |||
| } = useTranslation(["equipment", "common"]); | |||
| const { | |||
| register, | |||
| @@ -49,7 +49,7 @@ const CreateItem: React.FC<Props> = ({ | |||
| console.log(params.get("id")); | |||
| const [serverError, setServerError] = useState(""); | |||
| const [tabIndex, setTabIndex] = useState(0); | |||
| const { t } = useTranslation("common"); | |||
| const { t } = useTranslation(["equipment", "common"]); | |||
| const router = useRouter(); | |||
| const title = "Equipment Type"; | |||
| const [mode, redirPath] = useMemo(() => { | |||
| @@ -33,7 +33,7 @@ const ProductDetails: React.FC<Props> = ({ isEditMode }) => { | |||
| const { | |||
| t, | |||
| i18n: { language }, | |||
| } = useTranslation(); | |||
| } = useTranslation(["equipment", "common"]); | |||
| const { | |||
| register, | |||