| @@ -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","home"]}> | |||
| <Suspense fallback={<DoDetail.Loading />}> | |||
| <DoDetail id={parseInt(id)} /> | |||
| </Suspense> | |||
| @@ -23,7 +23,7 @@ const Page: React.FC = async () => { | |||
| /doworkbench | |||
| </Link> | |||
| </p> | |||
| <I18nProvider namespaces={["do", "common"]}> | |||
| <I18nProvider namespaces={["do","navigation","common","home"]}> | |||
| <Suspense fallback={<GeneralLoading />}> | |||
| <DoSearchWorkbench workbenchHrefBase="/do copy 2" /> | |||
| </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","home"]}> | |||
| <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","home"]}> | |||
| <Suspense fallback={<DoSearch.Loading />}> | |||
| <DoSearch /> | |||
| </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","home"]}> | |||
| <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","home"]}> | |||
| <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","home"]}> | |||
| <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","purchaseOrder","home","item"]}> | |||
| <Suspense fallback={<FinishedGoodSearchWrapper.Loading />}> | |||
| <FinishedGoodSearchWrapper /> | |||
| </Suspense> | |||
| @@ -18,7 +18,7 @@ const Page = async () => { | |||
| redirect("/dashboard"); | |||
| } | |||
| return ( | |||
| <I18nProvider namespaces={["common"]}> | |||
| <I18nProvider namespaces={["finishedgoodmanagement","navigation","common"]}> | |||
| <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","purchaseOrder","home","item"]}> | |||
| <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","item"]}> | |||
| <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","purchaseOrder","home","item"]}> | |||
| <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; | |||
| } | |||
| @@ -45,7 +45,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","purchaseOrder"]}> | |||
| <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","purchaseOrder","home","item"]}> | |||
| <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", "home"]}> | |||
| <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","dashboard"]}> | |||
| <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","common"]}> | |||
| <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> | |||
| @@ -13,7 +13,7 @@ const MasterDataIssuesPage: React.FC = async () => { | |||
| return ( | |||
| <> | |||
| <PageTitleBar title={t("masterDataIssue_pageTitle")} className="mb-4" /> | |||
| <I18nProvider namespaces={["masterDataIssue"]}> | |||
| <I18nProvider namespaces={["masterDataIssue","navigation","common"]}> | |||
| <MasterDataIssuesTabs /> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -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> | |||
| </> | |||
| @@ -36,9 +36,11 @@ const qcCategory: React.FC = async () => { | |||
| {t("Create Qc Category")} | |||
| </Button> | |||
| </Stack> | |||
| <Suspense fallback={<QcCategorySearch.Loading />}> | |||
| <QcCategorySearch /> | |||
| </Suspense> | |||
| <I18nProvider namespaces={["qcCategory","navigation","common"]}> | |||
| <Suspense fallback={<QcCategorySearch.Loading />}> | |||
| <QcCategorySearch /> | |||
| </Suspense> | |||
| </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> | |||
| @@ -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","common"]}> | |||
| <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","common"]}> | |||
| <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","common"]}> | |||
| <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","common"]}> | |||
| <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","purchaseOrder"]}> | |||
| <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","purchaseOrder","home","item"]}> | |||
| <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","pickOrder"]}> | |||
| <Suspense fallback={<StockTakeManagementWrapper.Loading />}> | |||
| <StockTakeManagementWrapper /> | |||
| </Suspense> | |||
| @@ -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 ( | |||
| <> | |||
| <Typography variant="h4">{t("Create Task Template")}</Typography> | |||
| <CreateTaskTemplate /> | |||
| <I18nProvider namespaces={["tasks","project","navigation","common"]}> | |||
| <Typography variant="h4">{t("Create Task Template")}</Typography> | |||
| <CreateTaskTemplate /> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -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")} | |||
| </Button> | |||
| </Stack> | |||
| <Suspense fallback={<TaskTemplateSearch.Loading />}> | |||
| <TaskTemplateSearch /> | |||
| </Suspense> | |||
| <I18nProvider namespaces={["project","tasks","navigation","common"]}> | |||
| <Suspense fallback={<TaskTemplateSearch.Loading />}> | |||
| <TaskTemplateSearch /> | |||
| </Suspense> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -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 /> | |||