瀏覽代碼

update create customer

tags/Baseline_30082024_FRONTEND_UAT
cyril.tsui 1 年之前
父節點
當前提交
32851b8be2
共有 15 個文件被更改,包括 500 次插入31 次删除
  1. +1
    -1
      src/app/(main)/settings/customer/create/page.tsx
  2. +1
    -1
      src/app/(main)/settings/customer/page.tsx
  3. +12
    -2
      src/app/api/customer/actions.ts
  4. +27
    -0
      src/app/api/customer/index.ts
  5. +2
    -2
      src/components/Breadcrumb/Breadcrumb.tsx
  6. +288
    -0
      src/components/CreateCustomer/ContactDetails.tsx
  7. +51
    -15
      src/components/CreateCustomer/CreateCustomer.tsx
  8. +4
    -3
      src/components/CreateCustomer/CreateCustomerWrapper.tsx
  9. +37
    -5
      src/components/CreateCustomer/CustomerDetails.tsx
  10. +2
    -2
      src/components/NavigationContent/NavigationContent.tsx
  11. +45
    -0
      src/components/Swal/CustomAlerts.js
  12. +1
    -0
      src/i18n/en/common.json
  13. +14
    -0
      src/i18n/en/customer.json
  14. +1
    -0
      src/i18n/zh/common.json
  15. +14
    -0
      src/i18n/zh/customer.json

src/app/(main)/customer/create/page.tsx → src/app/(main)/settings/customer/create/page.tsx 查看文件

