From 6fdbe946101652d9058bccf43867d9b8578159cc Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Tue, 30 Apr 2024 18:13:43 +0800 Subject: [PATCH] update tasks (can edit now), staff, subsidiary, customer, user, rename customer, --- .../(main)/settings/customer/create/page.tsx | 4 +- .../(main)/settings/customer/edit/page.tsx | 4 +- src/app/(main)/tasks/create/page.tsx | 5 +- src/app/(main)/tasks/edit/page.tsx | 26 +++ src/app/(main)/tasks/page.tsx | 13 +- src/app/api/tasks/actions.ts | 30 ++- .../CreateTaskTemplate/CreateTaskTemplate.tsx | 191 +++++++++++------- src/components/CustomerDetail/index.ts | 1 - .../ContactInfo.tsx | 0 .../CustomerInfo.tsx | 0 .../CustomerSave.tsx} | 4 +- .../CustomerSaveWrapper.tsx} | 8 +- .../SubsidiaryAllocation.tsx | 0 src/components/CustomerSave/index.ts | 1 + src/components/StaffSearch/StaffSearch.tsx | 2 +- .../SubsidiaryDetailWrapper.tsx | 4 +- .../SubsidiarySearch/SubsidiarySearch.tsx | 2 +- .../TaskTemplateSearch/TaskTemplateSearch.tsx | 28 ++- src/components/TeamSearch/TeamSearch.tsx | 2 +- src/components/TransferList/TransferList.tsx | 2 +- src/components/UserSearch/UserSearch.tsx | 2 +- src/i18n/en/common.json | 2 + src/i18n/en/tasks.json | 27 +++ src/i18n/zh/common.json | 2 + src/i18n/zh/tasks.json | 27 +++ 25 files changed, 291 insertions(+), 96 deletions(-) create mode 100644 src/app/(main)/tasks/edit/page.tsx delete mode 100644 src/components/CustomerDetail/index.ts rename src/components/{CustomerDetail => CustomerSave}/ContactInfo.tsx (100%) rename src/components/{CustomerDetail => CustomerSave}/CustomerInfo.tsx (100%) rename src/components/{CustomerDetail/CustomerDetail.tsx => CustomerSave/CustomerSave.tsx} (99%) rename src/components/{CustomerDetail/CustomerDetailWrapper.tsx => CustomerSave/CustomerSaveWrapper.tsx} (73%) rename src/components/{CustomerDetail => CustomerSave}/SubsidiaryAllocation.tsx (100%) create mode 100644 src/components/CustomerSave/index.ts create mode 100644 src/i18n/en/tasks.json create mode 100644 src/i18n/zh/tasks.json diff --git a/src/app/(main)/settings/customer/create/page.tsx b/src/app/(main)/settings/customer/create/page.tsx index e0dc0e0..13b38eb 100644 --- a/src/app/(main)/settings/customer/create/page.tsx +++ b/src/app/(main)/settings/customer/create/page.tsx @@ -1,4 +1,4 @@ -import CustomerDetail from "@/components/CustomerDetail"; +import CustomerSave from "@/components/CustomerSave"; // import { preloadAllTasks } from "@/app/api/tasks"; import CreateTaskTemplate from "@/components/CreateTaskTemplate"; import { I18nProvider, getServerI18n } from "@/i18n"; @@ -16,7 +16,7 @@ const CreateCustomer: React.FC = async () => { <> {t("Create Customer")} - + ); diff --git a/src/app/(main)/settings/customer/edit/page.tsx b/src/app/(main)/settings/customer/edit/page.tsx index 004781b..7755403 100644 --- a/src/app/(main)/settings/customer/edit/page.tsx +++ b/src/app/(main)/settings/customer/edit/page.tsx @@ -1,5 +1,5 @@ import { fetchAllSubsidiaries, preloadAllCustomers } from "@/app/api/customer"; -import CustomerDetail from "@/components/CustomerDetail"; +import CustomerSave from "@/components/CustomerSave"; // import { preloadAllTasks } from "@/app/api/tasks"; import CreateTaskTemplate from "@/components/CreateTaskTemplate"; import { I18nProvider, getServerI18n } from "@/i18n"; @@ -18,7 +18,7 @@ const EditCustomer: React.FC = async () => { <> {t("Edit Customer")} - + ); diff --git a/src/app/(main)/tasks/create/page.tsx b/src/app/(main)/tasks/create/page.tsx index 656139f..262f624 100644 --- a/src/app/(main)/tasks/create/page.tsx +++ b/src/app/(main)/tasks/create/page.tsx @@ -3,6 +3,7 @@ import CreateTaskTemplate from "@/components/CreateTaskTemplate"; import { getServerI18n } from "@/i18n"; import Typography from "@mui/material/Typography"; import { Metadata } from "next"; +import { I18nProvider } from "@/i18n"; export const metadata: Metadata = { title: "Create Task Template", @@ -15,7 +16,9 @@ const Projects: React.FC = async () => { return ( <> {t("Create Task Template")} - + + + ); }; diff --git a/src/app/(main)/tasks/edit/page.tsx b/src/app/(main)/tasks/edit/page.tsx new file mode 100644 index 0000000..2b2c02c --- /dev/null +++ b/src/app/(main)/tasks/edit/page.tsx @@ -0,0 +1,26 @@ +import { preloadAllTasks } from "@/app/api/tasks"; +import CreateTaskTemplate from "@/components/CreateTaskTemplate"; +import { getServerI18n } from "@/i18n"; +import Typography from "@mui/material/Typography"; +import { Metadata } from "next"; +import { I18nProvider } from "@/i18n"; + +export const metadata: Metadata = { + title: "Edit Task Template", +}; + +const TaskTemplates: React.FC = async () => { + const { t } = await getServerI18n("tasks"); + preloadAllTasks(); + + return ( + <> + {t("Edit Task Template")} + + + + + ); +}; + +export default TaskTemplates; diff --git a/src/app/(main)/tasks/page.tsx b/src/app/(main)/tasks/page.tsx index b9e9bf8..bf06dc2 100644 --- a/src/app/(main)/tasks/page.tsx +++ b/src/app/(main)/tasks/page.tsx @@ -8,13 +8,14 @@ import Typography from "@mui/material/Typography"; import { Metadata } from "next"; import Link from "next/link"; import { Suspense } from "react"; +import { I18nProvider } from "@/i18n"; export const metadata: Metadata = { title: "Tasks", }; const TaskTemplates: React.FC = async () => { - const { t } = await getServerI18n("projects"); + const { t } = await getServerI18n("tasks"); preloadTaskTemplates(); return ( @@ -34,12 +35,14 @@ const TaskTemplates: React.FC = async () => { LinkComponent={Link} href="/tasks/create" > - {t("Create Template")} + {t("Create Task Template")} - }> - - + + }> + + + ); }; diff --git a/src/app/api/tasks/actions.ts b/src/app/api/tasks/actions.ts index 862cc62..2c043be 100644 --- a/src/app/api/tasks/actions.ts +++ b/src/app/api/tasks/actions.ts @@ -1,6 +1,6 @@ "use server"; -import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; import { BASE_API_URL } from "@/config/api"; import { TaskTemplate } from "."; import { revalidateTag } from "next/cache"; @@ -9,11 +9,13 @@ export interface NewTaskTemplateFormInputs { code: string; name: string; taskIds: number[]; + + id: number | null; } export const saveTaskTemplate = async (data: NewTaskTemplateFormInputs) => { const newTaskTemplate = await serverFetchJson( - `${BASE_API_URL}/tasks/templates/new`, + `${BASE_API_URL}/tasks/templates/save`, { method: "POST", body: JSON.stringify(data), @@ -25,3 +27,27 @@ export const saveTaskTemplate = async (data: NewTaskTemplateFormInputs) => { return newTaskTemplate; }; + +export const fetchTaskTemplate = async (id: number) => { + const taskTemplate = await serverFetchJson( + `${BASE_API_URL}/tasks/templates/${id}`, + { + method: "GET", + headers: { "Content-Type": "application/json" }, + }, + ); + + return taskTemplate; +}; + +export const deleteTaskTemplate = async (id: number) => { + const taskTemplate = await serverFetchWithNoContent( + `${BASE_API_URL}/tasks/templates/${id}`, + { + method: "DELETE", + headers: { "Content-Type": "application/json" }, + }, + ); + + return taskTemplate +}; diff --git a/src/components/CreateTaskTemplate/CreateTaskTemplate.tsx b/src/components/CreateTaskTemplate/CreateTaskTemplate.tsx index 066c994..f7d5912 100644 --- a/src/components/CreateTaskTemplate/CreateTaskTemplate.tsx +++ b/src/components/CreateTaskTemplate/CreateTaskTemplate.tsx @@ -10,15 +10,17 @@ import TransferList from "../TransferList"; import Button from "@mui/material/Button"; import Check from "@mui/icons-material/Check"; import Close from "@mui/icons-material/Close"; -import { useRouter } from "next/navigation"; +import { useRouter, useSearchParams } from "next/navigation"; import React from "react"; import Stack from "@mui/material/Stack"; import { Task } from "@/app/api/tasks"; import { NewTaskTemplateFormInputs, + fetchTaskTemplate, saveTaskTemplate, } from "@/app/api/tasks/actions"; -import { SubmitHandler, useForm } from "react-hook-form"; +import { SubmitHandler, useFieldArray, useForm } from "react-hook-form"; +import { errorDialog, submitDialog, successDialog } from "../Swal/CustomAlerts"; interface Props { tasks: Task[]; @@ -27,6 +29,7 @@ interface Props { const CreateTaskTemplate: React.FC = ({ tasks }) => { const { t } = useTranslation(); + const searchParams = useSearchParams() const router = useRouter(); const handleCancel = () => { router.back(); @@ -49,6 +52,7 @@ const CreateTaskTemplate: React.FC = ({ tasks }) => { handleSubmit, setValue, watch, + resetField, formState: { errors, isSubmitting }, } = useForm({ defaultValues: { taskIds: [] } }); @@ -57,12 +61,56 @@ const CreateTaskTemplate: React.FC = ({ tasks }) => { return items.filter((item) => currentTaskIds.includes(item.id)); }, [currentTaskIds, items]); + const [refTaskTemplate, setRefTaskTemplate] = React.useState() + const id = searchParams.get('id') + + const fetchCurrentTaskTemplate = async () => { + try { + const taskTemplate = await fetchTaskTemplate(parseInt(id!!)) + + const defaultValues = { + id: parseInt(id!!), + code: taskTemplate.code ?? null, + name: taskTemplate.name ?? null, + taskIds: taskTemplate.tasks.map(task => task.id) ?? [], + } + + setRefTaskTemplate(defaultValues) + } catch (e) { + console.log(e) + } + } + + React.useLayoutEffect(() => { + if (id !== null && parseInt(id) > 0) fetchCurrentTaskTemplate() + }, [id]) + + React.useEffect(() => { + if (refTaskTemplate) { + setValue("taskIds", refTaskTemplate.taskIds) + resetField("code", { defaultValue: refTaskTemplate.code }) + resetField("name", { defaultValue: refTaskTemplate.name }) + setValue("id", refTaskTemplate.id) + } + }, [refTaskTemplate]) + const onSubmit: SubmitHandler = React.useCallback( async (data) => { try { setServerError(""); - await saveTaskTemplate(data); - router.replace("/tasks"); + submitDialog(async () => { + const response = await saveTaskTemplate(data); + + if (response?.id !== null && response?.id !== undefined && response?.id > 0) { + successDialog(t("Submit Success"), t).then(() => { + router.replace("/tasks"); + }) + } else { + errorDialog(t("Submit Fail"), t).then(() => { + return false + }) + } + }, t) } catch (e) { setServerError(t("An error has occurred. Please try again later.")); } @@ -71,72 +119,77 @@ const CreateTaskTemplate: React.FC = ({ tasks }) => { ); return ( - - - - {t("Task List Setup")} - - - - - - + { + (id === null || refTaskTemplate !== undefined) && + + + {t("Task List Setup")} + + + + + + + + + { + setValue( + "taskIds", + selectedTasks.map((item) => item.id), + ); + }} + allItemsLabel={t("Task Pool")} + selectedItemsLabel={t("Task List Template")} /> - - - { - setValue( - "taskIds", - selectedTasks.map((item) => item.id), - ); - }} - allItemsLabel={t("Task Pool")} - selectedItemsLabel={t("Task List Template")} - /> - - - {serverError && ( - - {serverError} - - )} - - - - - + + + { + serverError && ( + + {serverError} + + ) + } + + + + + } + ); }; diff --git a/src/components/CustomerDetail/index.ts b/src/components/CustomerDetail/index.ts deleted file mode 100644 index a8811e6..0000000 --- a/src/components/CustomerDetail/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./CustomerDetailWrapper"; \ No newline at end of file diff --git a/src/components/CustomerDetail/ContactInfo.tsx b/src/components/CustomerSave/ContactInfo.tsx similarity index 100% rename from src/components/CustomerDetail/ContactInfo.tsx rename to src/components/CustomerSave/ContactInfo.tsx diff --git a/src/components/CustomerDetail/CustomerInfo.tsx b/src/components/CustomerSave/CustomerInfo.tsx similarity index 100% rename from src/components/CustomerDetail/CustomerInfo.tsx rename to src/components/CustomerSave/CustomerInfo.tsx diff --git a/src/components/CustomerDetail/CustomerDetail.tsx b/src/components/CustomerSave/CustomerSave.tsx similarity index 99% rename from src/components/CustomerDetail/CustomerDetail.tsx rename to src/components/CustomerSave/CustomerSave.tsx index 88a99ad..fc2469e 100644 --- a/src/components/CustomerDetail/CustomerDetail.tsx +++ b/src/components/CustomerSave/CustomerSave.tsx @@ -42,7 +42,7 @@ const hasErrorsInTab = ( } }; -const CustomerDetail: React.FC = ({ +const CustomerSave: React.FC = ({ subsidiaries, customerTypes, }) => { @@ -277,4 +277,4 @@ const CustomerDetail: React.FC = ({ ); }; -export default CustomerDetail; \ No newline at end of file +export default CustomerSave; \ No newline at end of file diff --git a/src/components/CustomerDetail/CustomerDetailWrapper.tsx b/src/components/CustomerSave/CustomerSaveWrapper.tsx similarity index 73% rename from src/components/CustomerDetail/CustomerDetailWrapper.tsx rename to src/components/CustomerSave/CustomerSaveWrapper.tsx index 0206940..078f50a 100644 --- a/src/components/CustomerDetail/CustomerDetailWrapper.tsx +++ b/src/components/CustomerSave/CustomerSaveWrapper.tsx @@ -3,7 +3,7 @@ // import { fetchProjectCategories } from "@/app/api/projects"; // import { fetchTeamLeads } from "@/app/api/staff"; import { fetchCustomerTypes, fetchAllSubsidiaries } from "@/app/api/customer"; -import CustomerDetail from "./CustomerDetail"; +import CustomerSave from "./CustomerSave"; // type Props = { // params: { @@ -11,7 +11,7 @@ import CustomerDetail from "./CustomerDetail"; // }; // }; -const CustomerDetailWrapper: React.FC = async () => { +const CustomerSaveWrapper: React.FC = async () => { // const { params } = props // console.log(params) const [subsidiaries, customerTypes] = @@ -21,8 +21,8 @@ const CustomerDetailWrapper: React.FC = async () => { ]); return ( - + ); }; -export default CustomerDetailWrapper; +export default CustomerSaveWrapper; diff --git a/src/components/CustomerDetail/SubsidiaryAllocation.tsx b/src/components/CustomerSave/SubsidiaryAllocation.tsx similarity index 100% rename from src/components/CustomerDetail/SubsidiaryAllocation.tsx rename to src/components/CustomerSave/SubsidiaryAllocation.tsx diff --git a/src/components/CustomerSave/index.ts b/src/components/CustomerSave/index.ts new file mode 100644 index 0000000..ea74d25 --- /dev/null +++ b/src/components/CustomerSave/index.ts @@ -0,0 +1 @@ +export { default } from "./CustomerSaveWrapper"; \ No newline at end of file diff --git a/src/components/StaffSearch/StaffSearch.tsx b/src/components/StaffSearch/StaffSearch.tsx index fc6204d..4111d14 100644 --- a/src/components/StaffSearch/StaffSearch.tsx +++ b/src/components/StaffSearch/StaffSearch.tsx @@ -68,7 +68,7 @@ const StaffSearch: React.FC = ({ staff }) => { const deleteClick = useCallback((staff: StaffResult) => { deleteDialog(async () => { await deleteStaff(staff.id); - successDialog("Delete Success", t); + successDialog(t("Delete Success"), t); setFilteredStaff((prev) => prev.filter((obj) => obj.id !== staff.id)); }, t); }, []); diff --git a/src/components/SubsidiaryDetail/SubsidiaryDetailWrapper.tsx b/src/components/SubsidiaryDetail/SubsidiaryDetailWrapper.tsx index 1a9ced0..c335042 100644 --- a/src/components/SubsidiaryDetail/SubsidiaryDetailWrapper.tsx +++ b/src/components/SubsidiaryDetail/SubsidiaryDetailWrapper.tsx @@ -1,7 +1,7 @@ import { fetchAllCustomers, fetchSubsidiaryTypes } from "@/app/api/subsidiary"; import SubsidiaryDetail from "./SubsidiaryDetail"; -const CustomerDetailWrapper: React.FC = async () => { +const CustomerSaveWrapper: React.FC = async () => { const [customers, subsidiaryTypes] = await Promise.all([ fetchAllCustomers(), @@ -13,4 +13,4 @@ const CustomerDetailWrapper: React.FC = async () => { ); }; -export default CustomerDetailWrapper; +export default CustomerSaveWrapper; diff --git a/src/components/SubsidiarySearch/SubsidiarySearch.tsx b/src/components/SubsidiarySearch/SubsidiarySearch.tsx index 95c901f..c4e1db5 100644 --- a/src/components/SubsidiarySearch/SubsidiarySearch.tsx +++ b/src/components/SubsidiarySearch/SubsidiarySearch.tsx @@ -46,7 +46,7 @@ const SubsidiarySearch: React.FC = ({ subsidiaries }) => { deleteDialog(async() => { await deleteSubsidiary(subsidiary.id) - successDialog("Delete Success", t) + successDialog(t("Delete Success"), t) setFilteredSubsidiaries((prev) => prev.filter((obj) => obj.id !== subsidiary.id)) }, t) diff --git a/src/components/TaskTemplateSearch/TaskTemplateSearch.tsx b/src/components/TaskTemplateSearch/TaskTemplateSearch.tsx index 72be3c1..7563f53 100644 --- a/src/components/TaskTemplateSearch/TaskTemplateSearch.tsx +++ b/src/components/TaskTemplateSearch/TaskTemplateSearch.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 { useRouter, useSearchParams } from "next/navigation"; +import DeleteIcon from '@mui/icons-material/Delete'; +import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; +import { deleteTaskTemplate } from "@/app/api/tasks/actions"; interface Props { taskTemplates: TaskTemplate[]; @@ -16,6 +20,8 @@ type SearchParamNames = keyof SearchQuery; const TaskTemplateSearch: React.FC = ({ taskTemplates }) => { const { t } = useTranslation("tasks"); + const searchParams = useSearchParams() + const router = useRouter() const [filteredTemplates, setFilteredTemplates] = useState(taskTemplates); const searchCriteria: Criterion[] = useMemo( @@ -30,7 +36,20 @@ const TaskTemplateSearch: React.FC = ({ taskTemplates }) => { }, [taskTemplates]); const onTaskClick = useCallback((taskTemplate: TaskTemplate) => { - console.log(taskTemplate); + const params = new URLSearchParams(searchParams.toString()) + params.set("id", taskTemplate.id.toString()) + router.replace(`/tasks/edit?${params.toString()}`); + }, []); + + const onDeleteClick = useCallback((taskTemplate: TaskTemplate) => { + + deleteDialog(async () => { + await deleteTaskTemplate(taskTemplate.id) + + successDialog(t("Delete Success"), t) + + setFilteredTemplates((prev) => prev.filter((obj) => obj.id !== taskTemplate.id)) + }, t) }, []); const columns = useMemo[]>( @@ -43,6 +62,13 @@ const TaskTemplateSearch: React.FC = ({ taskTemplates }) => { }, { name: "code", label: t("Task Template Code") }, { name: "name", label: t("Task Template Name") }, + { + name: "id", + label: t("Delete"), + onClick: onDeleteClick, + buttonIcon: , + color: "error" + }, ], [onTaskClick, t], ); diff --git a/src/components/TeamSearch/TeamSearch.tsx b/src/components/TeamSearch/TeamSearch.tsx index a1db872..71ecb79 100644 --- a/src/components/TeamSearch/TeamSearch.tsx +++ b/src/components/TeamSearch/TeamSearch.tsx @@ -56,7 +56,7 @@ const TeamSearch: React.FC = ({ team }) => { deleteDialog(async () => { await deleteTeam(team.id); - successDialog("Delete Success", t); + successDialog(t("Delete Success"), t); setFilteredTeam((prev) => prev.filter((obj) => obj.id !== team.id)); }, t); diff --git a/src/components/TransferList/TransferList.tsx b/src/components/TransferList/TransferList.tsx index 84cd468..232c486 100644 --- a/src/components/TransferList/TransferList.tsx +++ b/src/components/TransferList/TransferList.tsx @@ -109,7 +109,7 @@ const ItemList: React.FC = ({ {label} - {`${checkedItems.length}/${items.length} selected`} + {`${checkedItems.length}/${items.length} ${t("selected")}`} diff --git a/src/components/UserSearch/UserSearch.tsx b/src/components/UserSearch/UserSearch.tsx index 095c544..658d25c 100644 --- a/src/components/UserSearch/UserSearch.tsx +++ b/src/components/UserSearch/UserSearch.tsx @@ -45,7 +45,7 @@ const UserSearch: React.FC = ({ users }) => { deleteDialog(async () => { await deleteUser(users.id); - successDialog("Delete Success", t); + successDialog(t("Delete Success"), t); setFilteredUser((prev) => prev.filter((obj) => obj.id !== users.id)); }, t); diff --git a/src/i18n/en/common.json b/src/i18n/en/common.json index 5f1d289..a7d019a 100644 --- a/src/i18n/en/common.json +++ b/src/i18n/en/common.json @@ -17,6 +17,8 @@ "Do you want to delete?": "Do you want to delete", "Delete Success": "Delete Success", + "Details": "Details", + "Delete": "Delete", "Search": "Search", "Search Criteria": "Search Criteria", "Cancel": "Cancel", diff --git a/src/i18n/en/tasks.json b/src/i18n/en/tasks.json new file mode 100644 index 0000000..d70d00a --- /dev/null +++ b/src/i18n/en/tasks.json @@ -0,0 +1,27 @@ +{ + "Task Template": "Task Template", + "Create Task Template": "Create Task Template", + "Edit Task Template": "Edit Task Template", + + "Task Template Code": "Task Template Code", + "Task Template Name": "Task Template Name", + "Task List Setup": "Task List Setup", + "Task Pool": "Task Pool", + "Task List Template": "Task List Template", + + "Task template code is required": "Task template code is required", + "Task template name is required": "Task template name is required", + + "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", + + "selected": "selected", + "Details": "Details", + "Delete": "Delete", + "Cancel": "Cancel", + "Submit": "Submit", + "Confirm": "Confirm" +} \ No newline at end of file diff --git a/src/i18n/zh/common.json b/src/i18n/zh/common.json index e4642ea..4ff2fcf 100644 --- a/src/i18n/zh/common.json +++ b/src/i18n/zh/common.json @@ -15,6 +15,8 @@ "Do you want to delete?": "你是否確認要刪除?", "Delete Success": "刪除成功", + "Details": "詳情", + "Delete": "刪除", "Search": "搜尋", "Search Criteria": "搜尋條件", "Cancel": "取消", diff --git a/src/i18n/zh/tasks.json b/src/i18n/zh/tasks.json new file mode 100644 index 0000000..16ba727 --- /dev/null +++ b/src/i18n/zh/tasks.json @@ -0,0 +1,27 @@ +{ + "Task Template": "工作範本", + "Create Task Template": "建立工作範本", + "Edit Task Template": "編輯工作範本", + + "Task Template Code": "工作範本編號", + "Task Template Name": "工作範本名稱", + "Task List Setup": "工作名單設置", + "Task Pool": "所有工作", + "Task List Template": "工作名單範本", + + "Task template code is required": "需要工作範本編號", + "Task template name is required": "需要工作範本名稱", + + "Do you want to submit?": "你是否確認要提交?", + "Submit Success": "提交成功", + "Submit Fail": "提交失敗", + "Do you want to delete?": "你是否確認要刪除?", + "Delete Success": "刪除成功", + + "selected": "已選擇", + "Details": "詳情", + "Delete": "刪除", + "Cancel": "取消", + "Submit": "提交", + "Confirm": "確認" +} \ No newline at end of file