diff --git a/src/app/(main)/settings/qcItemAll/page.tsx b/src/app/(main)/settings/qcItemAll/page.tsx index 78cc17c..9fb3ec5 100644 --- a/src/app/(main)/settings/qcItemAll/page.tsx +++ b/src/app/(main)/settings/qcItemAll/page.tsx @@ -9,9 +9,12 @@ import Tab1QcCategoryQcItemMapping from "@/components/QcItemAll/Tab1QcCategoryQc import Tab2QcCategoryManagement from "@/components/QcItemAll/Tab2QcCategoryManagement"; import Tab3QcItemManagement from "@/components/QcItemAll/Tab3QcItemManagement"; -export const metadata: Metadata = { - title: "Qc Item All", -}; +export async function generateMetadata(): Promise { + const { t } = await getServerI18n("qcItemAll"); + return { + title: t("Qc Item All"), + }; +} const qcItemAll: React.FC = async () => { const { t } = await getServerI18n("qcItemAll"); @@ -29,7 +32,7 @@ const qcItemAll: React.FC = async () => { {t("Qc Item All")} - Loading...}> + {t("Loading...")}}> } diff --git a/src/components/Breadcrumb/Breadcrumb.tsx b/src/components/Breadcrumb/Breadcrumb.tsx index 73b2ad2..f012cd6 100644 --- a/src/components/Breadcrumb/Breadcrumb.tsx +++ b/src/components/Breadcrumb/Breadcrumb.tsx @@ -20,6 +20,15 @@ const pathToLabelKey: { [path: string]: string } = { "/projects/create": "nav.breadcrumb.projectsCreate", "/tasks": "nav.breadcrumb.tasks", "/tasks/create": "nav.breadcrumb.tasksCreate", + "/settings": "nav.settings", + "/settings/user": "nav.settings.user", + "/settings/clientMonitor": "nav.settings.clientMonitor", + "/settings/items": "nav.settings.items", + "/settings/warehouse": "nav.settings.warehouse", + "/settings/qcCategory": "nav.settings.qcCategory", + "/settings/bomWeighting": "nav.settings.bomWeighting", + "/settings/importExcel": "nav.settings.importExcel", + "/settings/importBom": "nav.settings.importBom", "/settings/qcItem": "nav.breadcrumb.qcItem", "/settings/qcItemAll": "nav.breadcrumb.qcItemAll", "/settings/qrCodeHandle": "nav.breadcrumb.qrCodeHandle", diff --git a/src/components/CreatePrinter/CreatePrinter.tsx b/src/components/CreatePrinter/CreatePrinter.tsx index 07542ec..246dded 100644 --- a/src/components/CreatePrinter/CreatePrinter.tsx +++ b/src/components/CreatePrinter/CreatePrinter.tsx @@ -21,7 +21,7 @@ import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; const CreatePrinter: React.FC = () => { - const { t } = useTranslation("common"); + const { t } = useTranslation(["printer", "common"]); const router = useRouter(); const [isSubmitting, setIsSubmitting] = useState(false); const [descriptions, setDescriptions] = useState([]); @@ -146,7 +146,7 @@ const CreatePrinter: React.FC = () => { { = ({ rules, auths }) => { if (!regex_pw.test(pw)) { haveError = true; formProps.setError("password", { - message: + message: t( "A combination of uppercase letters, lowercase letters, numbers, and symbols is required.", + ), type: "required", }); } diff --git a/src/components/DoSearch/DoSearch.tsx b/src/components/DoSearch/DoSearch.tsx index 31beb22..66d4043 100644 --- a/src/components/DoSearch/DoSearch.tsx +++ b/src/components/DoSearch/DoSearch.tsx @@ -11,7 +11,7 @@ import { useRouter } from "next/navigation"; import React, { ForwardedRef, useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Criterion } from "../SearchBox"; -import { isEmpty, sortBy, uniqBy, upperFirst } from "lodash"; +import { isEmpty, sortBy, uniqBy } from "lodash"; import { arrayToDateString, arrayToDayjs } from "@/app/utils/formatUtil"; import SearchBox from "../SearchBox/SearchBox"; import { EditNote } from "@mui/icons-material"; @@ -285,7 +285,7 @@ const DoSearch: React.FC = ({ filterArgs, searchQuery, onDeliveryOrderSea headerName: t("Status"), flex: 1, renderCell: (params) => { - return t(upperFirst(params.row.status)); + return t(params.row.status); }, }, ], diff --git a/src/components/DoSearchWorkbench/DoSearchWorkbench.tsx b/src/components/DoSearchWorkbench/DoSearchWorkbench.tsx index 8f979b2..3f77cfc 100644 --- a/src/components/DoSearchWorkbench/DoSearchWorkbench.tsx +++ b/src/components/DoSearchWorkbench/DoSearchWorkbench.tsx @@ -11,7 +11,7 @@ import { useRouter } from "next/navigation"; import React, { ForwardedRef, useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Criterion } from "../SearchBox"; -import { isEmpty, sortBy, uniqBy, upperFirst } from "lodash"; +import { isEmpty, sortBy, uniqBy } from "lodash"; import { arrayToDateString, arrayToDayjs } from "@/app/utils/formatUtil"; import SearchBox from "../SearchBox/SearchBox"; import { EditNote } from "@mui/icons-material"; @@ -272,7 +272,7 @@ const DoSearchWorkbench: React.FC = ({ headerName: t("Status"), flex: 1, renderCell: (params) => { - return t(upperFirst(params.row.status)); + return t(params.row.status); }, }, ], diff --git a/src/components/EditPrinter/EditPrinter.tsx b/src/components/EditPrinter/EditPrinter.tsx index 31e0643..b22a8fb 100644 --- a/src/components/EditPrinter/EditPrinter.tsx +++ b/src/components/EditPrinter/EditPrinter.tsx @@ -25,7 +25,7 @@ type Props = { }; const EditPrinter: React.FC = ({ printer }) => { - const { t } = useTranslation("common"); + const { t } = useTranslation(["printer", "common"]); const router = useRouter(); const [isSubmitting, setIsSubmitting] = useState(false); const [formData, setFormData] = useState({ @@ -112,7 +112,7 @@ const EditPrinter: React.FC = ({ printer }) => { = ({ printer }) => { = ({ user, rules }) => { if (!regex_pw.test(pw)) { haveError = true; formProps.setError("password", { - message: + message: t( "A combination of uppercase letters, lowercase letters, numbers, and symbols is required.", + ), type: "required", }); } diff --git a/src/components/FinishedGoodSearch/AssignAndRelease.tsx b/src/components/FinishedGoodSearch/AssignAndRelease.tsx index c38ec1d..2fe86d5 100644 --- a/src/components/FinishedGoodSearch/AssignAndRelease.tsx +++ b/src/components/FinishedGoodSearch/AssignAndRelease.tsx @@ -28,7 +28,7 @@ import { } from "@/app/api/pickOrder/actions"; import { fetchNameList, NameList ,fetchNewNameList, NewNameList} from "@/app/api/user/actions"; import { FormProvider, useForm } from "react-hook-form"; -import { isEmpty, sortBy, uniqBy, upperFirst, groupBy } from "lodash"; +import { isEmpty, sortBy, uniqBy, groupBy } from "lodash"; import { OUTPUT_DATE_FORMAT, arrayToDayjs } from "@/app/utils/formatUtil"; import useUploadContext from "../UploadProvider/useUploadContext"; import dayjs from "dayjs"; @@ -237,7 +237,7 @@ console.log("First record targetDate formatted:", dayjs(res.records[0]?.targetDa uniqBy( originalItemData.map((item) => ({ value: item.status, - label: t(upperFirst(item.status)), + label: t(item.status), })), "value", ), @@ -511,7 +511,7 @@ console.log("First record targetDate formatted:", dayjs(res.records[0]?.targetDa {/* Pick Order Status - 只在第一个项目显示 */} - {index === 0 ? upperFirst(item.status) : null} + {index === 0 ? t(item.status) : null} )) diff --git a/src/components/FinishedGoodSearch/ConsolidatedPickOrders.tsx b/src/components/FinishedGoodSearch/ConsolidatedPickOrders.tsx index b2c2c29..ac6c6d0 100644 --- a/src/components/FinishedGoodSearch/ConsolidatedPickOrders.tsx +++ b/src/components/FinishedGoodSearch/ConsolidatedPickOrders.tsx @@ -134,6 +134,7 @@ const ConsolidatedPickOrders: React.FC = ({ filterArgs }) => { { name: "status", label: t("status"), + renderCell: (params: any) => t(params.status), }, ], [onDetailClick, t], diff --git a/src/components/FinishedGoodSearch/FinishedGood.tsx b/src/components/FinishedGoodSearch/FinishedGood.tsx index 5fb16f9..db62433 100644 --- a/src/components/FinishedGoodSearch/FinishedGood.tsx +++ b/src/components/FinishedGoodSearch/FinishedGood.tsx @@ -3,7 +3,7 @@ import SearchResults, { Column } from "../SearchResults/SearchResults"; import { PickOrderResult } from "@/app/api/pickOrder"; import { useTranslation } from "react-i18next"; import { useCallback, useEffect, useMemo, useState } from "react"; -import { isEmpty, upperCase, upperFirst } from "lodash"; +import { isEmpty, upperCase } from "lodash"; import { arrayToDateString, OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; import { consolidatePickOrder, @@ -127,7 +127,7 @@ const PickOrders: React.FC = ({ filteredPickOrders, filterArgs }) => { name: "status", label: t("Status"), renderCell: (params) => { - return upperFirst(params.status); + return t(params.status); }, }, ], diff --git a/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx b/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx index 0dabe83..b1e58e4 100644 --- a/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx +++ b/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx @@ -10,7 +10,6 @@ import { sortBy, uniqBy, upperCase, - upperFirst, } from "lodash"; import { arrayToDayjs, @@ -642,7 +641,7 @@ const handleAssignByLane = useCallback(async ( uniqBy( pickOrders.map((po) => ({ value: po.status, - label: t(upperFirst(po.status)), + label: t(po.status), })), "value", ), diff --git a/src/components/JoSave/InfoCard.tsx b/src/components/JoSave/InfoCard.tsx index 91866d3..1813904 100644 --- a/src/components/JoSave/InfoCard.tsx +++ b/src/components/JoSave/InfoCard.tsx @@ -1,7 +1,7 @@ import { JoDetail } from "@/app/api/jo"; import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; import { Box, Card, CardContent, Grid, Stack, TextField } from "@mui/material"; -import { upperFirst } from "lodash"; + import { useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { arrayToDateString } from "@/app/utils/formatUtil"; @@ -30,7 +30,7 @@ const InfoCard: React.FC = ({ label={t("Status")} fullWidth disabled={true} - value={`${t(upperFirst(watch("status")))}`} + value={`${t(watch("status"))}`} /> */} diff --git a/src/components/JoSearch/JoSearch.tsx b/src/components/JoSearch/JoSearch.tsx index 854f8c2..46638b1 100644 --- a/src/components/JoSearch/JoSearch.tsx +++ b/src/components/JoSearch/JoSearch.tsx @@ -6,7 +6,7 @@ import { Criterion } from "../SearchBox"; import SearchResults, { Column, defaultPagingController } from "../SearchResults/SearchResults"; import { EditNote } from "@mui/icons-material"; import { arrayToDateString, arrayToDateTimeString, integerFormatter, dayjsToDateString } from "@/app/utils/formatUtil"; -import { orderBy, uniqBy, upperFirst } from "lodash"; +import { orderBy, uniqBy } from "lodash"; import SearchBox from "../SearchBox/SearchBox"; import { useRouter } from "next/navigation"; import { FormProvider, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form"; @@ -478,7 +478,7 @@ const JoSearch: React.FC = ({ defaultInputs, bomCombo, printerCombo, jobT label: t("Status"), renderCell: (row) => { return - {t(upperFirst(row.status))} + {t(row.status)} } }, diff --git a/src/components/JoWorkbench/JoWorkbenchSearch.tsx b/src/components/JoWorkbench/JoWorkbenchSearch.tsx index 7f8c1eb..0da4da8 100644 --- a/src/components/JoWorkbench/JoWorkbenchSearch.tsx +++ b/src/components/JoWorkbench/JoWorkbenchSearch.tsx @@ -7,7 +7,7 @@ import { Criterion } from "../SearchBox"; import SearchResults, { Column, defaultPagingController } from "../SearchResults/SearchResults"; import { EditNote } from "@mui/icons-material"; import { arrayToDateString, arrayToDateTimeString, integerFormatter, dayjsToDateString } from "@/app/utils/formatUtil"; -import { orderBy, uniqBy, upperFirst } from "lodash"; +import { orderBy, uniqBy } from "lodash"; import SearchBox from "../SearchBox/SearchBox"; import { useRouter } from "next/navigation"; import { FormProvider, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form"; @@ -479,7 +479,7 @@ const JoWorkbenchSearch: React.FC = ({ defaultInputs, bomCombo, printerCo label: t("Status"), renderCell: (row) => { return - {t(upperFirst(row.status))} + {t(row.status)} } }, diff --git a/src/components/Jodetail/Jodetail.tsx b/src/components/Jodetail/Jodetail.tsx index 20704d3..059c40e 100644 --- a/src/components/Jodetail/Jodetail.tsx +++ b/src/components/Jodetail/Jodetail.tsx @@ -3,7 +3,7 @@ import SearchResults, { Column } from "../SearchResults/SearchResults"; import { PickOrderResult } from "@/app/api/pickOrder"; import { useTranslation } from "react-i18next"; import { useCallback, useEffect, useMemo, useState } from "react"; -import { isEmpty, upperCase, upperFirst } from "lodash"; +import { isEmpty, upperCase } from "lodash"; import { arrayToDateString, OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; import { consolidatePickOrder, @@ -127,7 +127,7 @@ const Jodetail: React.FC = ({ filteredPickOrders, filterArgs }) => { name: "status", label: t("Status"), renderCell: (params) => { - return upperFirst(params.status); + return t(params.status); }, }, ], diff --git a/src/components/Jodetail/JodetailSearch.tsx b/src/components/Jodetail/JodetailSearch.tsx index efec1a3..ad07fe6 100644 --- a/src/components/Jodetail/JodetailSearch.tsx +++ b/src/components/Jodetail/JodetailSearch.tsx @@ -10,7 +10,6 @@ import { sortBy, uniqBy, upperCase, - upperFirst, } from "lodash"; import { arrayToDayjs, @@ -351,7 +350,7 @@ const JodetailSearch: React.FC = ({ printerCombo }) => { uniqBy( pickOrders.map((po) => ({ value: po.status, - label: t(upperFirst(po.status)), + label: t(po.status), })), "value", ), diff --git a/src/components/PickOrderSearch/ConsolidatedPickOrders.tsx b/src/components/PickOrderSearch/ConsolidatedPickOrders.tsx index b2c2c29..ac6c6d0 100644 --- a/src/components/PickOrderSearch/ConsolidatedPickOrders.tsx +++ b/src/components/PickOrderSearch/ConsolidatedPickOrders.tsx @@ -134,6 +134,7 @@ const ConsolidatedPickOrders: React.FC = ({ filterArgs }) => { { name: "status", label: t("status"), + renderCell: (params: any) => t(params.status), }, ], [onDetailClick, t], diff --git a/src/components/PrinterSearch/PrinterSearch.tsx b/src/components/PrinterSearch/PrinterSearch.tsx index 3638e99..0878e98 100644 --- a/src/components/PrinterSearch/PrinterSearch.tsx +++ b/src/components/PrinterSearch/PrinterSearch.tsx @@ -20,7 +20,7 @@ type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; const PrinterSearch: React.FC = ({ printers }) => { - const { t } = useTranslation("common"); + const { t } = useTranslation(["printer", "common"]); const [filteredPrinters, setFilteredPrinters] = useState(printers); const [pagingController, setPagingController] = useState({ pageNum: 1, @@ -42,7 +42,7 @@ const PrinterSearch: React.FC = ({ printers }) => { type: "text", }, { - label: "IP", + label: t("IP"), paramName: "ip", type: "text", }, @@ -115,14 +115,14 @@ const PrinterSearch: React.FC = ({ printers }) => { }, { name: "ip", - label: "IP", + label: t("IP"), align: "left", headerAlign: "left", sx: { width: "15%", minWidth: "100px" }, }, { name: "port", - label: "Port", + label: t("Port"), align: "left", headerAlign: "left", sx: { width: "10%", minWidth: "80px" }, diff --git a/src/components/ProductionProcess/ProductionProcess.tsx b/src/components/ProductionProcess/ProductionProcess.tsx index f55610f..d30f132 100644 --- a/src/components/ProductionProcess/ProductionProcess.tsx +++ b/src/components/ProductionProcess/ProductionProcess.tsx @@ -45,7 +45,7 @@ const ProductionProcess: React.FC = ({ processes }) => { const [isQCModalOpen, setIsQCModalOpen] = React.useState(false); - const { t } = useTranslation(); + const { t } = useTranslation("productionProcess"); console.log("production process"); @@ -122,7 +122,7 @@ const ProductionProcess: React.FC = ({ processes }) => { {process.processName} diff --git a/src/components/QcCategorySave/QcCategoryDetails.tsx b/src/components/QcCategorySave/QcCategoryDetails.tsx index 15f77fa..956c8e0 100644 --- a/src/components/QcCategorySave/QcCategoryDetails.tsx +++ b/src/components/QcCategorySave/QcCategoryDetails.tsx @@ -28,7 +28,7 @@ const QcCategoryDetails = () => { label={t("Code")} fullWidth {...register("code", { - required: "Code required!", + required: t("Code required!"), maxLength: 30, })} /> @@ -38,7 +38,7 @@ const QcCategoryDetails = () => { label={t("Name")} fullWidth {...register("name", { - required: "Name required!", + required: t("Name required!"), maxLength: 30, })} /> diff --git a/src/components/QcItemAll/Tab0ItemQcCategoryMapping.tsx b/src/components/QcItemAll/Tab0ItemQcCategoryMapping.tsx index ae6451a..ea7afd4 100644 --- a/src/components/QcItemAll/Tab0ItemQcCategoryMapping.tsx +++ b/src/components/QcItemAll/Tab0ItemQcCategoryMapping.tsx @@ -46,6 +46,10 @@ import { submitDialog, successDialog, } from "../Swal/CustomAlerts"; +import { + formatQcTypeLabel, + translateQcItemAllBackendMessage, +} from "./qcItemAllMessages"; type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; @@ -181,17 +185,8 @@ const Tab0ItemQcCategoryMapping: React.FC = () => { } catch { // 解析失敗就維持原本的 message } - let displayMessage = message; - if (displayMessage.includes("already has type") && displayMessage.includes("linked to QcCategory")) { - const match = displayMessage.match(/type "([^"]+)" linked to QcCategory[:\s]+(.+?)(?:\.|One item)/); - const type = match?.[1] ?? ""; - const categoryName = match?.[2]?.trim() ?? ""; - displayMessage = t("Item already has type \"{{type}}\" in QcCategory \"{{category}}\". One item can only have each type in one QcCategory.", { - type, - category: categoryName, - }); - } - + const displayMessage = translateQcItemAllBackendMessage(message, t); + errorDialogWithContent(t("Submit Error"), displayMessage || t("Submit Error"), t); } }, t); @@ -218,17 +213,19 @@ const Tab0ItemQcCategoryMapping: React.FC = () => { ); const typeOptions = ["IQC", "IPQC", "EPQC"]; - function formatTypeDisplay(value: unknown): string { - if (value == null) return "null"; - if (typeof value === "string") return value; - if (typeof value === "object" && value !== null && "type" in value) { - const v = (value as { type?: unknown }).type; - if (typeof v === "string") return v; - if (v != null && typeof v === "object") return "null"; // 避免 [object Object] - return "null"; - } - return "null"; - } + const formatTypeDisplay = useCallback( + (value: unknown): string => { + if (value == null) return " "; + if (typeof value === "string") return formatQcTypeLabel(value, t); + if (typeof value === "object" && value !== null && "type" in value) { + const v = (value as { type?: unknown }).type; + if (typeof v === "string") return formatQcTypeLabel(v, t); + return " "; + } + return " "; + }, + [t], + ); const searchCriteria: Criterion[] = useMemo( () => [ { label: t("Code"), paramName: "code", type: "text" }, @@ -253,16 +250,7 @@ const Tab0ItemQcCategoryMapping: React.FC = () => { name: "type", label: t("Type"), sx: columnWidthSx("10%"), - renderCell: (row) => { - const t = row.type; - if (t == null) return " "; // 原来是 "null" - if (typeof t === "string") return t; - if (typeof t === "object" && t !== null && "type" in t) { - const v = (t as { type?: unknown }).type; - return typeof v === "string" ? v : " "; // 原来是 "null" - } - return " "; // 原来是 "null" - }, + renderCell: (row) => formatTypeDisplay(row.type), }, { name: "id", @@ -273,7 +261,7 @@ const Tab0ItemQcCategoryMapping: React.FC = () => { sx: columnWidthSx("10%"), }, ], - [t, handleViewMappings] + [t, handleViewMappings, formatTypeDisplay] ); if (loading) { @@ -333,7 +321,9 @@ const Tab0ItemQcCategoryMapping: React.FC = () => { SelectProps={{ native: true }} > {typeOptions.map((opt) => ( - + ))}