@@ -17,7 +17,7 @@ const Projects: React.FC = async () => {
return (
<>
<Typography variant="h4">{t("Create Customer")}</Typography>
<I18nProvider namespaces={["customer"]}>
<I18nProvider namespaces={["customer", "common"]}>
<CreateCustomer />
</I18nProvider>
</>

src/app/(main)/customer/page.tsx → src/app/(main)/settings/customer/page.tsx 查看文件

@@ -33,7 +33,7 @@ const Customer: React.FC = async () => {
variant="contained"
startIcon={<Add />}
LinkComponent={Link}
href="/customer/create"
href="/settings/customer/create"
>
{t("Create Customer")}
</Button>

+ 12
- 2
src/app/api/customer/actions.ts 查看文件

@@ -2,12 +2,14 @@

import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { Customer } from ".";
import { Contact, NewCustomerResponse } from ".";
import { revalidateTag } from "next/cache";


export interface CustomerFormInputs {

// Customer details
id: number | null;
name: string;
code: string;
address: string | null;
@@ -16,14 +18,22 @@ export interface CustomerFormInputs {
phone: string | null;
contactName: string | null;
brNo: string | null;
typeId: number;

// Subsidiary
addSubsidiaryIds: number[];
deleteSubsidiaryIds: number[];

// Contact
addContacts: Contact[];
deleteContactIds: number[];

// is grid editing
isGridEditing: boolean | null;
}

export const saveCustomer = async (data: CustomerFormInputs) => {
const saveCustomer = await serverFetchJson<Customer>(
const saveCustomer = await serverFetchJson<NewCustomerResponse>(
`${BASE_API_URL}/customer/save`,
{
method: "POST",


+ 27
- 0
src/app/api/customer/index.ts 查看文件

@@ -9,6 +9,16 @@ export interface Customer {
name: string;
}

export interface NewCustomerResponse {
customer: Customer;
message: string;
}

export interface CustomerType {
id: number;
name: string;
}

export interface Subsidiary {
id: number;
code: string;
@@ -22,6 +32,14 @@ export interface Subsidiary {
email: string | null;
}

export interface Contact {
id: number;
name: string;
phone: string;
email: string;
isNew: boolean;
}

export const preloadAllCustomers = () => {
fetchAllCustomers();
};
@@ -38,3 +56,12 @@ export const fetchSubsidiaries = cache(async () => {
},
);
});

export const fetchCustomerTypes = cache(async () => {
return serverFetchJson<CustomerType[]>(
`${BASE_API_URL}/customer/types`,
{
next: { tags: ["CustomerTypes"] },
},
);
});

+ 2
- 2
src/components/Breadcrumb/Breadcrumb.tsx 查看文件

@@ -14,8 +14,8 @@ const pathToLabelMap: { [path: string]: string } = {
"/projects/create": "Create Project",
"/tasks": "Task Template",
"/tasks/create": "Create Task Template",
"/customer": "Customer",
"/customer/create": "Create Customer",
"/settings/customer": "Customer",
"/settings/customer/create": "Create Customer",
"/settings": "Settings",
"/company": "Company",
"/settings/department": "Department",


+ 288
- 0
src/components/CreateCustomer/ContactDetails.tsx 查看文件

@@ -0,0 +1,288 @@
"use client";

import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Grid from "@mui/material/Grid";
import Typography from "@mui/material/Typography";
import { useTranslation } from "react-i18next";
import CardActions from "@mui/material/CardActions";
import RestartAlt from "@mui/icons-material/RestartAlt";
import Button from "@mui/material/Button";
import AddIcon from '@mui/icons-material/Add';
import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/DeleteOutlined';
import SaveIcon from '@mui/icons-material/Save';
import CancelIcon from '@mui/icons-material/Close';
import {
GridRowsProp,
GridRowModesModel,
GridRowModes,
DataGrid,
GridColDef,
GridToolbarContainer,
GridActionsCellItem,
GridEventListener,
GridRowId,
GridRowModel,
GridRowEditStopReasons,
GridPreProcessEditCellProps,
GridCellParams,
} from '@mui/x-data-grid';
import CustomDatagrid from "../CustomDatagrid/CustomDatagrid";
import { Contact } from "@/app/api/customer";
import { useFieldArray, useFormContext } from "react-hook-form";
import { useCallback, useEffect, useMemo, useState } from "react";

interface Props {
}

interface EditToolbarProps {
setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void;
setRowModesModel: (
newModel: (oldModel: GridRowModesModel) => GridRowModesModel,
) => void;
}

var rowId = -1
function EditToolbar(props: EditToolbarProps) {
const { setRows, setRowModesModel } = props;
const { t } = useTranslation();

const handleClick = () => {
const id = rowId;
rowId = rowId - 1;
setRows((oldRows) => [{ id, name: '', phone: '', email: '', isNew: true }, ...oldRows]);
setRowModesModel((oldModel) => ({
...oldModel,
[id]: { mode: GridRowModes.Edit, fieldToFocus: 'name' },
}));
};

return (
<GridToolbarContainer>
<Button color="primary" startIcon={<AddIcon />} onClick={handleClick}>
{t("Add Contact Person")}
</Button>
</GridToolbarContainer>
);
}

const ContactDetails: React.FC<Props> = ({
}) => {
const { t } = useTranslation();

const { control, setValue, getValues, formState: { errors }, setError, clearErrors } = useFormContext();
const { fields } = useFieldArray({
control,
name: "addContacts"
})

const initialRows: GridRowsProp = fields.map((item, index) => {
return ({
id: Number(getValues(`addContacts[${index}].id`)),
name: getValues(`addContacts[${index}].name`),
phone: getValues(`addContacts[${index}].phone`),
email: getValues(`addContacts[${index}].email`),
isNew: false,
})
})

const [rows, setRows] = useState(initialRows);
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});

const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => {
if (params.reason === GridRowEditStopReasons.rowFocusOut) {
event.defaultMuiPrevented = true;
}
};

const handleEditClick = (id: GridRowId) => () => {
setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } });
};

const handleSaveClick = (id: GridRowId) => () => {
setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } });
};

const handleDeleteClick = (id: GridRowId) => () => {
const updatedRows = rows.filter((row) => row.id !== id)
setRows(updatedRows);
setValue("addContacts", updatedRows)
};

const handleCancelClick = (id: GridRowId) => () => {
setRowModesModel({
...rowModesModel,
[id]: { mode: GridRowModes.View, ignoreModifications: true },
});

const editedRow = rows.find((row) => row.id === id);
if (editedRow!.isNew) {
setRows(rows.filter((row) => row.id !== id));
}
};

const processRowUpdate = useCallback((newRow: GridRowModel) => {
const updatedRow = { ...newRow };

const updatedRows = rows.map((row) => (row.id === newRow.id ? updatedRow : row))
setRows(updatedRows);
setValue("addContacts", updatedRows)
return updatedRow;
}, [rows]);

const handleRowModesModelChange = useCallback((newRowModesModel: GridRowModesModel) => {
setRowModesModel(newRowModesModel);
}, [rows]);

const columns = useMemo<GridColDef[]>(
() => [
{
field: 'name',
headerName: t('Contact Name'),
editable: true,
flex: 1,
},
{
field: 'phone',
headerName: t('Contact Phone'),
editable: true,
flex: 1,
},
{
field: 'email',
headerName: t('Contact Email'),
editable: true,
flex: 1,
},
{
field: 'actions',
type: 'actions',
headerName: '',
flex: 0.6,
// width: 100,
cellClassName: 'actions',
getActions: ({ id, ...params }) => {
const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit;

if (isInEditMode) {
return [
<GridActionsCellItem
icon={<SaveIcon />}
label="Save"
sx={{
color: 'primary.main',
}}
onClick={handleSaveClick(id)}
/>,
<GridActionsCellItem
icon={<CancelIcon />}
label="Cancel"
className="textPrimary"
onClick={handleCancelClick(id)}
color="inherit"
/>,
];
}

return [
<GridActionsCellItem
icon={<EditIcon />}
label="Edit"
className="textPrimary"
onClick={handleEditClick(id)}
color="inherit"
/>,
<GridActionsCellItem
icon={<DeleteIcon />}
label="Delete"
onClick={handleDeleteClick(id)}
color="inherit"
/>,
];
},
},
],
[rows, rowModesModel, t],
);

// check error
useEffect(() => {
if (getValues("addContacts").length === 0) {
clearErrors("addContacts")
} else {
const errorRows = rows.filter(row => String(row.name).trim().length === 0 || String(row.phone).trim().length === 0 || String(row.email).trim().length === 0)

if (errorRows.length > 0) {
setError("addContacts", { message: "Contact details include empty fields", type: "required" })
} else {
const errorRows_EmailFormat = rows.filter(row => !/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/.test(String(row.email)))

if (errorRows_EmailFormat.length > 0) {
setError("addContacts", { message: "Contact details include empty fields", type: "email_format" })
} else {
clearErrors("addContacts")
}
}
}
}, [rows])

// check editing
useEffect(() => {
const filteredByKey = Object.fromEntries(
Object.entries(rowModesModel).filter(([key, value]) => rowModesModel[key].mode === 'edit'))

if (Object.keys(filteredByKey).length > 0) {
setValue("isGridEditing", true)
} else {
setValue("isGridEditing", false)
}
}, [rowModesModel])
return (
<Card sx={{ display: "block" }}>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<Stack gap={2}>
{/* <div> */}
<Typography variant="overline" display='inline-block' noWrap>
{t("Contact Details")}
</Typography>
{Boolean(errors.addContacts?.type === "required") && <Typography sx={(theme) => ({ color: theme.palette.error.main })} variant="overline" display='inline-block' noWrap>
{t("Please ensure all the fields are inputted and saved")}
</Typography>}
{Boolean(errors.addContacts?.type === "email_format") && <Typography sx={(theme) => ({ color: theme.palette.error.main })} variant="overline" display='inline-block' noWrap>
{t("Please ensure all the email formats are correct")}
</Typography>}
{/* </div> */}
<CustomDatagrid
rows={[...rows]}
columns={columns}
editMode="row"
rowModesModel={rowModesModel}
onRowEditStop={handleRowEditStop}
processRowUpdate={processRowUpdate}
// onProcessRowUpdateError={handleProcessRowUpdateError}
onRowModesModelChange={handleRowModesModelChange}
slots={{
toolbar: EditToolbar,
}}
slotProps={{
toolbar: { setRows, setRowModesModel },
}}
sx={{
height: '100%'
}}
/>
<CardActions sx={{ justifyContent: "flex-end" }}>
<Button variant="text" startIcon={<RestartAlt />}>
{t("Reset")}
</Button>
</CardActions>
</Stack>
</CardContent>
</Card>
);
};

