tommy 1 неделю назад
Родитель
Сommit
66f3589d34
61 измененных файлов: 585 добавлений и 140 удалений
  1. +7
    -4
      src/app/(main)/settings/qcItemAll/page.tsx
  2. +9
    -0
      src/components/Breadcrumb/Breadcrumb.tsx
  3. +3
    -3
      src/components/CreatePrinter/CreatePrinter.tsx
  4. +2
    -1
      src/components/CreateUser/CreateUser.tsx
  5. +2
    -2
      src/components/DoSearch/DoSearch.tsx
  6. +2
    -2
      src/components/DoSearchWorkbench/DoSearchWorkbench.tsx
  7. +3
    -3
      src/components/EditPrinter/EditPrinter.tsx
  8. +2
    -1
      src/components/EditUser/EditUser.tsx
  9. +3
    -3
      src/components/FinishedGoodSearch/AssignAndRelease.tsx
  10. +1
    -0
      src/components/FinishedGoodSearch/ConsolidatedPickOrders.tsx
  11. +2
    -2
      src/components/FinishedGoodSearch/FinishedGood.tsx
  12. +1
    -2
      src/components/FinishedGoodSearch/FinishedGoodSearch.tsx
  13. +2
    -2
      src/components/JoSave/InfoCard.tsx
  14. +2
    -2
      src/components/JoSearch/JoSearch.tsx
  15. +2
    -2
      src/components/JoWorkbench/JoWorkbenchSearch.tsx
  16. +2
    -2
      src/components/Jodetail/Jodetail.tsx
  17. +1
    -2
      src/components/Jodetail/JodetailSearch.tsx
  18. +1
    -0
      src/components/PickOrderSearch/ConsolidatedPickOrders.tsx
  19. +4
    -4
      src/components/PrinterSearch/PrinterSearch.tsx
  20. +2
    -2
      src/components/ProductionProcess/ProductionProcess.tsx
  21. +2
    -2
      src/components/QcCategorySave/QcCategoryDetails.tsx
  22. +31
    -36
      src/components/QcItemAll/Tab0ItemQcCategoryMapping.tsx
  23. +38
    -3
      src/components/QcItemAll/Tab1QcCategoryQcItemMapping.tsx
  24. +35
    -9
      src/components/QcItemAll/Tab2QcCategoryManagement.tsx
  25. +23
    -6
      src/components/QcItemAll/Tab3QcItemManagement.tsx
  26. +72
    -0
      src/components/QcItemAll/qcItemAllMessages.ts
  27. +2
    -2
      src/components/QcItemSave/QcItemDetails.tsx
  28. +1
    -1
      src/components/Shop/ScheduleTaskHistoryModal.tsx
  29. +3
    -3
      src/components/Warehouse/TabStockTakeSectionMapping.tsx
  30. +14
    -14
      src/config/reportConfig.ts
  31. +5
    -0
      src/i18n/en/common.json
  32. +5
    -0
      src/i18n/en/do.json
  33. +6
    -3
      src/i18n/en/finishedgoodmanagement.json
  34. +37
    -1
      src/i18n/en/importBom.json
  35. +4
    -0
      src/i18n/en/jo.json
  36. +47
    -1
      src/i18n/en/m18Sync.json
  37. +9
    -9
      src/i18n/en/pickOrder.json
  38. +3
    -0
      src/i18n/en/po.json
  39. +11
    -1
      src/i18n/en/printer.json
  40. +5
    -0
      src/i18n/en/productionProcess.json
  41. +4
    -0
      src/i18n/en/qcCategory.json
  42. +4
    -0
      src/i18n/en/qcItem.json
  43. +11
    -0
      src/i18n/en/qcItemAll.json
  44. +8
    -0
      src/i18n/en/shop.json
  45. +3
    -3
      src/i18n/en/ticketReleaseTable.json
  46. +2
    -0
      src/i18n/en/user.json
  47. +5
    -0
      src/i18n/zh/common.json
  48. +2
    -0
      src/i18n/zh/do.json
  49. +37
    -1
      src/i18n/zh/importBom.json
  50. +4
    -0
      src/i18n/zh/jo.json
  51. +47
    -1
      src/i18n/zh/m18Sync.json
  52. +3
    -0
      src/i18n/zh/po.json
  53. +11
    -1
      src/i18n/zh/printer.json
  54. +5
    -0
      src/i18n/zh/productionProcess.json
  55. +4
    -0
      src/i18n/zh/qcCategory.json
  56. +4
    -0
      src/i18n/zh/qcItem.json
  57. +12
    -1
      src/i18n/zh/qcItemAll.json
  58. +8
    -0
      src/i18n/zh/shop.json
  59. +2
    -2
      src/i18n/zh/stockTake.json
  60. +2
    -0
      src/i18n/zh/user.json
  61. +1
    -1
      src/i18n/zh/warehouse.json

