| @@ -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<Metadata> { | |||
| 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")} | |||
| </Typography> | |||
| </Stack> | |||
| <Suspense fallback={<div>Loading...</div>}> | |||
| <Suspense fallback={<div>{t("Loading...")}</div>}> | |||
| <I18nProvider namespaces={["qcItemAll","navigation","common","qcCategory","qcItem"]}> | |||
| <QcItemAllTabs | |||
| tab0Content={<Tab0ItemQcCategoryMapping />} | |||
| @@ -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", | |||
| @@ -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<string[]>([]); | |||
| @@ -146,7 +146,7 @@ const CreatePrinter: React.FC = () => { | |||
| <Grid item xs={12} md={6}> | |||
| <TextField | |||
| fullWidth | |||
| label="IP" | |||
| label={t("IP")} | |||
| value={formData.ip} | |||
| onChange={handleChange("ip")} | |||
| variant="outlined" | |||
| @@ -155,7 +155,7 @@ const CreatePrinter: React.FC = () => { | |||
| <Grid item xs={12} md={6}> | |||
| <TextField | |||
| fullWidth | |||
| label="Port" | |||
| label={t("Port")} | |||
| type="number" | |||
| value={formData.port ?? ""} | |||
| onChange={handleChange("port")} | |||
| @@ -139,8 +139,9 @@ const CreateUser: React.FC<Props> = ({ 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", | |||
| }); | |||
| } | |||
| @@ -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<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea | |||
| headerName: t("Status"), | |||
| flex: 1, | |||
| renderCell: (params) => { | |||
| return t(upperFirst(params.row.status)); | |||
| return t(params.row.status); | |||
| }, | |||
| }, | |||
| ], | |||
| @@ -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<Props> = ({ | |||
| headerName: t("Status"), | |||
| flex: 1, | |||
| renderCell: (params) => { | |||
| return t(upperFirst(params.row.status)); | |||
| return t(params.row.status); | |||
| }, | |||
| }, | |||
| ], | |||
| @@ -25,7 +25,7 @@ type Props = { | |||
| }; | |||
| const EditPrinter: React.FC<Props> = ({ printer }) => { | |||
| const { t } = useTranslation("common"); | |||
| const { t } = useTranslation(["printer", "common"]); | |||
| const router = useRouter(); | |||
| const [isSubmitting, setIsSubmitting] = useState(false); | |||
| const [formData, setFormData] = useState<PrinterInputs>({ | |||
| @@ -112,7 +112,7 @@ const EditPrinter: React.FC<Props> = ({ printer }) => { | |||
| <Grid item xs={12} md={6}> | |||
| <TextField | |||
| fullWidth | |||
| label="IP" | |||
| label={t("IP")} | |||
| value={formData.ip} | |||
| onChange={handleChange("ip")} | |||
| variant="outlined" | |||
| @@ -121,7 +121,7 @@ const EditPrinter: React.FC<Props> = ({ printer }) => { | |||
| <Grid item xs={12} md={6}> | |||
| <TextField | |||
| fullWidth | |||
| label="Port" | |||
| label={t("Port")} | |||
| type="number" | |||
| value={formData.port || ""} | |||
| onChange={handleChange("port")} | |||
| @@ -95,8 +95,9 @@ const EditUser: React.FC<Props> = ({ 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", | |||
| }); | |||
| } | |||
| @@ -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 - 只在第一个项目显示 */} | |||
| <TableCell> | |||
| {index === 0 ? upperFirst(item.status) : null} | |||
| {index === 0 ? t(item.status) : null} | |||
| </TableCell> | |||
| </TableRow> | |||
| )) | |||
| @@ -134,6 +134,7 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | |||
| { | |||
| name: "status", | |||
| label: t("status"), | |||
| renderCell: (params: any) => t(params.status), | |||
| }, | |||
| ], | |||
| [onDetailClick, t], | |||
| @@ -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<Props> = ({ filteredPickOrders, filterArgs }) => { | |||
| name: "status", | |||
| label: t("Status"), | |||
| renderCell: (params) => { | |||
| return upperFirst(params.status); | |||
| return t(params.status); | |||
| }, | |||
| }, | |||
| ], | |||
| @@ -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", | |||
| ), | |||
| @@ -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<Props> = ({ | |||
| label={t("Status")} | |||
| fullWidth | |||
| disabled={true} | |||
| value={`${t(upperFirst(watch("status")))}`} | |||
| value={`${t(watch("status"))}`} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={6}/>*/} | |||
| @@ -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<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT | |||
| label: t("Status"), | |||
| renderCell: (row) => { | |||
| return <span style={{color: row.stockInLineStatus == "escalated" ? "red" : "inherit"}}> | |||
| {t(upperFirst(row.status))} | |||
| {t(row.status)} | |||
| </span> | |||
| } | |||
| }, | |||
| @@ -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<Props> = ({ defaultInputs, bomCombo, printerCo | |||
| label: t("Status"), | |||
| renderCell: (row) => { | |||
| return <span style={{color: row.stockInLineStatus == "escalated" ? "red" : "inherit"}}> | |||
| {t(upperFirst(row.status))} | |||
| {t(row.status)} | |||
| </span> | |||
| } | |||
| }, | |||
| @@ -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<Props> = ({ filteredPickOrders, filterArgs }) => { | |||
| name: "status", | |||
| label: t("Status"), | |||
| renderCell: (params) => { | |||
| return upperFirst(params.status); | |||
| return t(params.status); | |||
| }, | |||
| }, | |||
| ], | |||
| @@ -10,7 +10,6 @@ import { | |||
| sortBy, | |||
| uniqBy, | |||
| upperCase, | |||
| upperFirst, | |||
| } from "lodash"; | |||
| import { | |||
| arrayToDayjs, | |||
| @@ -351,7 +350,7 @@ const JodetailSearch: React.FC<Props> = ({ printerCombo }) => { | |||
| uniqBy( | |||
| pickOrders.map((po) => ({ | |||
| value: po.status, | |||
| label: t(upperFirst(po.status)), | |||
| label: t(po.status), | |||
| })), | |||
| "value", | |||
| ), | |||
| @@ -134,6 +134,7 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | |||
| { | |||
| name: "status", | |||
| label: t("status"), | |||
| renderCell: (params: any) => t(params.status), | |||
| }, | |||
| ], | |||
| [onDetailClick, t], | |||
| @@ -20,7 +20,7 @@ type SearchQuery = Partial<Omit<PrinterResult, "id">>; | |||
| type SearchParamNames = keyof SearchQuery; | |||
| const PrinterSearch: React.FC<Props> = ({ 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<Props> = ({ printers }) => { | |||
| type: "text", | |||
| }, | |||
| { | |||
| label: "IP", | |||
| label: t("IP"), | |||
| paramName: "ip", | |||
| type: "text", | |||
| }, | |||
| @@ -115,14 +115,14 @@ const PrinterSearch: React.FC<Props> = ({ 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" }, | |||
| @@ -45,7 +45,7 @@ const ProductionProcess: React.FC<ProductionProcessProps> = ({ processes }) => { | |||
| const [isQCModalOpen, setIsQCModalOpen] = React.useState<boolean>(false); | |||
| const { t } = useTranslation(); | |||
| const { t } = useTranslation("productionProcess"); | |||
| console.log("production process"); | |||
| @@ -122,7 +122,7 @@ const ProductionProcess: React.FC<ProductionProcessProps> = ({ processes }) => { | |||
| <TableCell>{process.processName}</TableCell> | |||
| <TableCell> | |||
| <Chip | |||
| label={process.status} | |||
| label={t(process.status)} | |||
| color={getStatusColor(process.status)} | |||
| size="small" | |||
| /> | |||
| @@ -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, | |||
| })} | |||
| /> | |||
| @@ -46,6 +46,10 @@ import { | |||
| submitDialog, | |||
| successDialog, | |||
| } from "../Swal/CustomAlerts"; | |||
| import { | |||
| formatQcTypeLabel, | |||
| translateQcItemAllBackendMessage, | |||
| } from "./qcItemAllMessages"; | |||
| type SearchQuery = Partial<Omit<QcCategoryResult, "id">>; | |||
| 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<SearchParamNames>[] = 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) => ( | |||
| <option key={opt} value={opt}>{opt}</option> | |||
| <option key={opt} value={opt}> | |||
| {formatQcTypeLabel(opt, t)} | |||
| </option> | |||
| ))} | |||
| </TextField> | |||
| <Button | |||
| @@ -363,7 +353,12 @@ const Tab0ItemQcCategoryMapping: React.FC = () => { | |||
| const mappingData = await getItemQcCategoryMappings(selectedCategory.id); | |||
| setMappings(mappingData); | |||
| } catch (e) { | |||
| errorDialogWithContent(t("Submit Error"), String(e), t); | |||
| const raw = e instanceof Error ? e.message : String(e); | |||
| errorDialogWithContent( | |||
| t("Submit Error"), | |||
| translateQcItemAllBackendMessage(raw, t), | |||
| t, | |||
| ); | |||
| } finally { | |||
| setSavingCategoryType(false); | |||
| } | |||
| @@ -459,7 +454,7 @@ const Tab0ItemQcCategoryMapping: React.FC = () => { | |||
| > | |||
| {typeOptions.map((type) => ( | |||
| <option key={type} value={type}> | |||
| {type} | |||
| {formatQcTypeLabel(type, t)} | |||
| </option> | |||
| ))} | |||
| </TextField> | |||
| @@ -42,6 +42,10 @@ import { | |||
| submitDialog, | |||
| successDialog, | |||
| } from "../Swal/CustomAlerts"; | |||
| import { | |||
| formatQcTypeLabel, | |||
| translateQcItemAllBackendMessage, | |||
| } from "./qcItemAllMessages"; | |||
| type SearchQuery = Partial<Omit<QcCategoryResult, "id">>; | |||
| type SearchParamNames = keyof SearchQuery; | |||
| @@ -122,7 +126,12 @@ const Tab1QcCategoryQcItemMapping: React.FC = () => { | |||
| await successDialog(t("Submit Success"), t); | |||
| // Keep the view dialog open to show updated data | |||
| } catch (error) { | |||
| errorDialogWithContent(t("Submit Error"), String(error), t); | |||
| const raw = error instanceof Error ? error.message : String(error); | |||
| errorDialogWithContent( | |||
| t("Submit Error"), | |||
| translateQcItemAllBackendMessage(raw, t), | |||
| t, | |||
| ); | |||
| } | |||
| }, t); | |||
| }, [selectedCategory, selectedQcItem, order, t]); | |||
| @@ -140,7 +149,12 @@ const Tab1QcCategoryQcItemMapping: React.FC = () => { | |||
| setMappings(mappingData); | |||
| // No need to reload categories list - it doesn't change | |||
| } catch (error) { | |||
| errorDialogWithContent(t("Delete Error"), String(error), t); | |||
| const raw = error instanceof Error ? error.message : String(error); | |||
| errorDialogWithContent( | |||
| t("Delete Error"), | |||
| translateQcItemAllBackendMessage(raw, t), | |||
| t, | |||
| ); | |||
| } | |||
| }, t); | |||
| }, | |||
| @@ -167,7 +181,13 @@ const Tab1QcCategoryQcItemMapping: React.FC = () => { | |||
| () => [ | |||
| { name: "code", label: t("Qc Category Code"), sx: columnWidthSx("20%") }, | |||
| { name: "name", label: t("Qc Category Name"), sx: columnWidthSx("40%") }, | |||
| { name: "type", label: t("Type"), sx: columnWidthSx("10%") }, | |||
| { | |||
| name: "type", | |||
| label: t("Type"), | |||
| sx: columnWidthSx("10%"), | |||
| renderCell: (row) => | |||
| row.type ? formatQcTypeLabel(String(row.type), t) : " ", | |||
| }, | |||
| { | |||
| name: "id", | |||
| label: t("Actions"), | |||
| @@ -180,6 +200,21 @@ const Tab1QcCategoryQcItemMapping: React.FC = () => { | |||
| [t, handleViewMappings] | |||
| ); | |||
| if (loading) { | |||
| return ( | |||
| <Box | |||
| sx={{ | |||
| display: "flex", | |||
| justifyContent: "center", | |||
| alignItems: "center", | |||
| minHeight: "200px", | |||
| }} | |||
| > | |||
| <CircularProgress /> | |||
| </Box> | |||
| ); | |||
| } | |||
| return ( | |||
| <Box> | |||
| <SearchBox | |||
| @@ -24,6 +24,10 @@ import { Add } from "@mui/icons-material"; | |||
| import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Stack } from "@mui/material"; | |||
| import QcCategoryDetails from "../QcCategorySave/QcCategoryDetails"; | |||
| import { FormProvider, useForm } from "react-hook-form"; | |||
| import { | |||
| formatQcTypeLabel, | |||
| translateQcItemAllBackendMessage, | |||
| } from "./qcItemAllMessages"; | |||
| type SearchQuery = Partial<Omit<QcCategoryResult, "id">>; | |||
| type SearchParamNames = keyof SearchQuery; | |||
| @@ -95,9 +99,12 @@ const Tab2QcCategoryManagement: React.FC = () => { | |||
| for (const [key, value] of Object.entries(response.errors)) { | |||
| formProps.setError(key as keyof SaveQcCategoryInputs, { | |||
| type: "custom", | |||
| message: value, | |||
| message: translateQcItemAllBackendMessage(String(value), t), | |||
| }); | |||
| errorContents = errorContents + t(value) + "<br>"; | |||
| errorContents = | |||
| errorContents + | |||
| translateQcItemAllBackendMessage(String(value), t) + | |||
| "<br>"; | |||
| } | |||
| errorDialogWithContent(t("Submit Error"), errorContents, t); | |||
| } else { | |||
| @@ -106,7 +113,12 @@ const Tab2QcCategoryManagement: React.FC = () => { | |||
| await loadCategories(); | |||
| } | |||
| } catch (error) { | |||
| errorDialogWithContent(t("Submit Error"), String(error), t); | |||
| const raw = error instanceof Error ? error.message : String(error); | |||
| errorDialogWithContent( | |||
| t("Submit Error"), | |||
| translateQcItemAllBackendMessage(raw, t), | |||
| t, | |||
| ); | |||
| } | |||
| }, t); | |||
| }, [formProps, t]); | |||
| @@ -118,8 +130,8 @@ const Tab2QcCategoryManagement: React.FC = () => { | |||
| if (!canDelete) { | |||
| errorDialogWithContent( | |||
| t("Cannot Delete"), | |||
| t("Cannot delete QcCategory. It has {itemCount} item(s) and {qcItemCount} qc item(s) linked to it.").replace("{itemCount}", "some").replace("{qcItemCount}", "some"), | |||
| t | |||
| t("Cannot delete QcCategory linked"), | |||
| t, | |||
| ); | |||
| return; | |||
| } | |||
| @@ -130,15 +142,23 @@ const Tab2QcCategoryManagement: React.FC = () => { | |||
| if (!response.success || !response.canDelete) { | |||
| errorDialogWithContent( | |||
| t("Delete Error"), | |||
| response.message || t("Cannot Delete"), | |||
| t | |||
| translateQcItemAllBackendMessage( | |||
| response.message || t("Cannot Delete"), | |||
| t, | |||
| ), | |||
| t, | |||
| ); | |||
| } else { | |||
| await successDialog(t("Delete Success"), t); | |||
| await loadCategories(); | |||
| } | |||
| } catch (error) { | |||
| errorDialogWithContent(t("Delete Error"), String(error), t); | |||
| const raw = error instanceof Error ? error.message : String(error); | |||
| errorDialogWithContent( | |||
| t("Delete Error"), | |||
| translateQcItemAllBackendMessage(raw, t), | |||
| t, | |||
| ); | |||
| } | |||
| }, t); | |||
| }, [t]); | |||
| @@ -158,7 +178,13 @@ const Tab2QcCategoryManagement: React.FC = () => { | |||
| }, | |||
| { name: "code", label: t("Code"), sx: columnWidthSx("15%") }, | |||
| { name: "name", label: t("Name"), sx: columnWidthSx("30%") }, | |||
| { name: "type", label: t("Type"), sx: columnWidthSx("10%") }, | |||
| { | |||
| name: "type", | |||
| label: t("Type"), | |||
| sx: columnWidthSx("10%"), | |||
| renderCell: (row) => | |||
| row.type ? formatQcTypeLabel(String(row.type), t) : " ", | |||
| }, | |||
| { | |||
| name: "id", | |||
| label: t("Delete"), | |||
| @@ -24,6 +24,7 @@ import { Add } from "@mui/icons-material"; | |||
| import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Stack } from "@mui/material"; | |||
| import QcItemDetails from "../QcItemSave/QcItemDetails"; | |||
| import { FormProvider, useForm } from "react-hook-form"; | |||
| import { translateQcItemAllBackendMessage } from "./qcItemAllMessages"; | |||
| type SearchQuery = Partial<Omit<QcItemResult, "id">>; | |||
| type SearchParamNames = keyof SearchQuery; | |||
| @@ -95,9 +96,12 @@ const Tab3QcItemManagement: React.FC = () => { | |||
| for (const [key, value] of Object.entries(response.errors)) { | |||
| formProps.setError(key as keyof SaveQcItemInputs, { | |||
| type: "custom", | |||
| message: value, | |||
| message: translateQcItemAllBackendMessage(String(value), t), | |||
| }); | |||
| errorContents = errorContents + t(value) + "<br>"; | |||
| errorContents = | |||
| errorContents + | |||
| translateQcItemAllBackendMessage(String(value), t) + | |||
| "<br>"; | |||
| } | |||
| errorDialogWithContent(t("Submit Error"), errorContents, t); | |||
| } else { | |||
| @@ -106,7 +110,12 @@ const Tab3QcItemManagement: React.FC = () => { | |||
| await loadItems(); | |||
| } | |||
| } catch (error) { | |||
| errorDialogWithContent(t("Submit Error"), String(error), t); | |||
| const raw = error instanceof Error ? error.message : String(error); | |||
| errorDialogWithContent( | |||
| t("Submit Error"), | |||
| translateQcItemAllBackendMessage(raw, t), | |||
| t, | |||
| ); | |||
| } | |||
| }, t); | |||
| }, [formProps, t]); | |||
| @@ -130,15 +139,23 @@ const Tab3QcItemManagement: React.FC = () => { | |||
| if (!response.success || !response.canDelete) { | |||
| errorDialogWithContent( | |||
| t("Delete Error"), | |||
| response.message || t("Cannot Delete"), | |||
| t | |||
| translateQcItemAllBackendMessage( | |||
| response.message || t("Cannot Delete"), | |||
| t, | |||
| ), | |||
| t, | |||
| ); | |||
| } else { | |||
| await successDialog(t("Delete Success"), t); | |||
| await loadItems(); | |||
| } | |||
| } catch (error) { | |||
| errorDialogWithContent(t("Delete Error"), String(error), t); | |||
| const raw = error instanceof Error ? error.message : String(error); | |||
| errorDialogWithContent( | |||
| t("Delete Error"), | |||
| translateQcItemAllBackendMessage(raw, t), | |||
| t, | |||
| ); | |||
| } | |||
| }, t); | |||
| }, [t]); | |||
| @@ -0,0 +1,72 @@ | |||
| import { TFunction } from "i18next"; | |||
| export function translateQcItemAllBackendMessage( | |||
| message: string, | |||
| t: TFunction, | |||
| ): string { | |||
| const categoryDeleteMatch = message.match( | |||
| /Cannot delete QcCategory\. It has (\d+) item\(s\) and (\d+) qc item\(s\) linked to it\./, | |||
| ); | |||
| if (categoryDeleteMatch) { | |||
| return t( | |||
| "Cannot delete QcCategory. It has {itemCount} item(s) and {qcItemCount} qc item(s) linked to it.", | |||
| ) | |||
| .replace("{itemCount}", categoryDeleteMatch[1]) | |||
| .replace("{qcItemCount}", categoryDeleteMatch[2]); | |||
| } | |||
| if ( | |||
| message.includes("Cannot delete QcItem") && | |||
| message.includes("QcCategories") | |||
| ) { | |||
| return t( | |||
| "Cannot delete QcItem. It is linked to one or more QcCategories.", | |||
| ); | |||
| } | |||
| if ( | |||
| message.includes("already has type") && | |||
| message.includes("linked to QcCategory") | |||
| ) { | |||
| const match = message.match( | |||
| /type "([^"]+)" linked to QcCategory[:\s]+(.+?)(?:\.|One item)/, | |||
| ); | |||
| const type = match?.[1] ?? ""; | |||
| const categoryName = match?.[2]?.trim() ?? ""; | |||
| return t( | |||
| 'Item already has type "{{type}}" in QcCategory "{{category}}". One item can only have each type in one QcCategory.', | |||
| { type, category: categoryName }, | |||
| ); | |||
| } | |||
| const zhCategoryTypeMatch = message.match( | |||
| /物料 (.+?) 已經以 (.+?) 映射在模板 (.+?),不能再把模板 (.+?) 改為 (.+)/, | |||
| ); | |||
| if (zhCategoryTypeMatch) { | |||
| return t( | |||
| "Item {{itemCode}} is already mapped as {{type}} in category {{conflictCategory}}. Cannot set category {{currentCategory}} to {{newType}}.", | |||
| { | |||
| itemCode: zhCategoryTypeMatch[1], | |||
| type: zhCategoryTypeMatch[2], | |||
| conflictCategory: zhCategoryTypeMatch[3], | |||
| currentCategory: zhCategoryTypeMatch[4], | |||
| newType: zhCategoryTypeMatch[5], | |||
| }, | |||
| ); | |||
| } | |||
| const staticKeys = ["Code cannot be empty", "Name cannot be empty"]; | |||
| if (staticKeys.includes(message)) { | |||
| const translated = t(message); | |||
| if (translated !== message) { | |||
| return translated; | |||
| } | |||
| } | |||
| return message; | |||
| } | |||
| export function formatQcTypeLabel(type: string, t: TFunction): string { | |||
| const translated = t(`Type.${type}`, { defaultValue: type }); | |||
| return translated; | |||
| } | |||
| @@ -28,7 +28,7 @@ const QcItemDetails = () => { | |||
| label={t("Code")} | |||
| fullWidth | |||
| {...register("code", { | |||
| required: "Code required!", | |||
| required: t("Code required!"), | |||
| maxLength: 30, | |||
| })} | |||
| /> | |||
| @@ -38,7 +38,7 @@ const QcItemDetails = () => { | |||
| label={t("Name")} | |||
| fullWidth | |||
| {...register("name", { | |||
| required: "Name required!", | |||
| required: t("Name required!"), | |||
| maxLength: 30, | |||
| })} | |||
| /> | |||
| @@ -819,7 +819,7 @@ const ScheduleTaskHistoryModal: React.FC<Props> = ({ | |||
| </Typography> | |||
| <Chip | |||
| size="small" | |||
| label={line.lineStatus} | |||
| label={t(line.lineStatus)} | |||
| color={lineChipColor} | |||
| sx={{ height: 18, fontSize: "0.65rem" }} | |||
| /> | |||
| @@ -97,10 +97,10 @@ export default function TabStockTakeSectionMapping() { | |||
| const criteria: Criterion<SearchKey>[] = useMemo( | |||
| () => [ | |||
| { type: "text", label: "Stock Take Section", paramName: "stockTakeSection", placeholder: "" }, | |||
| { type: "text", label: "Stock Take Section Description", paramName: "stockTakeSectionDescription", placeholder: "" }, | |||
| { type: "text", label: t("Stock Take Section"), paramName: "stockTakeSection", placeholder: "" }, | |||
| { type: "text", label: t("Stock Take Section Description"), paramName: "stockTakeSectionDescription", placeholder: "" }, | |||
| ], | |||
| [] | |||
| [t] | |||
| ); | |||
| const handleSearch = useCallback((inputs: Record<SearchKey | `${SearchKey}To`, string>) => { | |||
| @@ -81,7 +81,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| { label: "入倉日期:由 Last In Date Start", name: "lastInDateStart", type: "date", required: false }, | |||
| { label: "入倉日期:至 Last In Date End", name: "lastInDateEnd", type: "date", required: false }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| ] | |||
| }, | |||
| { | |||
| @@ -92,7 +92,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| { label: "出貨日期:由 Last Out Date Start", name: "lastOutDateStart", type: "date", required: false }, | |||
| { label: "出貨日期:至 Last Out Date End", name: "lastOutDateEnd", type: "date", required: false }, | |||
| { label: "年份 Year", name: "year", type: "text", required: false, placeholder: "e.g. 2026" }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| ] | |||
| }, | |||
| /* | |||
| @@ -102,7 +102,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| fields: [ | |||
| { label: "盤點日期:由 Stock Take Date Start", name: "stockTakeDateStart", type: "date", required: false }, | |||
| { label: "盤點日期:至 Stock Take Date End", name: "stockTakeDateEnd", type: "date", required: false }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| ] | |||
| }, | |||
| */ | |||
| @@ -121,7 +121,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| dynamicOptionsEndpoint: `${NEXT_PUBLIC_API_URL}/report/stock-take-rounds`, | |||
| options: [] | |||
| }, | |||
| { label: "物料編號", name: "itemCode", type: "text", required: false}, | |||
| { label: "貨品編號", name: "itemCode", type: "text", required: false}, | |||
| { | |||
| label: "倉庫樓層", | |||
| name: "store_id", | |||
| @@ -154,7 +154,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| fields: [ | |||
| { label: "庫存日期:由 Last In Date Start", name: "lastInDateStart", type: "date", required: false }, | |||
| { label: "庫存日期:至 Last In Date End", name: "lastInDateEnd", type: "date", required: false }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| ] | |||
| }, | |||
| /* | |||
| @@ -172,7 +172,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| dynamicOptionsEndpoint: `${NEXT_PUBLIC_API_URL}/report/stock-take-rounds`, | |||
| options: [] | |||
| }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| ] | |||
| }, | |||
| */ | |||
| @@ -183,7 +183,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| fields: [ | |||
| { label: "庫存日期: Stock Date", name: "stockDate", type: "date", required: true }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| ] | |||
| }, | |||
| @@ -195,7 +195,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| fields: [ | |||
| { label: "收貨日期:由 Receipt Date Start", name: "receiptDateStart", type: "date", required: false }, | |||
| { label: "收貨日期:至 Receipt Date End", name: "receiptDateEnd", type: "date", required: false }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false }, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false }, | |||
| ], | |||
| }, | |||
| @@ -205,7 +205,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| fields: [ | |||
| { label: "出貨日期:由 Last Out Date Start", name: "lastOutDateStart", type: "date", required: false }, | |||
| { label: "出貨日期:至 Last Out Date End", name: "lastOutDateEnd", type: "date", required: false }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { label: "提料人 Handler", name: "handler", type: "select", required: false, | |||
| multiple: true, | |||
| dynamicOptions: true, | |||
| @@ -221,16 +221,16 @@ export const REPORTS: ReportDefinition[] = [ | |||
| { label: "QC 不合格日期:由 Last In Date Start", name: "lastInDateStart", type: "date", required: false }, | |||
| { label: "QC 不合格日期:至 Last In Date End", name: "lastInDateEnd", type: "date", required: false }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| ] | |||
| }, | |||
| { id: "rep-013", | |||
| title: "物料出倉追蹤報告", | |||
| title: "貨品出倉追蹤報告", | |||
| apiEndpoint: `${NEXT_PUBLIC_API_URL}/report/print-material-stock-out-traceability`, | |||
| fields: [ | |||
| { label: "庫存日期:由 Last In Date Start", name: "lastInDateStart", type: "date", required: false }, | |||
| { label: "庫存日期:至 Last In Date End", name: "lastInDateEnd", type: "date", required: false }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { label: "提料人 Handler", name: "handler", type: "select", required: false, | |||
| multiple: true, | |||
| dynamicOptions: true, | |||
| @@ -256,7 +256,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| { label: "FG", value: "FG" }, | |||
| { label: "CMB", value: "CMB" } | |||
| ] }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "select", required: false, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "select", required: false, | |||
| multiple: true, | |||
| allowInput: true, | |||
| dynamicOptions: true, | |||
| @@ -282,7 +282,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| { label: "WIP", value: "WIP" }, | |||
| { label: "FG", value: "FG" }, | |||
| ] }, | |||
| { label: "物料編號 Item Code", name: "itemCode", type: "select", required: false, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "select", required: false, | |||
| multiple: true, | |||
| allowInput: true, | |||
| dynamicOptions: true, | |||
| @@ -23,6 +23,7 @@ | |||
| "Day Before Yesterday": "前天", | |||
| "Delete": "刪除", | |||
| "Delete Failed": "刪除失敗", | |||
| "Do you want to delete?": "Do you want to delete?", | |||
| "Density": "濃淡", | |||
| "Depth": "顔色深淺度 深1淺5", | |||
| "Description": "描述", | |||
| @@ -42,6 +43,7 @@ | |||
| "Grade {{grade}}": "等級 {{grade}}", | |||
| "Invoice": "發票", | |||
| "Invoice Date": "發票日期", | |||
| "IP": "IP", | |||
| "Item Code": "Item Code", | |||
| "Item Name": "Item Name", | |||
| "Loading": "載入中...", | |||
| @@ -51,6 +53,7 @@ | |||
| "MAT": "材料", | |||
| "MI": "雜項", | |||
| "Material Name": "材料清單", | |||
| "Name": "Name", | |||
| "Min": "最小值", | |||
| "NM": "雜項及非消耗品", | |||
| "No": "否", | |||
| @@ -59,6 +62,7 @@ | |||
| "No options": "沒有選項", | |||
| "Order": "順序", | |||
| "Pending": "Pending", | |||
| "Port": "Port", | |||
| "Please Select BOM": "請選擇 BOM", | |||
| "Please try again later.": "請稍後重試。", | |||
| "Project Code": "專案代碼", | |||
| @@ -88,6 +92,7 @@ | |||
| "Supporting Document": "證明文件", | |||
| "Task": "任務", | |||
| "Time Sequence": "時段", | |||
| "Type": "Type", | |||
| "Today": "今天", | |||
| "Total weighting must equal 1": "權重總和必須等於 1", | |||
| "Unauthorized: Please log in again": "未經授權:請重新登入", | |||
| @@ -49,6 +49,11 @@ | |||
| "Order Date From": "Order Date From", | |||
| "Order Date To": "Order Date To", | |||
| "Pending": "Pending", | |||
| "pending": "Pending", | |||
| "picking": "Picking", | |||
| "receiving": "Receiving", | |||
| "released": "Released", | |||
| "completed": "Completed", | |||
| "Pick Order Assignment": "Pick Order Assignment", | |||
| "Please wait": "Please wait", | |||
| "Price": "Price", | |||
| @@ -17,8 +17,10 @@ | |||
| "Order": "Order", | |||
| "Code": "Code", | |||
| "Name": "Name", | |||
| "Shipping Warehouse": "Shipping Warehouse", | |||
| "Receiving Warehouse": "Receiving Warehouse", | |||
| "Shipping Warehouse": "Default Stock Out Location", | |||
| "Receiving Warehouse": "Default Stock In Location", | |||
| "Stock In Location": "Stock In Location", | |||
| "Stock Out Location": "Stock Out Location", | |||
| "Actions": "Actions", | |||
| "Loading": "Loading...", | |||
| "No data available": "No data available", | |||
| @@ -34,5 +36,6 @@ | |||
| "Confirm": "Confirm", | |||
| "Move to order": "Move to order", | |||
| "Target order": "Target order", | |||
| "This will move the item to exact order": "This will move the item to the specified order and reorder the rest" | |||
| "This will move the item to exact order": "This will move the item to the specified order and reorder the rest", | |||
| "Click to set": "Click to set" | |||
| } | |||
| @@ -1,4 +1,6 @@ | |||
| { | |||
| "Code": "Code", | |||
| "Name": "Name", | |||
| "Create Material": "Create Material", | |||
| "Update Equipment Maintenance and Repair": "Update Equipment Maintenance and Repair", | |||
| "Correct BOM List (Can Import)": "Correct BOM List (Can Import)", | |||
| @@ -8,5 +10,39 @@ | |||
| "Is Drink": "Is Drink", | |||
| "Drink": "Drink", | |||
| "Powder_Mixture": "Powder Mixture", | |||
| "Base Score": "Base Score" | |||
| "Base Score": "Base Score", | |||
| "Basic Info": "Basic Info", | |||
| "Edit": "Edit", | |||
| "Save": "Save", | |||
| "Cancel": "Cancel", | |||
| "Loading...": "Loading...", | |||
| "Saving...": "Saving...", | |||
| "Output Quantity": "Output Quantity", | |||
| "Output Quantity UOM": "Output Quantity UOM", | |||
| "Type": "Type", | |||
| "Allergic Substances": "Allergic Substances", | |||
| "Depth": "Depth", | |||
| "Float": "Float", | |||
| "Density": "Density", | |||
| "Time Sequence": "Time Sequence", | |||
| "Complexity": "Complexity", | |||
| "Scrap Rate": "Scrap Rate", | |||
| "Item Code": "Item Code", | |||
| "Item Name": "Item Name", | |||
| "Base Qty": "Base Qty", | |||
| "Base UOM": "Base UOM", | |||
| "Stock Qty": "Stock Qty", | |||
| "Stock UOM": "Stock UOM", | |||
| "Sales Qty": "Sales Qty", | |||
| "Sales UOM": "Sales UOM", | |||
| "Process & Equipment": "Process & Equipment", | |||
| "Process Code": "Process Code", | |||
| "Process Description": "Process Description", | |||
| "Process Name": "Process Name", | |||
| "Duration (Minutes)": "Duration (Minutes)", | |||
| "Prep Time (Minutes)": "Prep Time (Minutes)", | |||
| "Post Prod Time (Minutes)": "Post Prod Time (Minutes)", | |||
| "Add": "Add", | |||
| "Sequence": "Sequence", | |||
| "Actions": "Actions" | |||
| } | |||
| @@ -606,6 +606,10 @@ | |||
| "packaging": "packaging", | |||
| "paused": "paused", | |||
| "pending": "pending", | |||
| "pendingQC": "Pending QC", | |||
| "planning": "Planning", | |||
| "processing": "Processing", | |||
| "scanned": "Scanned", | |||
| "printQty": "printQty", | |||
| "process epqc": "process epqc", | |||
| "process stockIn": "process stockIn", | |||
| @@ -1,3 +1,49 @@ | |||
| { | |||
| "title": "M18 Sync" | |||
| "title": "M18 Sync", | |||
| "pageTitle": "M18 Sync (by code)", | |||
| "pageSubtitle": "ADMIN only. Sync Purchase Order, Delivery Order, or product/material from M18 using document or item code.", | |||
| "tabPo": "1. Purchase Order", | |||
| "tabDo": "2. Delivery Order", | |||
| "tabDoExtra": "3. Delivery Order (Extra)", | |||
| "tabProduct": "4. Product", | |||
| "sectionPo": "M18 Purchase Order — sync by code", | |||
| "sectionDo": "M18 Delivery Order — sync by code", | |||
| "sectionDoExtra": "M18 Delivery Order — Extra (isExtra)", | |||
| "sectionProduct": "M18 Product / Material — sync by code", | |||
| "labelPoCode": "PO Code", | |||
| "labelDoCode": "DO / Shop PO Code", | |||
| "labelDoExtraCode": "DO / Shop PO Code (Extra)", | |||
| "labelProductCode": "Item / Product Code", | |||
| "labelSyncResult": "Sync Result", | |||
| "btnSyncPo": "Sync PO from M18", | |||
| "btnSyncDo": "Sync DO from M18", | |||
| "btnSyncDoExtra": "Sync DO (Extra) from M18", | |||
| "btnSyncProduct": "Sync product from M18", | |||
| "syncing": "Syncing...", | |||
| "placeholderPoCode": "e.g. PFP002PO26030341", | |||
| "placeholderDoCode": "e.g. same document code as M18 shop PO", | |||
| "placeholderDoExtraCode": "Enter multiple codes separated by comma or newline", | |||
| "placeholderProductCode": "e.g. PP1175 (M18 item code)", | |||
| "waiting": "Waiting for implementation...", | |||
| "noResponse": "No response received", | |||
| "alreadyExists": "{{docName}} already exists in system", | |||
| "notFoundInM18": "{{docName}} not found in M18", | |||
| "syncSuccess": "Synced successfully: {{total}} {{docName}}(s)", | |||
| "syncFail": "Failed: unable to sync {{docName}}", | |||
| "syncMixed": "Complete: success {{success}} / fail {{fail}} ({{docName}})", | |||
| "syncedOk": "Synced successfully", | |||
| "summarySuccess": "{{count}} delivery order(s) synced successfully", | |||
| "summaryNotFound": "{{count}} order(s) not found in M18 ({{codes}})", | |||
| "summaryExists": "{{count}} order(s) already exist in system ({{codes}})", | |||
| "summaryFail": "{{count}} order(s) sync failed ({{codes}})", | |||
| "alertEnterPoCode": "Please enter PO code.", | |||
| "alertEnterDoCode": "Please enter DO / shop PO code.", | |||
| "alertEnterDoExtraCode": "Please enter DO / shop PO code(s) (Extra).", | |||
| "alertEnterAtLeastOne": "Please enter at least one code.", | |||
| "alertEnterProductCode": "Please enter M18 item / product code.", | |||
| "alertSyncFailed": "Sync failed: {{status}}", | |||
| "errorPoSync": "M18 PO sync failed. Check console/network.", | |||
| "errorDoSync": "M18 DO sync failed. Check console/network.", | |||
| "errorDoExtraSync": "M18 DO (Extra) sync failed. Check console/network.", | |||
| "errorProductSync": "M18 product sync failed. Check console/network." | |||
| } | |||
| @@ -94,15 +94,15 @@ | |||
| "qty cannot be greater than remaining qty": "qty cannot be greater than remaining qty", | |||
| "Record pol": "Record pol", | |||
| "Add some entries!": "Add some entries!", | |||
| "draft": "draft", | |||
| "pending": "pending", | |||
| "determine1": "determine1", | |||
| "determine2": "determine2", | |||
| "determine3": "determine3", | |||
| "receiving": "receiving", | |||
| "received": "received", | |||
| "completed": "completed", | |||
| "rejected": "rejected", | |||
| "draft": "Draft", | |||
| "pending": "Pending", | |||
| "determine1": "Escalation 1", | |||
| "determine2": "Escalation 2", | |||
| "determine3": "Escalation 3", | |||
| "receiving": "Receiving", | |||
| "received": "Awaiting Putaway", | |||
| "completed": "Completed", | |||
| "rejected": "Rejected", | |||
| "success": "success", | |||
| "acceptedQty must not greater than": "acceptedQty must not greater than", | |||
| "minimal value is 1": "minimal value is 1", | |||
| @@ -5,8 +5,11 @@ | |||
| "notEscalated": "notEscalated", | |||
| "All": "All", | |||
| "Pending": "Pending", | |||
| "pending": "Pending", | |||
| "Receiving": "Receiving", | |||
| "receiving": "Receiving", | |||
| "Completed": "Completed", | |||
| "completed": "Completed", | |||
| "Purchase Order": "Purchase Order", | |||
| "Details": "Details", | |||
| "OrderDate": "OrderDate", | |||
| @@ -1,8 +1,18 @@ | |||
| { | |||
| "Brand": "Brand", | |||
| "Create Printer": "Create Printer", | |||
| "Delete": "Delete", | |||
| "Delete Failed": "Delete failed", | |||
| "Delete Success": "Delete Success", | |||
| "Description": "Description", | |||
| "DPI": "DPI", | |||
| "Edit": "Edit", | |||
| "IP": "IP", | |||
| "Name": "Name", | |||
| "PDF Preview": "PDF Preview", | |||
| "Port": "Port", | |||
| "Print failed": "Print failed", | |||
| "Print job sent successfully": "Print job sent successfully", | |||
| "Printer": "Printer" | |||
| "Printer": "Printer", | |||
| "Type": "Type" | |||
| } | |||
| @@ -59,6 +59,11 @@ | |||
| "Finished lines": "Finished lines", | |||
| "In Progress": "In Progress", | |||
| "In progress": "In progress", | |||
| "Not Started": "Not Started", | |||
| "cancelled": "Cancelled", | |||
| "in_progress": "In Progress", | |||
| "pending": "Pending", | |||
| "stopped": "Stopped", | |||
| "Invalid Job Order Id": "Invalid Job Order Id", | |||
| "Invalid Stock In Line Id": "Invalid Stock In Line Id", | |||
| "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity", | |||
| @@ -1,4 +1,8 @@ | |||
| { | |||
| "Code cannot be empty": "Code cannot be empty", | |||
| "Code required!": "Code is required", | |||
| "Name cannot be empty": "Name cannot be empty", | |||
| "Name required!": "Name is required", | |||
| "Cancel": "Cancel", | |||
| "Code": "Code", | |||
| "Create Qc Category": "Create Qc Category", | |||
| @@ -1,4 +1,8 @@ | |||
| { | |||
| "Code cannot be empty": "Code cannot be empty", | |||
| "Code required!": "Code is required", | |||
| "Name cannot be empty": "Name cannot be empty", | |||
| "Name required!": "Name is required", | |||
| "Cancel": "Cancel", | |||
| "Code": "Code", | |||
| "Create Qc Item": "Create Qc Item", | |||
| @@ -1,4 +1,11 @@ | |||
| { | |||
| "Code cannot be empty": "Code cannot be empty", | |||
| "Code required!": "Code is required", | |||
| "Error": "Error", | |||
| "Item {{itemCode}} is already mapped as {{type}} in category {{conflictCategory}}. Cannot set category {{currentCategory}} to {{newType}}.": "Item {{itemCode}} is already mapped as {{type}} in category {{conflictCategory}}. Cannot set category {{currentCategory}} to {{newType}}.", | |||
| "Loading...": "Loading...", | |||
| "Name cannot be empty": "Name cannot be empty", | |||
| "Name required!": "Name is required", | |||
| "Actions": "Actions", | |||
| "Add": "Add", | |||
| "Add Association": "Add Association", | |||
| @@ -7,6 +14,7 @@ | |||
| "Association Details": "Association Details", | |||
| "Cancel": "Cancel", | |||
| "Cannot Delete": "Cannot Delete", | |||
| "Cannot delete QcCategory linked": "Cannot delete QcCategory. It still has linked items or QC items.", | |||
| "Cannot delete QcCategory. It has {itemCount} item(s) and {qcItemCount} qc item(s) linked to it.": "Cannot delete QcCategory. It has {itemCount} item(s) and {qcItemCount} qc item(s) linked to it.", | |||
| "Cannot delete QcItem. It is linked to one or more QcCategories.": "Cannot delete QcItem. It is linked to one or more QcCategories.", | |||
| "Category": "Category", | |||
| @@ -62,5 +70,8 @@ | |||
| "Submit Error": "Submit Error", | |||
| "Submit Success": "Submit Success", | |||
| "Type": "Type", | |||
| "Type.EPQC": "End-product QC (EPQC)", | |||
| "Type.IPQC": "In-process QC (IPQC)", | |||
| "Type.IQC": "Incoming QC (IQC)", | |||
| "View": "View" | |||
| } | |||
| @@ -329,6 +329,14 @@ | |||
| "schedule_history_subtitle": "Monitor, run, or troubleshoot scheduled shop-lane changes", | |||
| "schedule_history_success_lines": "{{count}} shop move(s) succeeded", | |||
| "schedule_history_title": "Scheduled task status & history", | |||
| "PENDING": "Pending", | |||
| "APPLYING": "Applying", | |||
| "APPLIED": "Applied", | |||
| "PARTIAL": "Partial", | |||
| "FAILED": "Failed", | |||
| "CANCELLED": "Cancelled", | |||
| "IGNORED": "Ignored", | |||
| "SKIPPED": "Skipped", | |||
| "schedule_history_unknown_user": "System", | |||
| "schedule_history_view_lines": "View shop lines", | |||
| "schedule_import_col_action": "Action", | |||
| @@ -42,7 +42,7 @@ | |||
| "Tomorrow": "Tomorrow", | |||
| "Truck Information": "Truck Information", | |||
| "Truck Lane Code": "Truck Lane Code", | |||
| "completed": "completed", | |||
| "pending": "pending", | |||
| "released": "released" | |||
| "completed": "Completed", | |||
| "pending": "Pending", | |||
| "released": "Released" | |||
| } | |||
| @@ -41,6 +41,8 @@ | |||
| "view user": "view user", | |||
| "An error has occurred. Please try again later.": "An error has occurred. Please try again later.", | |||
| "Please input correct password": "Please input correct password", | |||
| "The password requires 8-20 characters.": "The password requires 8-20 characters.", | |||
| "A combination of uppercase letters, lowercase letters, numbers, and symbols is required.": "The password must be 8-20 characters and include uppercase letters, lowercase letters, numbers, and symbols.", | |||
| "Failed to search by name": "Failed to search by name", | |||
| "Failed to search by username": "Failed to search by username", | |||
| "Staff No is required": "Staff No is required", | |||
| @@ -23,6 +23,7 @@ | |||
| "Day Before Yesterday": "前天", | |||
| "Delete": "刪除", | |||
| "Delete Failed": "刪除失敗", | |||
| "Do you want to delete?": "您確定要刪除嗎?", | |||
| "Density": "濃淡", | |||
| "Depth": "顔色深淺度 深1淺5", | |||
| "Description": "描述", | |||
| @@ -43,6 +44,7 @@ | |||
| "Invalid Job Order Id": "無效工單編號", | |||
| "Invoice": "發票", | |||
| "Invoice Date": "發票日期", | |||
| "IP": "IP 地址", | |||
| "Item Code": "物品編號", | |||
| "Item Name": "物品名稱", | |||
| "Loading": "載入中...", | |||
| @@ -53,6 +55,7 @@ | |||
| "MI": "雜項", | |||
| "Management Job Order": "管理工單", | |||
| "Material Name": "材料清單", | |||
| "Name": "名稱", | |||
| "Min": "最小值", | |||
| "NM": "雜項及非消耗品", | |||
| "No": "否", | |||
| @@ -62,6 +65,7 @@ | |||
| "No processes found for this job order": "找不到此工單的工序", | |||
| "Order": "順序", | |||
| "Pending": "待處理", | |||
| "Port": "連接埠", | |||
| "Please Select BOM": "請選擇 BOM", | |||
| "Please try again later.": "請稍後重試。", | |||
| "Project Code": "專案代碼", | |||
| @@ -91,6 +95,7 @@ | |||
| "Supporting Document": "證明文件", | |||
| "Task": "任務", | |||
| "Time Sequence": "時段", | |||
| "Type": "類型", | |||
| "Today": "今天", | |||
| "Total weighting must equal 1": "權重總和必須等於 1", | |||
| "Unauthorized: Please log in again": "未經授權:請重新登入", | |||
| @@ -53,6 +53,8 @@ | |||
| "Details": "詳情", | |||
| "Pending": "待處理", | |||
| "pending": "待處理", | |||
| "picking": "提料中", | |||
| "released": "已放單", | |||
| "Receiving": "接收中", | |||
| "receiving": "接收中", | |||
| "Completed": "已完成", | |||
| @@ -1,4 +1,6 @@ | |||
| { | |||
| "Code": "物品編號", | |||
| "Name": "物品名稱", | |||
| "Create Material": "新增材料", | |||
| "Update Equipment Maintenance and Repair": "更新設備的維護和保養", | |||
| "Correct BOM List (Can Import)": "正確 BOM 列表(可匯入)", | |||
| @@ -8,5 +10,39 @@ | |||
| "Is Drink": "飲料", | |||
| "Drink": "飲料", | |||
| "Powder_Mixture": "箱料粉", | |||
| "Base Score": "基礎得分" | |||
| "Base Score": "基礎得分", | |||
| "Basic Info": "基本資訊", | |||
| "Edit": "編輯", | |||
| "Save": "儲存", | |||
| "Cancel": "取消", | |||
| "Loading...": "載入中…", | |||
| "Saving...": "儲存中…", | |||
| "Output Quantity": "產出數量", | |||
| "Output Quantity UOM": "產出單位", | |||
| "Type": "類型", | |||
| "Allergic Substances": "過敏原", | |||
| "Depth": "色深", | |||
| "Float": "浮沉", | |||
| "Density": "濃淡", | |||
| "Time Sequence": "時段", | |||
| "Complexity": "複雜度", | |||
| "Scrap Rate": "損耗率", | |||
| "Item Code": "物品編號", | |||
| "Item Name": "物品名稱", | |||
| "Base Qty": "基本數量", | |||
| "Base UOM": "基本單位", | |||
| "Stock Qty": "庫存數量", | |||
| "Stock UOM": "庫存單位", | |||
| "Sales Qty": "銷售數量", | |||
| "Sales UOM": "銷售單位", | |||
| "Process & Equipment": "製程與設備", | |||
| "Process Code": "工序代碼", | |||
| "Process Description": "工序說明", | |||
| "Process Name": "工序名稱", | |||
| "Duration (Minutes)": "時間(分)", | |||
| "Prep Time (Minutes)": "準備時間(分鐘)", | |||
| "Post Prod Time (Minutes)": "收尾時間(分鐘)", | |||
| "Add": "新增", | |||
| "Sequence": "次序", | |||
| "Actions": "操作" | |||
| } | |||
| @@ -606,6 +606,10 @@ | |||
| "packaging": "提料中", | |||
| "paused": "已暫停", | |||
| "pending": "待處理", | |||
| "pendingQC": "待品檢", | |||
| "planning": "規劃中", | |||
| "processing": "生產中", | |||
| "scanned": "已掃描", | |||
| "printQty": "打印數量", | |||
| "process epqc": "進行成品檢驗", | |||
| "process stockIn": "進行收貨程序", | |||
| @@ -1,3 +1,49 @@ | |||
| { | |||
| "title": "M18 同步" | |||
| "title": "M18 同步", | |||
| "pageTitle": "M18 同步", | |||
| "pageSubtitle": "僅限管理員。使用文件或貨品編號從 M18 同步採購單、送貨訂單或貨品/物料。", | |||
| "tabPo": "1. 採購單", | |||
| "tabDo": "2. 送貨訂單", | |||
| "tabDoExtra": "3. 送貨訂單 (加單)", | |||
| "tabProduct": "4. 貨品", | |||
| "sectionPo": "M18 採購單 — sync by code", | |||
| "sectionDo": "M18 送貨訂單 — sync by code", | |||
| "sectionDoExtra": "M18 送貨訂單 — 加單", | |||
| "sectionProduct": "M18 貨品 / 物料 — sync by code", | |||
| "labelPoCode": "採購單編號", | |||
| "labelDoCode": "送貨單 / 店鋪訂單編號", | |||
| "labelDoExtraCode": "送貨單 / 店鋪訂單編號(加單)", | |||
| "labelProductCode": "貨品 / 物料編號", | |||
| "labelSyncResult": "同步結果", | |||
| "btnSyncPo": "從 M18 同步採購單", | |||
| "btnSyncDo": "從 M18 同步送貨訂單", | |||
| "btnSyncDoExtra": "從 M18 同步送貨訂單(加單)", | |||
| "btnSyncProduct": "從 M18 同步貨品", | |||
| "syncing": "同步中…", | |||
| "placeholderPoCode": "例:PFP002PO26030341", | |||
| "placeholderDoCode": "例:與 M18 店鋪訂單相同的文件編號", | |||
| "placeholderDoExtraCode": "可輸入多個 code,用逗號或換行分隔", | |||
| "placeholderProductCode": "例:PP1175(M18 貨品編號)", | |||
| "waiting": "等待實作…", | |||
| "noResponse": "未收到回應", | |||
| "alreadyExists": "{{docName}}已存在系統", | |||
| "notFoundInM18": "在M18找不到{{docName}}", | |||
| "syncSuccess": "成功同步:{{total}}張{{docName}}", | |||
| "syncFail": "失敗:無法同步{{docName}}", | |||
| "syncMixed": "完成:成功 {{success}}/失敗 {{fail}}({{docName}})", | |||
| "syncedOk": "成功同步", | |||
| "summarySuccess": "共{{count}}張送貨訂單成功", | |||
| "summaryNotFound": "共{{count}}張在M18找不到訂單({{codes}})", | |||
| "summaryExists": "共{{count}}張訂單已存在系統({{codes}})", | |||
| "summaryFail": "共{{count}}張同步失敗({{codes}})", | |||
| "alertEnterPoCode": "請輸入採購單編號。", | |||
| "alertEnterDoCode": "請輸入送貨單/店鋪訂單編號。", | |||
| "alertEnterDoExtraCode": "請輸入送貨單/店鋪訂單編號(加單)。", | |||
| "alertEnterAtLeastOne": "請輸入至少一個編號。", | |||
| "alertEnterProductCode": "請輸入 M18 貨品/物料編號。", | |||
| "alertSyncFailed": "同步失敗:{{status}}", | |||
| "errorPoSync": "M18 採購單同步失敗,請檢查 console/network。", | |||
| "errorDoSync": "M18 送貨訂單同步失敗,請檢查 console/network。", | |||
| "errorDoExtraSync": "M18 送貨訂單(加單)同步失敗,請檢查 console/network。", | |||
| "errorProductSync": "M18 貨品同步失敗,請檢查 console/network。" | |||
| } | |||
| @@ -5,8 +5,11 @@ | |||
| "notEscalated": "未升级", | |||
| "All": "全部", | |||
| "Pending": "待处理", | |||
| "pending": "待處理", | |||
| "Receiving": "接收中", | |||
| "receiving": "接收中", | |||
| "Completed": "已完成", | |||
| "completed": "已完成", | |||
| "Purchase Order": "采购订单", | |||
| "Details": "详情", | |||
| "OrderDate": "订单日期", | |||
| @@ -1,8 +1,18 @@ | |||
| { | |||
| "Brand": "品牌", | |||
| "Create Printer": "新增列印機", | |||
| "Delete": "刪除", | |||
| "Delete Failed": "刪除失敗", | |||
| "Delete Success": "刪除成功", | |||
| "Description": "描述", | |||
| "DPI": "DPI", | |||
| "Edit": "編輯", | |||
| "IP": "IP 地址", | |||
| "Name": "名稱", | |||
| "PDF Preview": "PDF 預覽", | |||
| "Port": "連接埠", | |||
| "Print failed": "列印失敗", | |||
| "Print job sent successfully": "列印作業已成功送出", | |||
| "Printer": "列印機" | |||
| "Printer": "列印機", | |||
| "Type": "類型" | |||
| } | |||
| @@ -59,6 +59,11 @@ | |||
| "Finished lines": "已完成流程", | |||
| "In Progress": "進行中", | |||
| "In progress": "進行中", | |||
| "Not Started": "未開始", | |||
| "cancelled": "已取消", | |||
| "in_progress": "進行中", | |||
| "pending": "待處理", | |||
| "stopped": "已停止", | |||
| "Invalid Job Order Id": "無效工單編號", | |||
| "Invalid Stock In Line Id": "無效庫存行ID", | |||
| "Is Dark | Dense | Float| Scrap Rate| Allergic Substance | Time Sequence | Complexity": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原 | 時間次序 | 複雜度", | |||
| @@ -1,4 +1,8 @@ | |||
| { | |||
| "Code cannot be empty": "編號不可為空", | |||
| "Code required!": "請輸入編號", | |||
| "Name cannot be empty": "名稱不可為空", | |||
| "Name required!": "請輸入名稱", | |||
| "Qc Category": "品檢模板", | |||
| "Qc Category List": "QC 類別列表", | |||
| "Qc Category Name": "QC 類別名稱", | |||
| @@ -1,4 +1,8 @@ | |||
| { | |||
| "Code cannot be empty": "編號不可為空", | |||
| "Code required!": "請輸入編號", | |||
| "Name cannot be empty": "名稱不可為空", | |||
| "Name required!": "請輸入名稱", | |||
| "Name": "名稱", | |||
| "Code": "編號", | |||
| "Description": "描述", | |||
| @@ -1,4 +1,11 @@ | |||
| { | |||
| "Code cannot be empty": "編號不可為空", | |||
| "Code required!": "請輸入編號", | |||
| "Error": "錯誤", | |||
| "Item {{itemCode}} is already mapped as {{type}} in category {{conflictCategory}}. Cannot set category {{currentCategory}} to {{newType}}.": "物料 {{itemCode}} 已以 {{type}} 映射於模板「{{conflictCategory}}」,無法將模板「{{currentCategory}}」改為 {{newType}}。", | |||
| "Loading...": "載入中...", | |||
| "Name cannot be empty": "名稱不可為空", | |||
| "Name required!": "請輸入名稱", | |||
| "Qc Item All": "QC 綜合管理", | |||
| "Item and Qc Category Mapping": "物品與品檢模板映射", | |||
| "Qc Category and Qc Item Mapping": "品檢模板與品檢項目映射", | |||
| @@ -20,6 +27,9 @@ | |||
| "Name": "名稱", | |||
| "Description": "描述", | |||
| "Type": "類型", | |||
| "Type.EPQC": "生產後品檢EPQC", | |||
| "Type.IPQC": "製程品檢IPQC", | |||
| "Type.IQC": "來貨品檢IQC", | |||
| "Order": "順序", | |||
| "Item Count": "關聯物品數量", | |||
| "Qc Item Count": "關聯品檢項目數量", | |||
| @@ -44,7 +54,8 @@ | |||
| "Submit Success": "提交成功", | |||
| "Submit Error": "提交失敗", | |||
| "Cannot Delete": "無法刪除", | |||
| "Cannot delete QcCategory. It has {itemCount} item(s) and {qcItemCount} qc item(s) linked to it.": "無法刪除品檢模板。它有 {itemCount} 個物品和 {qcItemCount} 個品檢項目與其關聯。", | |||
| "Cannot delete QcCategory linked": "無法刪除品檢模板,仍有物品或品檢項目與其關聯。", | |||
| "Cannot delete QcCategory. It has {itemCount} item(s) and {qcItemCount} qc item(s) linked to it.": "無法刪除品檢模板,仍有 {itemCount} 個物品及 {qcItemCount} 個品檢項目與其關聯。", | |||
| "Cannot delete QcItem. It is linked to one or more QcCategories.": "無法刪除品檢項目。它與一個或多個品檢模板關聯。", | |||
| "Select Item": "選擇物品", | |||
| "Select Qc Category": "選擇品檢模板", | |||
| @@ -359,6 +359,14 @@ | |||
| "schedule_history_ignore": "忽略", | |||
| "schedule_history_status_ignored": "已忽略", | |||
| "schedule_history_close": "關閉視窗", | |||
| "PENDING": "待處理", | |||
| "APPLYING": "執行中", | |||
| "APPLIED": "已套用", | |||
| "PARTIAL": "部分完成", | |||
| "FAILED": "失敗", | |||
| "CANCELLED": "已取消", | |||
| "IGNORED": "已忽略", | |||
| "SKIPPED": "已跳過", | |||
| "schedule_log_failed_hint": "有 {{count}} 個預約任務執行失敗,請查看 Log", | |||
| "btn_scheduleHistory": "查看預約變更 Log", | |||
| "id": "ID", | |||
| @@ -1,7 +1,7 @@ | |||
| { | |||
| "View Details": "查看詳細", | |||
| "Back to List": "返回列表", | |||
| "Stock Take Section": "盤點區域", | |||
| "Stock Take Section": "盤點區域類型", | |||
| "Warehouse": "倉庫", | |||
| "All": "全部", | |||
| "Rows per page": "每頁行數", | |||
| @@ -21,7 +21,7 @@ | |||
| "Cancel": "取消", | |||
| "Confirm": "確認", | |||
| "Type": "類型", | |||
| "Status": "來貨狀態", | |||
| "Status": "盤點狀態", | |||
| "Lot No": "批號", | |||
| "Location": "位置", | |||
| "Reason": "原因", | |||
| @@ -41,6 +41,8 @@ | |||
| "Testing": "測試", | |||
| "An error has occurred. Please try again later.": "發生錯誤,請稍後再試。", | |||
| "Please input correct password": "請輸入正確密碼", | |||
| "The password requires 8-20 characters.": "密碼須為 8 至 20 個字元。", | |||
| "A combination of uppercase letters, lowercase letters, numbers, and symbols is required.": "密碼須為 8 至 20 個字元,且須同時包含大寫字母、小寫字母、數字及符號。", | |||
| "Failed to search by name": "依名稱搜尋失敗", | |||
| "Failed to search by username": "依使用者名稱搜尋失敗", | |||
| "Staff No is required": "員工編號必填", | |||
| @@ -32,7 +32,7 @@ | |||
| "store_id": "樓層", | |||
| "area": "區域", | |||
| "slot": "位置", | |||
| "order": "提料單次序", | |||
| "order": "提料區域次序", | |||
| "stockTakeSection": "盤點區域", | |||
| "Do you want to delete?": "您確定要刪除嗎?", | |||
| "Cancel": "取消", | |||