export default ContactDetails;

+ 51
- 15
src/components/CreateCustomer/CreateCustomer.tsx 查看文件

@@ -9,7 +9,6 @@ import Tabs, { TabsProps } from "@mui/material/Tabs";
import { useRouter } from "next/navigation";
import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { Task, TaskTemplate } from "@/app/api/tasks";
import {
FieldErrors,
FormProvider,
@@ -17,18 +16,18 @@ import {
SubmitHandler,
useForm,
} from "react-hook-form";
import { CreateProjectInputs, saveProject } from "@/app/api/projects/actions";
import { Error } from "@mui/icons-material";
import { ProjectCategory } from "@/app/api/projects";
import { Typography } from "@mui/material";
import { CustomerFormInputs, saveCustomer } from "@/app/api/customer/actions";
import CustomerDetails from "./CustomerDetails";
import SubsidiaryAllocation from "./SubsidiaryAllocation";
import { Subsidiary } from "@/app/api/customer";
import { CustomerType, Subsidiary } from "@/app/api/customer";
import { getDeletedRecordWithRefList } from "@/app/utils/commonUtil";
import { errorDialog, submitDialog, successDialog, warningDialog } from "../Swal/CustomAlerts";

export interface Props {
subsidiaries: Subsidiary[],
customerTypes: CustomerType[],
}

const hasErrorsInTab = (
@@ -37,7 +36,7 @@ const hasErrorsInTab = (
) => {
switch (tabIndex) {
case 0:
return errors.name;
return Object.keys(errors).length > 0;
default:
false;
}
@@ -45,6 +44,7 @@ const hasErrorsInTab = (

const CreateCustomer: React.FC<Props> = ({
subsidiaries,
customerTypes,
}) => {
const [serverError, setServerError] = useState("");
const [tabIndex, setTabIndex] = useState(0);
@@ -54,6 +54,10 @@ const CreateCustomer: React.FC<Props> = ({
defaultValues: {
code: "",
name: "",
addContacts: [],
addSubsidiaryIds: [],
deleteSubsidiaryIds: [],
deleteContactIds: []
},
});

@@ -71,27 +75,42 @@ const CreateCustomer: React.FC<Props> = ({
const onSubmit = useCallback<SubmitHandler<CustomerFormInputs>>(
async (data) => {
try {
if (data.isGridEditing) {
warningDialog(t("Please save all the rows before submitting"), t)
return false
}
console.log(data);

let haveError = false
if (data.name.length === 0) {
haveError = true
formProps.setError("name", {message: "Name is empty", type: "required"})
formProps.setError("name", { message: "Name is empty", type: "required" })
}

if (data.code.length === 0) {
haveError = true
formProps.setError("code", {message: "Code is empty", type: "required"})
formProps.setError("code", { message: "Code is empty", type: "required" })
}

if (data.email && data.email?.length > 0 && !/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/.test(data.email)) {
haveError = true
formProps.setError("email", {message: "Email format is not valid", type: "custom"})
formProps.setError("email", { message: "Email format is not valid", type: "custom" })
}

if (data.brNo && data.brNo?.length > 0 && !/[0-9]{8}/.test(data.brNo)) {
haveError = true
formProps.setError("brNo", {message: "Br No. format is not valid", type: "custom"})
formProps.setError("brNo", { message: "Br No. format is not valid", type: "custom" })
}

if (data.addContacts.length === 0 || data.addContacts.filter(row => String(row.name).trim().length === 0 || String(row.phone).trim().length === 0 || String(row.email).trim().length === 0).length > 0) {
haveError = true
formProps.setError("addContacts", { message: "Contact details include empty fields", type: "required" })
}

if (data.addContacts.length > 0 && data.addContacts.filter(row => !/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/.test(String(row.email))).length > 0) {
haveError = true
formProps.setError("addContacts", { message: "Contact details include empty fields", type: "email_format" })
}

if (haveError) {
@@ -100,11 +119,28 @@ const CreateCustomer: React.FC<Props> = ({
return false
}

data.deleteSubsidiaryIds = []
// data.deleteSubsidiaryIds = data.deleteSubsidiaryIds ?? []
// data.addSubsidiaryIds = data.addSubsidiaryIds ?? []
// data.deleteContactIds = data.deleteContactIds ?? []
setServerError("");
await saveCustomer(data);
router.replace("/customer");

submitDialog(async () => {
const response = await saveCustomer(data);

if (response.message === "Success") {
successDialog(t("Submit Success"), t).then(() => {
router.replace("/settings/customer");
})
} else {
errorDialog(t("Submit Fail"), t).then(() => {
formProps.setError("code", { message: response.message, type: "custom" })
setTabIndex(0)
return false
})
}
}, t)
} catch (e) {
console.log(e)
setServerError(t("An error has occurred. Please try again later."));
}
},
@@ -114,7 +150,7 @@ const CreateCustomer: React.FC<Props> = ({
const onSubmitError = useCallback<SubmitErrorHandler<CustomerFormInputs>>(
(errors) => {
// Set the tab so that the focus will go there
if (errors.name || errors.code) {
if (Object.keys(errors).length > 0) {
setTabIndex(0);
}
},
@@ -147,8 +183,8 @@ const CreateCustomer: React.FC<Props> = ({
{serverError}
</Typography>
)}
{tabIndex === 0 && <CustomerDetails/>}
{tabIndex === 1 && <SubsidiaryAllocation subsidiaries={subsidiaries}/>}
{tabIndex === 0 && <CustomerDetails customerTypes={customerTypes} />}
{tabIndex === 1 && <SubsidiaryAllocation subsidiaries={subsidiaries} />}

<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button


+ 4
- 3
src/components/CreateCustomer/CreateCustomerWrapper.tsx 查看文件

@@ -2,17 +2,18 @@
// import CreateProject from "./CreateProject";
// import { fetchProjectCategories } from "@/app/api/projects";
// import { fetchTeamLeads } from "@/app/api/staff";
import { fetchSubsidiaries } from "@/app/api/customer";
import { fetchCustomerTypes, fetchSubsidiaries } from "@/app/api/customer";
import CreateCustomer from "./CreateCustomer";

const CreateCustomerWrapper: React.FC = async () => {
const [subsidiaries] =
const [subsidiaries, customerTypes] =
await Promise.all([
fetchSubsidiaries(),
fetchCustomerTypes(),
]);

return (
<CreateCustomer subsidiaries={subsidiaries}/>
<CreateCustomer subsidiaries={subsidiaries} customerTypes={customerTypes}/>
);
};



+ 37
- 5
src/components/CreateCustomer/CustomerDetails.tsx 查看文件

@@ -11,22 +11,30 @@ import { useTranslation } from "react-i18next";
import CardActions from "@mui/material/CardActions";
import RestartAlt from "@mui/icons-material/RestartAlt";
import Button from "@mui/material/Button";
import { useFormContext } from "react-hook-form";
import { Controller, useFormContext } from "react-hook-form";
import { CustomerFormInputs } from "@/app/api/customer/actions";
import { CustomerType } from "@/app/api/customer";
import { FormControl, InputLabel, MenuItem, Select } from "@mui/material";
import ContactDetails from "./ContactDetails";

interface Props {
customerTypes: CustomerType[],
}

const CustomerDetails: React.FC<Props> = ({
customerTypes,
}) => {
const { t } = useTranslation();
const {
register,
formState: { errors },
control,
reset
} = useFormContext<CustomerFormInputs>();

return (
<Card sx={{ display: "block"}}>
<>
<Card sx={{ display: "block" }}>
<CardContent component={Stack} spacing={4}>
<Box>
<Typography variant="overline" display="block" marginBlockEnd={1}>
@@ -41,7 +49,7 @@ const CustomerDetails: React.FC<Props> = ({
required: true,
})}
error={Boolean(errors.code)}
helperText={Boolean(errors.code) && t("Please input correct customer code")}
helperText={Boolean(errors.code) && (errors.code?.message ? t(errors.code.message) : t("Please input correct customer code"))}
/>
</Grid>
<Grid item xs={6}>
@@ -69,7 +77,7 @@ const CustomerDetails: React.FC<Props> = ({
{...register("district")}
/>
</Grid>
<Grid item xs={6}>
{/* <Grid item xs={6}>
<TextField
label={t("Customer Email")}
fullWidth
@@ -93,6 +101,28 @@ const CustomerDetails: React.FC<Props> = ({
fullWidth
{...register("contactName")}
/>
</Grid> */}
<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Customer Type")}</InputLabel>
<Controller
defaultValue={customerTypes[0].id}
control={control}
name="typeId"
render={({ field }) => (
<Select label={t("Project Category")} {...field}>
{customerTypes.map((type, index) => (
<MenuItem
key={`${type.id}-${index}`}
value={type.id}
>
{t(type.name)}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
</Grid>
<Grid item xs={6}>
<TextField
@@ -108,12 +138,14 @@ const CustomerDetails: React.FC<Props> = ({
</Grid>
</Box>
<CardActions sx={{ justifyContent: "flex-end" }}>
<Button variant="text" startIcon={<RestartAlt />}>
<Button onClick={() => reset()} variant="text" startIcon={<RestartAlt />}>
{t("Reset")}
</Button>
</CardActions>
</CardContent>
</Card>
<ContactDetails/>
</>
);
};



+ 2
- 2
src/components/NavigationContent/NavigationContent.tsx 查看文件

@@ -99,8 +99,8 @@ const navigationItems: NavigationItem[] = [
{
icon: <Settings />, label: "Setting", path: "",
children: [
{ icon: <GroupIcon />, label: "Customer", path: "/customer" },
{ icon: <Staff />, label: "Staff", path: "/staff" },
{ icon: <GroupIcon />, label: "Customer", path: "/settings/customer" },
{ icon: <Staff />, label: "Staff", path: "/settings/staff" },
{ icon: <Company />, label: "Company", path: "/settings/company" },
{ icon: <Department />, label: "Department", path: "/settings/department" },
{ icon: <Position />, label: "Position", path: "/settings/position" },


+ 45
- 0
src/components/Swal/CustomAlerts.js 查看文件

@@ -1,3 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import Swal from "sweetalert2";

export const msg = (text) => {
@@ -20,3 +22,46 @@ export const msg = (text) => {
export const popup = (text) => {
Swal.fire(text);
};

export const successDialog = (text, t) => {
return Swal.fire({
icon: "success",
title: text,
confirmButtonText: t("Confirm"),
showConfirmButton: true,
})
}

export const errorDialog = (text, t) => {
return Swal.fire({
icon: "error",
title: text,
confirmButtonText: t("Confirm"),
showConfirmButton: true,
})
}

export const warningDialog = (text, t) => {
return Swal.fire({
icon: "warning",
title: text,
confirmButtonText: t("Confirm"),
showConfirmButton: true,
})
}

export const submitDialog = (confirmAction, t) => {
// const { t } = useTranslation("common")
return Swal.fire({
icon: "question",
title: t("Do you want to submit?"),
cancelButtonText: t("Cancel"),
confirmButtonText: t("Submit"),
showCancelButton: true,
showConfirmButton: true,
}).then((result) => {
if (result.isConfirmed) {
confirmAction()
}
})
}

+ 1
- 0
src/i18n/en/common.json 查看文件

@@ -5,5 +5,6 @@
"Search Criteria": "Search Criteria",
"Cancel": "Cancel",
"Confirm": "Confirm",
"Submit": "Submit",
"Reset": "Reset"
}

+ 14
- 0
src/i18n/en/customer.json 查看文件

@@ -11,11 +11,13 @@
"Customer Contact Name": "Client Contact Name",
"Customer Br No.": "Client Br No.",
"Customer Details": "Client Details",
"Customer Type": "Client Type",

"Please input correct customer code": "Please input correct client code",
"Please input correct customer name": "Please input correct client name",
"Please input correct customer email": "Please input correct client email",
"Please input correct customer br no.": "Please input correct client br no.",
"The customer code has already existed": "The customer code has already existed",

"Subsidiary" : "Subsidiary",
"Subsidiary Allocation": "Subsidiary Allocation",
@@ -32,11 +34,23 @@
"Subsidiary Br No.": "Subsidiary Br No.",
"Subsidiary Details": "Subsidiary Details",

"Add Contact Person": "Add Contact Person",
"Contact Details": "Contact Details",
"Contact Name": "Contact Name",
"Contact Email": "Contact Email",
"Contact Phone": "Contact Phone",
"Please ensure all the fields are inputted and saved": "Please ensure all the fields are inputted and saved",
"Please ensure all the email formats are correct": "Please ensure all the email formats are correct",

"Do you want to submit?": "Do you want to submit?",
"Submit Success": "Submit Success",

"Add": "Add",
"Details": "Details",
"Search": "Search",
"Search Criteria": "Search Criteria",
"Cancel": "Cancel",
"Confirm": "Confirm",
"Submit": "Submit",
"Reset": "Reset"
}

+ 1
- 0
src/i18n/zh/common.json 查看文件

@@ -3,5 +3,6 @@
"Search Criteria": "搜尋條件",
"Cancel": "取消",
"Confirm": "確認",
"Submit": "提交",
"Reset": "重置"
}

+ 14
- 0
src/i18n/zh/customer.json 查看文件

@@ -11,11 +11,13 @@
"Customer Contact Name": "客戶聯絡名稱",
"Customer Br No.": "客戶商業登記號碼",
"Customer Details": "客戶詳請",
"Customer Type": "客戶類型",

"Please input correct customer code": "請輸入客戶編號",
"Please input correct customer name": "請輸入客戶編號",
"Please input correct customer email": "請輸入正確客戶電郵",
"Please input correct customer br no.": "請輸入正確客戶商業登記號碼",
"The customer code has already existed": "該客戶編號已存在",

"Subsidiary": "子公司",
"Subsidiary Allocation": "子公司分配",
@@ -32,11 +34,23 @@
"Subsidiary Br No.": "子公司商業登記號碼",
"Subsidiary Details": "子公司詳請",

"Add Contact Person": "新增聯絡人",
"Contact Details": "聯絡詳請",
"Contact Name": "聯絡姓名",
"Contact Email": "聯絡電郵",
"Contact Phone": "聯絡電話",
"Please ensure all the fields are inputted and saved": "請確保所有欄位已輸入及儲存",
"Please ensure all the email formats are correct": "請確保所有電郵格式輸入正確",
"Do you want to submit?": "你是否確認要提交?",
"Submit Success": "提交成功",

"Add": "新增",
"Details": "詳請",
"Search": "搜尋",
"Search Criteria": "搜尋條件",
"Cancel": "取消",
"Confirm": "確認",
"Submit": "提交",
"Reset": "重置"
}

Loading…
取消
儲存