+ 7
- 4
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<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 />}


+ 9
- 0
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",


+ 3
- 3
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<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")}


+ 2
- 1
src/components/CreateUser/CreateUser.tsx Просмотреть файл

@@ -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",
});
}


+ 2
- 2
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<Props> = ({ filterArgs, searchQuery, onDeliveryOrderSea
headerName: t("Status"),
flex: 1,
renderCell: (params) => {
return t(upperFirst(params.row.status));
return t(params.row.status);
},
},
],


+ 2
- 2
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<Props> = ({
headerName: t("Status"),
flex: 1,
renderCell: (params) => {
return t(upperFirst(params.row.status));
return t(params.row.status);
},
},
],


+ 3
- 3
src/components/EditPrinter/EditPrinter.tsx Просмотреть файл

@@ -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")}


+ 2
- 1
src/components/EditUser/EditUser.tsx Просмотреть файл

@@ -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",
});
}


+ 3
- 3
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 - 只在第一个项目显示 */}
<TableCell>
{index === 0 ? upperFirst(item.status) : null}
{index === 0 ? t(item.status) : null}
</TableCell>
</TableRow>
))


+ 1
- 0
src/components/FinishedGoodSearch/ConsolidatedPickOrders.tsx Просмотреть файл

@@ -134,6 +134,7 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => {
{
name: "status",
label: t("status"),
renderCell: (params: any) => t(params.status),
},
],
[onDetailClick, t],


+ 2
- 2
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<Props> = ({ filteredPickOrders, filterArgs }) => {
name: "status",
label: t("Status"),
renderCell: (params) => {
return upperFirst(params.status);
return t(params.status);
},
},
],


+ 1
- 2
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",
),


+ 2
- 2
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<Props> = ({
label={t("Status")}
fullWidth
disabled={true}
value={`${t(upperFirst(watch("status")))}`}
value={`${t(watch("status"))}`}
/>
</Grid>
<Grid item xs={6}/>*/}


+ 2
- 2
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<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>
}
},


+ 2
- 2
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<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>
}
},


+ 2
- 2
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<Props> = ({ filteredPickOrders, filterArgs }) => {
name: "status",
label: t("Status"),
renderCell: (params) => {
return upperFirst(params.status);
return t(params.status);
},
},
],


+ 1
- 2
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<Props> = ({ printerCombo }) => {
uniqBy(
pickOrders.map((po) => ({
value: po.status,
label: t(upperFirst(po.status)),
label: t(po.status),
})),
"value",
),


+ 1
- 0
src/components/PickOrderSearch/ConsolidatedPickOrders.tsx Просмотреть файл

@@ -134,6 +134,7 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => {
{
name: "status",
label: t("status"),
renderCell: (params: any) => t(params.status),
},
],
[onDetailClick, t],


+ 4
- 4
src/components/PrinterSearch/PrinterSearch.tsx Просмотреть файл

@@ -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" },


+ 2
- 2
src/components/ProductionProcess/ProductionProcess.tsx Просмотреть файл

@@ -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"
/>


+ 2
- 2
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,
})}
/>


+ 31
- 36
src/components/QcItemAll/Tab0ItemQcCategoryMapping.tsx Просмотреть файл

@@ -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>


+ 38
- 3
src/components/QcItemAll/Tab1QcCategoryQcItemMapping.tsx Просмотреть файл

@@ -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


+ 35
- 9
src/components/QcItemAll/Tab2QcCategoryManagement.tsx Просмотреть файл

@@ -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"),


+ 23
- 6
src/components/QcItemAll/Tab3QcItemManagement.tsx Просмотреть файл

@@ -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]);


+ 72
- 0
src/components/QcItemAll/qcItemAllMessages.ts Просмотреть файл

@@ -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;
}

+ 2
- 2
src/components/QcItemSave/QcItemDetails.tsx Просмотреть файл

@@ -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,
})}
/>


+ 1
- 1
src/components/Shop/ScheduleTaskHistoryModal.tsx Просмотреть файл

@@ -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" }}
/>


+ 3
- 3
src/components/Warehouse/TabStockTakeSectionMapping.tsx Просмотреть файл

@@ -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>) => {


+ 14
- 14
src/config/reportConfig.ts Просмотреть файл

@@ -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,


+ 5
- 0
src/i18n/en/common.json Просмотреть файл

@@ -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": "未經授權:請重新登入",


+ 5
- 0
src/i18n/en/do.json Просмотреть файл

@@ -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",


+ 6
- 3
src/i18n/en/finishedgoodmanagement.json Просмотреть файл

@@ -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"
}

