*/}
- {t("Contact Details")}
+ {t("Contact Info")}
{Boolean(errors.addContacts?.type === "required") &&
({ color: theme.palette.error.main })} variant="overline" display='inline-block' noWrap>
{t("Please ensure all the fields are inputted and saved")}
@@ -275,7 +290,7 @@ const ContactDetails: React.FC = ({
}}
/>
- }>
+ } onClick={resetContact} disabled={Boolean(watch("isGridEditing"))}>
{t("Reset")}
@@ -285,4 +300,4 @@ const ContactDetails: React.FC = ({
);
};
-export default ContactDetails;
\ No newline at end of file
+export default ContactInfo;
\ No newline at end of file
diff --git a/src/components/CreateCustomer/CreateCustomer.tsx b/src/components/CustomerDetail/CustomerDetail.tsx
similarity index 60%
rename from src/components/CreateCustomer/CreateCustomer.tsx
rename to src/components/CustomerDetail/CustomerDetail.tsx
index 02ee033..bfc85b0 100644
--- a/src/components/CreateCustomer/CreateCustomer.tsx
+++ b/src/components/CustomerDetail/CustomerDetail.tsx
@@ -6,8 +6,8 @@ import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import Tab from "@mui/material/Tab";
import Tabs, { TabsProps } from "@mui/material/Tabs";
-import { useRouter } from "next/navigation";
-import React, { useCallback, useState } from "react";
+import { useRouter, useSearchParams } from "next/navigation";
+import React, { useCallback, useEffect, useLayoutEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import {
FieldErrors,
@@ -18,8 +18,8 @@ import {
} from "react-hook-form";
import { Error } from "@mui/icons-material";
import { Typography } from "@mui/material";
-import { CustomerFormInputs, saveCustomer } from "@/app/api/customer/actions";
-import CustomerDetails from "./CustomerDetails";
+import { CustomerFormInputs, fetchCustomer, saveCustomer } from "@/app/api/customer/actions";
+import CustomerInfo from "./CustomerInfo";
import SubsidiaryAllocation from "./SubsidiaryAllocation";
import { CustomerType, Subsidiary } from "@/app/api/customer";
import { getDeletedRecordWithRefList } from "@/app/utils/commonUtil";
@@ -42,25 +42,97 @@ const hasErrorsInTab = (
}
};
-const CreateCustomer: React.FC = ({
+const CustomerDetail: React.FC = ({
subsidiaries,
customerTypes,
}) => {
const [serverError, setServerError] = useState("");
const [tabIndex, setTabIndex] = useState(0);
+ const [refCustomer, setRefCustomer] = useState()
const { t } = useTranslation();
const router = useRouter();
+ const searchParams = useSearchParams()
+
+ const fetchCurrentCustomer = async () => {
+ const id = searchParams.get('id')
+ try {
+ const defaultCustomer = {
+ id: null,
+ code: "",
+ name: "",
+ brNo: null,
+ address: null,
+ district: null,
+ typeId: 1,
+ addContacts: [],
+ addSubsidiaryIds: [],
+ deleteSubsidiaryIds: [],
+ deleteContactIds: [],
+ isGridEditing: false
+ }
+
+ if (id !== null && parseInt(id) > 0) {
+
+ const customer = await fetchCustomer(parseInt(id))
+
+ if (customer !== null && Object.keys(customer).length > 0) {
+ const tempCustomerInput = {
+ id: customer.customer.id,
+ code: customer.customer.code ?? "",
+ name: customer.customer.name ?? "",
+ brNo: customer.customer.brNo ?? "",
+ address: customer.customer.address ?? "",
+ district: customer.customer.district ?? "",
+ typeId: customer.customer.customerType.id,
+ addContacts: customer.contacts ?? [],
+ addSubsidiaryIds: customer.subsidiaryIds ?? [],
+ deleteSubsidiaryIds: [],
+ deleteContactIds: [],
+ isGridEditing: false
+ }
+ setRefCustomer(tempCustomerInput)
+ } else {
+ setRefCustomer(defaultCustomer)
+ }
+ } else {
+ setRefCustomer(defaultCustomer)
+ }
+ } catch (e) {
+ console.log(e)
+ setServerError(t("An error has occurred. Please try again later."));
+ }
+ }
+
+ useLayoutEffect(() => {
+ fetchCurrentCustomer()
+ }, [])
+
const formProps = useForm({
- defaultValues: {
- code: "",
- name: "",
- addContacts: [],
- addSubsidiaryIds: [],
- deleteSubsidiaryIds: [],
- deleteContactIds: []
- },
+ // defaultValues: useMemo(() => {
+ // return refCustomer;
+ // }, [refCustomer])
+ // defaultValues: {
+ // id: null,
+ // code: "",
+ // name: "",
+ // brNo: null,
+ // address: null,
+ // district: null,
+ // typeId: 1,
+ // addContacts: [],
+ // addSubsidiaryIds: [],
+ // deleteSubsidiaryIds: [],
+ // deleteContactIds: [],
+ // isGridEditing: false
+ // }
});
+ useEffect(() => {
+ if (refCustomer !== null && refCustomer !== undefined) {
+ formProps.reset(refCustomer)
+ }
+ }, [refCustomer])
+
const handleCancel = () => {
router.back();
};
@@ -79,7 +151,7 @@ const CreateCustomer: React.FC = ({
warningDialog(t("Please save all the rows before submitting"), t)
return false
}
-
+
console.log(data);
let haveError = false
@@ -93,10 +165,10 @@ const CreateCustomer: React.FC = ({
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" })
- }
+ // 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" })
+ // }
if (data.brNo && data.brNo?.length > 0 && !/[0-9]{8}/.test(data.brNo)) {
haveError = true
@@ -105,12 +177,12 @@ const CreateCustomer: React.FC = ({
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" })
+ formProps.setError("addContacts", { message: "Contact info 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" })
+ formProps.setError("addContacts", { message: "Contact info include empty fields", type: "email_format" })
}
if (haveError) {
@@ -119,9 +191,9 @@ const CreateCustomer: React.FC = ({
return false
}
- // data.deleteSubsidiaryIds = data.deleteSubsidiaryIds ?? []
- // data.addSubsidiaryIds = data.addSubsidiaryIds ?? []
- // data.deleteContactIds = data.deleteContactIds ?? []
+ data.deleteContactIds = getDeletedRecordWithRefList(refCustomer?.addContacts.map(contact => contact.id)!!, data.addContacts.map(contact => contact.id)!!)
+ data.deleteSubsidiaryIds = getDeletedRecordWithRefList(refCustomer?.addSubsidiaryIds!!, data.addSubsidiaryIds)
+
setServerError("");
submitDialog(async () => {
@@ -144,7 +216,7 @@ const CreateCustomer: React.FC = ({
setServerError(t("An error has occurred. Please try again later."));
}
},
- [router, t],
+ [router, t, refCustomer],
);
const onSubmitError = useCallback>(
@@ -161,14 +233,14 @@ const CreateCustomer: React.FC = ({
return (
-
@@ -183,7 +255,7 @@ const CreateCustomer: React.FC = ({
{serverError}
)}
- {tabIndex === 0 &&
}
+ {tabIndex === 0 &&
}
{tabIndex === 1 &&
}
@@ -194,13 +266,13 @@ const CreateCustomer: React.FC = ({
>
{t("Cancel")}
- } type="submit">
+ } type="submit" disabled={Boolean(formProps.watch("isGridEditing"))}>
{t("Confirm")}
-
+ }
);
};
-export default CreateCustomer;
\ No newline at end of file
+export default CustomerDetail;
\ No newline at end of file
diff --git a/src/components/CreateCustomer/CreateCustomerWrapper.tsx b/src/components/CustomerDetail/CustomerDetailWrapper.tsx
similarity index 52%
rename from src/components/CreateCustomer/CreateCustomerWrapper.tsx
rename to src/components/CustomerDetail/CustomerDetailWrapper.tsx
index d4c85b2..0e1b3e1 100644
--- a/src/components/CreateCustomer/CreateCustomerWrapper.tsx
+++ b/src/components/CustomerDetail/CustomerDetailWrapper.tsx
@@ -3,9 +3,18 @@
// import { fetchProjectCategories } from "@/app/api/projects";
// import { fetchTeamLeads } from "@/app/api/staff";
import { fetchCustomerTypes, fetchSubsidiaries } from "@/app/api/customer";
-import CreateCustomer from "./CreateCustomer";
+import CustomerDetail from "./CustomerDetail";
+import { getServerSideProps } from "next/dist/build/templates/pages";
-const CreateCustomerWrapper: React.FC = async () => {
+// type Props = {
+// params: {
+// id: string | undefined;
+// };
+// };
+
+const CustomerDetailWrapper: React.FC = async () => {
+ // const { params } = props
+ // console.log(params)
const [subsidiaries, customerTypes] =
await Promise.all([
fetchSubsidiaries(),
@@ -13,8 +22,8 @@ const CreateCustomerWrapper: React.FC = async () => {
]);
return (
-
+
);
};
-export default CreateCustomerWrapper;
+export default CustomerDetailWrapper;
diff --git a/src/components/CustomerDetail/CustomerInfo.tsx b/src/components/CustomerDetail/CustomerInfo.tsx
new file mode 100644
index 0000000..695f6d5
--- /dev/null
+++ b/src/components/CustomerDetail/CustomerInfo.tsx
@@ -0,0 +1,177 @@
+"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 TextField from "@mui/material/TextField";
+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 { 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 ContactInfo from "./ContactInfo";
+import { useCallback } from "react";
+
+interface Props {
+ customerTypes: CustomerType[],
+}
+
+const CustomerInfo: React.FC = ({
+ customerTypes,
+}) => {
+ const { t } = useTranslation();
+ const {
+ register,
+ formState: { errors, defaultValues },
+ control,
+ reset,
+ resetField,
+ setValue
+ } = useFormContext();
+
+ const resetCustomer = useCallback(() => {
+ console.log(defaultValues)
+ if (defaultValues !== undefined) {
+ resetField("code")
+ resetField("name")
+ resetField("address")
+ resetField("district")
+ resetField("typeId")
+ resetField("brNo")
+
+ // setValue("code", defaultValues.code ?? "")
+ // reset({
+ // code: defaultValues.code,
+ // name: defaultValues.name,
+ // address: defaultValues.address,
+ // district: defaultValues.district,
+ // typeId: defaultValues.typeId,
+ // brNo: defaultValues.brNo
+ // })
+ }
+ }, [defaultValues])
+
+ return (
+ <>
+
+
+
+
+ {t("Customer Info")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/*
+
+
+
+
+
+
+
+ */}
+
+
+ {t("Customer Type")}
+ (
+
+ )}
+ />
+
+
+
+
+
+
+
+
+ }>
+ {t("Reset")}
+
+
+
+
+
+ >
+ );
+};
+
+export default CustomerInfo;
diff --git a/src/components/CreateCustomer/SubsidiaryAllocation.tsx b/src/components/CustomerDetail/SubsidiaryAllocation.tsx
similarity index 86%
rename from src/components/CreateCustomer/SubsidiaryAllocation.tsx
rename to src/components/CustomerDetail/SubsidiaryAllocation.tsx
index 25f35a4..2b54294 100644
--- a/src/components/CreateCustomer/SubsidiaryAllocation.tsx
+++ b/src/components/CustomerDetail/SubsidiaryAllocation.tsx
@@ -12,10 +12,6 @@ import {
TextField,
InputAdornment,
IconButton,
- FormControl,
- InputLabel,
- Select,
- MenuItem,
Box,
Button,
Card,
@@ -24,12 +20,9 @@ import {
TabsProps,
Tab,
Tabs,
- SelectChangeEvent,
} from "@mui/material";
import differenceBy from "lodash/differenceBy";
-import uniq from "lodash/uniq";
import { useFormContext } from "react-hook-form";
-import { CreateProjectInputs } from "@/app/api/projects/actions";
import { CustomerFormInputs } from "@/app/api/customer/actions";
import { Subsidiary } from "@/app/api/customer";
@@ -41,7 +34,7 @@ const SubsidiaryAllocation: React.FC = ({
subsidiaries,
}) => {
const { t } = useTranslation();
- const { setValue, getValues } = useFormContext();
+ const { setValue, getValues, formState: { defaultValues }, reset, resetField } = useFormContext();
const [filteredSubsidiary, setFilteredSubsidiary] = React.useState(subsidiaries);
const [selectedSubsidiary, setSelectedSubsidiary] = React.useState<
@@ -49,19 +42,28 @@ const SubsidiaryAllocation: React.FC = ({
>(
subsidiaries.filter((subsidiary) =>
getValues("addSubsidiaryIds")?.includes(subsidiary.id),
- ),
+ )
);
// Adding / Removing staff
const addSubsidiary = React.useCallback((subsidiary: Subsidiary) => {
setSelectedSubsidiary((subsidiaries) => [...subsidiaries, subsidiary]);
}, []);
+
const removeSubsidiary = React.useCallback((subsidiary: Subsidiary) => {
setSelectedSubsidiary((subsidiaries) => subsidiaries.filter((s) => s.id !== subsidiary.id));
}, []);
+
const clearSubsidiary = React.useCallback(() => {
- setSelectedSubsidiary([]);
- }, []);
+ if (defaultValues !== undefined) {
+ // reset({ addSubsidiaryIds: defaultValues.addSubsidiaryIds })
+ resetField("addSubsidiaryIds")
+ setSelectedSubsidiary(subsidiaries.filter((subsidiary) =>
+ defaultValues.addSubsidiaryIds?.includes(subsidiary.id),
+ ))
+ }
+ }, [defaultValues]);
+
// Sync with form
useEffect(() => {
setValue(
@@ -81,11 +83,11 @@ const SubsidiaryAllocation: React.FC = ({
{ label: t("Subsidiary Code"), name: "code" },
{ label: t("Subsidiary Name"), name: "name" },
{ label: t("Subsidiary Br No."), name: "brNo" },
- { label: t("Subsidiary Contact Name"), name: "contactName" },
- { label: t("Subsidiary Phone"), name: "phone" },
+ // { label: t("Subsidiary Contact Name"), name: "contactName" },
+ // { label: t("Subsidiary Phone"), name: "phone" },
{ label: t("Subsidiary Address"), name: "address" },
{ label: t("Subsidiary District"), name: "district" },
- { label: t("Subsidiary Email"), name: "email" },
+ // { label: t("Subsidiary Email"), name: "email" },
],
[addSubsidiary, t],
);
@@ -101,11 +103,11 @@ const SubsidiaryAllocation: React.FC = ({
{ label: t("Subsidiary Code"), name: "code" },
{ label: t("Subsidiary Name"), name: "name" },
{ label: t("Subsidiary Br No."), name: "brNo" },
- { label: t("Subsidiary Contact Name"), name: "contactName" },
- { label: t("Subsidiary Phone"), name: "phone" },
+ // { label: t("Subsidiary Contact Name"), name: "contactName" },
+ // { label: t("Subsidiary Phone"), name: "phone" },
{ label: t("Subsidiary Address"), name: "address" },
{ label: t("Subsidiary District"), name: "district" },
- { label: t("Subsidiary Email"), name: "email" },
+ // { label: t("Subsidiary Email"), name: "email" },
],
[removeSubsidiary, t],
);
@@ -143,14 +145,14 @@ const SubsidiaryAllocation: React.FC = ({
[],
);
- const reset = React.useCallback(() => {
+ const resetSubsidiary = React.useCallback(() => {
clearQueryInput();
clearSubsidiary();
}, [clearQueryInput, clearSubsidiary]);
return (
<>
-
+
@@ -201,7 +203,7 @@ const SubsidiaryAllocation: React.FC = ({
- } onClick={reset}>
+ } onClick={resetSubsidiary}>
{t("Reset")}
diff --git a/src/components/CustomerDetail/index.ts b/src/components/CustomerDetail/index.ts
new file mode 100644
index 0000000..a8811e6
--- /dev/null
+++ b/src/components/CustomerDetail/index.ts
@@ -0,0 +1 @@
+export { default } from "./CustomerDetailWrapper";
\ No newline at end of file
diff --git a/src/components/CustomerSearch/CustomerSearch.tsx b/src/components/CustomerSearch/CustomerSearch.tsx
index c65075d..d4d1a30 100644
--- a/src/components/CustomerSearch/CustomerSearch.tsx
+++ b/src/components/CustomerSearch/CustomerSearch.tsx
@@ -6,6 +6,10 @@ import SearchBox, { Criterion } from "../SearchBox";
import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults";
import EditNote from "@mui/icons-material/EditNote";
+import DeleteIcon from '@mui/icons-material/Delete';
+import { useRouter, useSearchParams } from "next/navigation";
+import { deleteDialog, successDialog } from "../Swal/CustomAlerts";
+import { deleteCustomer } from "@/app/api/customer/actions";
interface Props {
customers: Customer[];
@@ -16,6 +20,8 @@ type SearchParamNames = keyof SearchQuery;
const CustomerSearch: React.FC = ({ customers }) => {
const { t } = useTranslation();
+ const router = useRouter()
+ const searchParams = useSearchParams()
const [filteredCustomers, setFilteredCustomers] = useState(customers);
const searchCriteria: Criterion[] = useMemo(
@@ -30,7 +36,20 @@ const CustomerSearch: React.FC = ({ customers }) => {
}, [customers]);
const onTaskClick = useCallback((customer: Customer) => {
- console.log(customer);
+ const params = new URLSearchParams(searchParams.toString())
+ params.set("id", customer.id.toString())
+ router.replace(`/settings/customer/edit?${params.toString()}`);
+ }, []);
+
+ const onDeleteClick = useCallback((customer: Customer) => {
+
+ deleteDialog(async() => {
+ await deleteCustomer(customer.id)
+
+ successDialog("Delete Success", t)
+
+ setFilteredCustomers((prev) => prev.filter((obj) => obj.id !== customer.id))
+ }, t)
}, []);
const columns = useMemo[]>(
@@ -43,6 +62,12 @@ const CustomerSearch: React.FC = ({ customers }) => {
},
{ name: "code", label: t("Customer Code") },
{ name: "name", label: t("Customer Name") },
+ {
+ name: "id",
+ label: t("Delete"),
+ onClick: onDeleteClick,
+ buttonIcon: ,
+ },
],
[onTaskClick, t],
);
diff --git a/src/components/Swal/CustomAlerts.js b/src/components/Swal/CustomAlerts.js
index c6154e3..6eddfb2 100644
--- a/src/components/Swal/CustomAlerts.js
+++ b/src/components/Swal/CustomAlerts.js
@@ -50,18 +50,32 @@ export const warningDialog = (text, t) => {
})
}
-export const submitDialog = (confirmAction, t) => {
+export const submitDialog = async (confirmAction, t) => {
// const { t } = useTranslation("common")
- return Swal.fire({
+ const result = await 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()
- }
- })
+ });
+ if (result.isConfirmed) {
+ confirmAction();
+ }
+}
+
+export const deleteDialog = async (confirmAction, t) => {
+ // const { t } = useTranslation("common")
+ const result = await Swal.fire({
+ icon: "question",
+ title: t("Do you want to delete?"),
+ cancelButtonText: t("Cancel"),
+ confirmButtonText: t("Delete"),
+ showCancelButton: true,
+ showConfirmButton: true,
+ });
+ if (result.isConfirmed) {
+ confirmAction();
+ }
}
diff --git a/src/i18n/en/customer.json b/src/i18n/en/customer.json
index 5a740d1..06d7a6e 100644
--- a/src/i18n/en/customer.json
+++ b/src/i18n/en/customer.json
@@ -11,6 +11,7 @@
"Customer Contact Name": "Client Contact Name",
"Customer Br No.": "Client Br No.",
"Customer Details": "Client Details",
+ "Customer Info": "Client Info",
"Customer Type": "Client Type",
"Please input correct customer code": "Please input correct client code",
@@ -33,9 +34,11 @@
"Subsidiary Contact Name": "Subsidiary Contact Name",
"Subsidiary Br No.": "Subsidiary Br No.",
"Subsidiary Details": "Subsidiary Details",
+ "Subsidiary Info": "Subsidiary Info",
"Add Contact Person": "Add Contact Person",
"Contact Details": "Contact Details",
+ "Contact Info": "Contact Info",
"Contact Name": "Contact Name",
"Contact Email": "Contact Email",
"Contact Phone": "Contact Phone",
@@ -44,13 +47,18 @@
"Do you want to submit?": "Do you want to submit?",
"Submit Success": "Submit Success",
+ "Submit Fail": "Submit Fail",
+ "Do you want to delete?": "Do you want to delete",
+ "Delete Success": "Delete Success",
"Add": "Add",
"Details": "Details",
+ "Info": "Info",
"Search": "Search",
"Search Criteria": "Search Criteria",
"Cancel": "Cancel",
"Confirm": "Confirm",
"Submit": "Submit",
- "Reset": "Reset"
+ "Reset": "Reset",
+ "Delete": "Delete"
}
\ No newline at end of file
diff --git a/src/i18n/zh/customer.json b/src/i18n/zh/customer.json
index c39d417..6eb4e0d 100644
--- a/src/i18n/zh/customer.json
+++ b/src/i18n/zh/customer.json
@@ -11,6 +11,7 @@
"Customer Contact Name": "客戶聯絡名稱",
"Customer Br No.": "客戶商業登記號碼",
"Customer Details": "客戶詳請",
+ "Customer Info": "客戶資料",
"Customer Type": "客戶類型",
"Please input correct customer code": "請輸入客戶編號",
@@ -33,9 +34,11 @@
"Subsidiary Contact Name": "子公司聯絡名稱",
"Subsidiary Br No.": "子公司商業登記號碼",
"Subsidiary Details": "子公司詳請",
+ "Subsidiary Info": "子公司資料",
"Add Contact Person": "新增聯絡人",
"Contact Details": "聯絡詳請",
+ "Contact Info": "聯絡資料",
"Contact Name": "聯絡姓名",
"Contact Email": "聯絡電郵",
"Contact Phone": "聯絡電話",
@@ -44,13 +47,18 @@
"Do you want to submit?": "你是否確認要提交?",
"Submit Success": "提交成功",
+ "Submit Fail": "提交失敗",
+ "Do you want to delete?": "你是否確認要刪除?",
+ "Delete Success": "刪除成功",
"Add": "新增",
"Details": "詳請",
+ "Info": "資料",
"Search": "搜尋",
"Search Criteria": "搜尋條件",
"Cancel": "取消",
"Confirm": "確認",
"Submit": "提交",
- "Reset": "重置"
+ "Reset": "重置",
+ "Delete": "刪除"
}
\ No newline at end of file