| @@ -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 () => { | |||
| <> | |||
| <Typography variant="h4">{t("Create Customer")}</Typography> | |||
| <I18nProvider namespaces={["customer", "common"]}> | |||
| <CustomerDetail /> | |||
| <CustomerSave /> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| @@ -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 () => { | |||
| <> | |||
| <Typography variant="h4">{t("Edit Customer")}</Typography> | |||
| <I18nProvider namespaces={["customer", "common"]}> | |||
| <CustomerDetail /> | |||
| <CustomerSave /> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| @@ -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 ( | |||
| <> | |||
| <Typography variant="h4">{t("Create Task Template")}</Typography> | |||
| <CreateTaskTemplate /> | |||
| <I18nProvider namespaces={["tasks", "common"]}> | |||
| <CreateTaskTemplate /> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -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 ( | |||
| <> | |||
| <Typography variant="h4">{t("Edit Task Template")}</Typography> | |||
| <I18nProvider namespaces={["tasks", "common"]}> | |||
| <CreateTaskTemplate /> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| }; | |||
| export default TaskTemplates; | |||
| @@ -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")} | |||
| </Button> | |||
| </Stack> | |||
| <Suspense fallback={<TaskTemplateSearch.Loading />}> | |||
| <TaskTemplateSearch /> | |||
| </Suspense> | |||
| <I18nProvider namespaces={["tasks", "common"]}> | |||
| <Suspense fallback={<TaskTemplateSearch.Loading />}> | |||
| <TaskTemplateSearch /> | |||
| </Suspense> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -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<TaskTemplate>( | |||
| `${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<TaskTemplate>( | |||
| `${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 | |||
| }; | |||
| @@ -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<Props> = ({ tasks }) => { | |||
| const { t } = useTranslation(); | |||
| const searchParams = useSearchParams() | |||
| const router = useRouter(); | |||
| const handleCancel = () => { | |||
| router.back(); | |||
| @@ -49,6 +52,7 @@ const CreateTaskTemplate: React.FC<Props> = ({ tasks }) => { | |||
| handleSubmit, | |||
| setValue, | |||
| watch, | |||
| resetField, | |||
| formState: { errors, isSubmitting }, | |||
| } = useForm<NewTaskTemplateFormInputs>({ defaultValues: { taskIds: [] } }); | |||
| @@ -57,12 +61,56 @@ const CreateTaskTemplate: React.FC<Props> = ({ tasks }) => { | |||
| return items.filter((item) => currentTaskIds.includes(item.id)); | |||
| }, [currentTaskIds, items]); | |||
| const [refTaskTemplate, setRefTaskTemplate] = React.useState<NewTaskTemplateFormInputs>() | |||
| 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<NewTaskTemplateFormInputs> = 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<Props> = ({ tasks }) => { | |||
| ); | |||
| return ( | |||
| <Stack component="form" onSubmit={handleSubmit(onSubmit)} gap={2}> | |||
| <Card> | |||
| <CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}> | |||
| <Typography variant="overline">{t("Task List Setup")}</Typography> | |||
| <Grid | |||
| container | |||
| spacing={2} | |||
| columns={{ xs: 6, sm: 12 }} | |||
| marginBlockEnd={1} | |||
| > | |||
| <Grid item xs={6}> | |||
| <TextField | |||
| label={t("Task Template Code")} | |||
| fullWidth | |||
| {...register("code", { | |||
| required: t("Task template code is required"), | |||
| })} | |||
| error={Boolean(errors.code?.message)} | |||
| helperText={errors.code?.message} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={6}> | |||
| <TextField | |||
| label={t("Task Template Name")} | |||
| fullWidth | |||
| {...register("name", { | |||
| required: t("Task template name is required"), | |||
| })} | |||
| error={Boolean(errors.name?.message)} | |||
| helperText={errors.name?.message} | |||
| <> | |||
| { | |||
| (id === null || refTaskTemplate !== undefined) && <Stack component="form" onSubmit={handleSubmit(onSubmit)} gap={2}> | |||
| <Card> | |||
| <CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}> | |||
| <Typography variant="overline">{t("Task List Setup")}</Typography> | |||
| <Grid | |||
| container | |||
| spacing={2} | |||
| columns={{ xs: 6, sm: 12 }} | |||
| marginBlockEnd={1} | |||
| > | |||
| <Grid item xs={6}> | |||
| <TextField | |||
| label={t("Task Template Code")} | |||
| fullWidth | |||
| {...register("code", { | |||
| required: t("Task template code is required"), | |||
| })} | |||
| error={Boolean(errors.code?.message)} | |||
| helperText={errors.code?.message} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={6}> | |||
| <TextField | |||
| label={t("Task Template Name")} | |||
| fullWidth | |||
| {...register("name", { | |||
| required: t("Task template name is required"), | |||
| })} | |||
| error={Boolean(errors.name?.message)} | |||
| helperText={errors.name?.message} | |||
| /> | |||
| </Grid> | |||
| </Grid> | |||
| <TransferList | |||
| allItems={items} | |||
| selectedItems={selectedItems} | |||
| onChange={(selectedTasks) => { | |||
| setValue( | |||
| "taskIds", | |||
| selectedTasks.map((item) => item.id), | |||
| ); | |||
| }} | |||
| allItemsLabel={t("Task Pool")} | |||
| selectedItemsLabel={t("Task List Template")} | |||
| /> | |||
| </Grid> | |||
| </Grid> | |||
| <TransferList | |||
| allItems={items} | |||
| selectedItems={selectedItems} | |||
| onChange={(selectedTasks) => { | |||
| setValue( | |||
| "taskIds", | |||
| selectedTasks.map((item) => item.id), | |||
| ); | |||
| }} | |||
| allItemsLabel={t("Task Pool")} | |||
| selectedItemsLabel={t("Task List Template")} | |||
| /> | |||
| </CardContent> | |||
| </Card> | |||
| {serverError && ( | |||
| <Typography variant="body2" color="error" alignSelf="flex-end"> | |||
| {serverError} | |||
| </Typography> | |||
| )} | |||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||
| <Button variant="outlined" startIcon={<Close />} onClick={handleCancel}> | |||
| {t("Cancel")} | |||
| </Button> | |||
| <Button | |||
| variant="contained" | |||
| startIcon={<Check />} | |||
| type="submit" | |||
| disabled={isSubmitting} | |||
| > | |||
| {t("Confirm")} | |||
| </Button> | |||
| </Stack> | |||
| </Stack> | |||
| </CardContent> | |||
| </Card> | |||
| { | |||
| serverError && ( | |||
| <Typography variant="body2" color="error" alignSelf="flex-end"> | |||
| {serverError} | |||
| </Typography> | |||
| ) | |||
| } | |||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||
| <Button variant="outlined" startIcon={<Close />} onClick={handleCancel}> | |||
| {t("Cancel")} | |||
| </Button> | |||
| <Button | |||
| variant="contained" | |||
| startIcon={<Check />} | |||
| type="submit" | |||
| disabled={isSubmitting} | |||
| > | |||
| {t("Confirm")} | |||
| </Button> | |||
| </Stack> | |||
| </Stack >} | |||
| </> | |||
| ); | |||
| }; | |||
| @@ -1 +0,0 @@ | |||
| export { default } from "./CustomerDetailWrapper"; | |||
| @@ -42,7 +42,7 @@ const hasErrorsInTab = ( | |||
| } | |||
| }; | |||
| const CustomerDetail: React.FC<Props> = ({ | |||
| const CustomerSave: React.FC<Props> = ({ | |||
| subsidiaries, | |||
| customerTypes, | |||
| }) => { | |||
| @@ -277,4 +277,4 @@ const CustomerDetail: React.FC<Props> = ({ | |||
| ); | |||
| }; | |||
| export default CustomerDetail; | |||
| export default CustomerSave; | |||
| @@ -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 ( | |||
| <CustomerDetail subsidiaries={subsidiaries} customerTypes={customerTypes} /> | |||
| <CustomerSave subsidiaries={subsidiaries} customerTypes={customerTypes} /> | |||
| ); | |||
| }; | |||
| export default CustomerDetailWrapper; | |||
| export default CustomerSaveWrapper; | |||
| @@ -0,0 +1 @@ | |||
| export { default } from "./CustomerSaveWrapper"; | |||
| @@ -68,7 +68,7 @@ const StaffSearch: React.FC<Props> = ({ 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); | |||
| }, []); | |||
| @@ -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; | |||
| @@ -46,7 +46,7 @@ const SubsidiarySearch: React.FC<Props> = ({ 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) | |||
| @@ -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<Props> = ({ taskTemplates }) => { | |||
| const { t } = useTranslation("tasks"); | |||
| const searchParams = useSearchParams() | |||
| const router = useRouter() | |||
| const [filteredTemplates, setFilteredTemplates] = useState(taskTemplates); | |||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
| @@ -30,7 +36,20 @@ const TaskTemplateSearch: React.FC<Props> = ({ 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<Column<TaskTemplate>[]>( | |||
| @@ -43,6 +62,13 @@ const TaskTemplateSearch: React.FC<Props> = ({ taskTemplates }) => { | |||
| }, | |||
| { name: "code", label: t("Task Template Code") }, | |||
| { name: "name", label: t("Task Template Name") }, | |||
| { | |||
| name: "id", | |||
| label: t("Delete"), | |||
| onClick: onDeleteClick, | |||
| buttonIcon: <DeleteIcon />, | |||
| color: "error" | |||
| }, | |||
| ], | |||
| [onTaskClick, t], | |||
| ); | |||
| @@ -56,7 +56,7 @@ const TeamSearch: React.FC<Props> = ({ 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); | |||
| @@ -109,7 +109,7 @@ const ItemList: React.FC<ItemListProps> = ({ | |||
| </ListItemIcon> | |||
| <Stack> | |||
| <Typography variant="subtitle2">{label}</Typography> | |||
| <Typography variant="caption">{`${checkedItems.length}/${items.length} selected`}</Typography> | |||
| <Typography variant="caption">{`${checkedItems.length}/${items.length} ${t("selected")}`}</Typography> | |||
| </Stack> | |||
| </Stack> | |||
| <Divider /> | |||
| @@ -45,7 +45,7 @@ const UserSearch: React.FC<Props> = ({ 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); | |||
| @@ -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", | |||
| @@ -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" | |||
| } | |||
| @@ -15,6 +15,8 @@ | |||
| "Do you want to delete?": "你是否確認要刪除?", | |||
| "Delete Success": "刪除成功", | |||
| "Details": "詳情", | |||
| "Delete": "刪除", | |||
| "Search": "搜尋", | |||
| "Search Criteria": "搜尋條件", | |||
| "Cancel": "取消", | |||
| @@ -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": "確認" | |||
| } | |||