+ 37
- 1
src/i18n/en/importBom.json Просмотреть файл

@@ -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"
}

+ 4
- 0
src/i18n/en/jo.json Просмотреть файл

@@ -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",


+ 47
- 1
src/i18n/en/m18Sync.json Просмотреть файл

@@ -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."
}

+ 9
- 9
src/i18n/en/pickOrder.json Просмотреть файл

@@ -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",


+ 3
- 0
src/i18n/en/po.json Просмотреть файл

@@ -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",


+ 11
- 1
src/i18n/en/printer.json Просмотреть файл

@@ -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"
}

+ 5
- 0
src/i18n/en/productionProcess.json Просмотреть файл

@@ -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",


+ 4
- 0
src/i18n/en/qcCategory.json Просмотреть файл

@@ -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",


+ 4
- 0
src/i18n/en/qcItem.json Просмотреть файл

@@ -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",


+ 11
- 0
src/i18n/en/qcItemAll.json Просмотреть файл

@@ -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"
}

+ 8
- 0
src/i18n/en/shop.json Просмотреть файл

@@ -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",


+ 3
- 3
src/i18n/en/ticketReleaseTable.json Просмотреть файл

@@ -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"
}

+ 2
- 0
src/i18n/en/user.json Просмотреть файл

@@ -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",


+ 5
- 0
src/i18n/zh/common.json Просмотреть файл

@@ -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": "未經授權:請重新登入",


+ 2
- 0
src/i18n/zh/do.json Просмотреть файл

@@ -53,6 +53,8 @@
"Details": "詳情",
"Pending": "待處理",
"pending": "待處理",
"picking": "提料中",
"released": "已放單",
"Receiving": "接收中",
"receiving": "接收中",
"Completed": "已完成",


+ 37
- 1
src/i18n/zh/importBom.json Просмотреть файл

@@ -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": "操作"
}

+ 4
- 0
src/i18n/zh/jo.json Просмотреть файл

@@ -606,6 +606,10 @@
"packaging": "提料中",
"paused": "已暫停",
"pending": "待處理",
"pendingQC": "待品檢",
"planning": "規劃中",
"processing": "生產中",
"scanned": "已掃描",
"printQty": "打印數量",
"process epqc": "進行成品檢驗",
"process stockIn": "進行收貨程序",


+ 47
- 1
src/i18n/zh/m18Sync.json Просмотреть файл

@@ -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。"
}

+ 3
- 0
src/i18n/zh/po.json Просмотреть файл

@@ -5,8 +5,11 @@
"notEscalated": "未升级",
"All": "全部",
"Pending": "待处理",
"pending": "待處理",
"Receiving": "接收中",
"receiving": "接收中",
"Completed": "已完成",
"completed": "已完成",
"Purchase Order": "采购订单",
"Details": "详情",
"OrderDate": "订单日期",


+ 11
- 1
src/i18n/zh/printer.json Просмотреть файл

@@ -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": "類型"
}

+ 5
- 0
src/i18n/zh/productionProcess.json Просмотреть файл

@@ -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": "顔色深淺度 | 濃淡 | 浮沉 | 損耗率 | 過敏原 | 時間次序 | 複雜度",


+ 4
- 0
src/i18n/zh/qcCategory.json Просмотреть файл

@@ -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 類別名稱",


+ 4
- 0
src/i18n/zh/qcItem.json Просмотреть файл

@@ -1,4 +1,8 @@
{
"Code cannot be empty": "編號不可為空",
"Code required!": "請輸入編號",
"Name cannot be empty": "名稱不可為空",
"Name required!": "請輸入名稱",
"Name": "名稱",
"Code": "編號",
"Description": "描述",


+ 12
- 1
src/i18n/zh/qcItemAll.json Просмотреть файл

@@ -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": "選擇品檢模板",


+ 8
- 0
src/i18n/zh/shop.json Просмотреть файл

@@ -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",


+ 2
- 2
src/i18n/zh/stockTake.json Просмотреть файл

@@ -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": "原因",


+ 2
- 0
src/i18n/zh/user.json Просмотреть файл

@@ -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": "員工編號必填",


+ 1
- 1
src/i18n/zh/warehouse.json Просмотреть файл

@@ -32,7 +32,7 @@
"store_id": "樓層",
"area": "區域",
"slot": "位置",
"order": "提料次序",
"order": "提料區域次序",
"stockTakeSection": "盤點區域",
"Do you want to delete?": "您確定要刪除嗎?",
"Cancel": "取消",


Загрузка…
Отмена
Сохранить