Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

197 строки
6.0 KiB

  1. "use client";
  2. import Card from "@mui/material/Card";
  3. import CardContent from "@mui/material/CardContent";
  4. import Grid from "@mui/material/Grid";
  5. import TextField from "@mui/material/TextField";
  6. import Typography from "@mui/material/Typography";
  7. import { useTranslation } from "react-i18next";
  8. import TransferList from "../TransferList";
  9. import Button from "@mui/material/Button";
  10. import Check from "@mui/icons-material/Check";
  11. import Close from "@mui/icons-material/Close";
  12. import { useRouter, useSearchParams } from "next/navigation";
  13. import React from "react";
  14. import Stack from "@mui/material/Stack";
  15. import { Task } from "@/app/api/tasks";
  16. import {
  17. NewTaskTemplateFormInputs,
  18. fetchTaskTemplate,
  19. saveTaskTemplate,
  20. } from "@/app/api/tasks/actions";
  21. import { SubmitHandler, useFieldArray, useForm } from "react-hook-form";
  22. import { errorDialog, submitDialog, successDialog } from "../Swal/CustomAlerts";
  23. interface Props {
  24. tasks: Task[];
  25. }
  26. const CreateTaskTemplate: React.FC<Props> = ({ tasks }) => {
  27. const { t } = useTranslation();
  28. const searchParams = useSearchParams()
  29. const router = useRouter();
  30. const handleCancel = () => {
  31. router.back();
  32. };
  33. const items = React.useMemo(
  34. () =>
  35. tasks.map((task) => ({
  36. id: task.id,
  37. label: task.name,
  38. group: task.taskGroup || undefined,
  39. })),
  40. [tasks],
  41. );
  42. const [serverError, setServerError] = React.useState("");
  43. const {
  44. register,
  45. handleSubmit,
  46. setValue,
  47. watch,
  48. resetField,
  49. formState: { errors, isSubmitting },
  50. } = useForm<NewTaskTemplateFormInputs>({ defaultValues: { taskIds: [] } });
  51. const currentTaskIds = watch("taskIds");
  52. const selectedItems = React.useMemo(() => {
  53. return items.filter((item) => currentTaskIds.includes(item.id));
  54. }, [currentTaskIds, items]);
  55. const [refTaskTemplate, setRefTaskTemplate] = React.useState<NewTaskTemplateFormInputs>()
  56. const id = searchParams.get('id')
  57. const fetchCurrentTaskTemplate = async () => {
  58. try {
  59. const taskTemplate = await fetchTaskTemplate(parseInt(id!!))
  60. const defaultValues = {
  61. id: parseInt(id!!),
  62. code: taskTemplate.code ?? null,
  63. name: taskTemplate.name ?? null,
  64. taskIds: taskTemplate.tasks.map(task => task.id) ?? [],
  65. }
  66. setRefTaskTemplate(defaultValues)
  67. } catch (e) {
  68. console.log(e)
  69. }
  70. }
  71. React.useLayoutEffect(() => {
  72. if (id !== null && parseInt(id) > 0) fetchCurrentTaskTemplate()
  73. }, [id])
  74. React.useEffect(() => {
  75. if (refTaskTemplate) {
  76. setValue("taskIds", refTaskTemplate.taskIds)
  77. resetField("code", { defaultValue: refTaskTemplate.code })
  78. resetField("name", { defaultValue: refTaskTemplate.name })
  79. setValue("id", refTaskTemplate.id)
  80. }
  81. }, [refTaskTemplate])
  82. const onSubmit: SubmitHandler<NewTaskTemplateFormInputs> = React.useCallback(
  83. async (data) => {
  84. try {
  85. setServerError("");
  86. submitDialog(async () => {
  87. const response = await saveTaskTemplate(data);
  88. if (response?.id !== null && response?.id !== undefined && response?.id > 0) {
  89. successDialog(t("Submit Success"), t).then(() => {
  90. router.replace("/tasks");
  91. })
  92. } else {
  93. errorDialog(t("Submit Fail"), t).then(() => {
  94. return false
  95. })
  96. }
  97. }, t)
  98. } catch (e) {
  99. setServerError(t("An error has occurred. Please try again later."));
  100. }
  101. },
  102. [router, t],
  103. );
  104. return (
  105. <>
  106. {
  107. (id === null || refTaskTemplate !== undefined) && <Stack component="form" onSubmit={handleSubmit(onSubmit)} gap={2}>
  108. <Card>
  109. <CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
  110. <Typography variant="overline">{t("Task List Setup")}</Typography>
  111. <Grid
  112. container
  113. spacing={2}
  114. columns={{ xs: 6, sm: 12 }}
  115. marginBlockEnd={1}
  116. >
  117. <Grid item xs={6}>
  118. <TextField
  119. label={t("Task Template Code")}
  120. fullWidth
  121. {...register("code", {
  122. required: t("Task template code is required"),
  123. })}
  124. error={Boolean(errors.code?.message)}
  125. helperText={errors.code?.message}
  126. />
  127. </Grid>
  128. <Grid item xs={6}>
  129. <TextField
  130. label={t("Task Template Name")}
  131. fullWidth
  132. {...register("name", {
  133. required: t("Task template name is required"),
  134. })}
  135. error={Boolean(errors.name?.message)}
  136. helperText={errors.name?.message}
  137. />
  138. </Grid>
  139. </Grid>
  140. <TransferList
  141. allItems={items}
  142. selectedItems={selectedItems}
  143. onChange={(selectedTasks) => {
  144. setValue(
  145. "taskIds",
  146. selectedTasks.map((item) => item.id),
  147. );
  148. }}
  149. allItemsLabel={t("Task Pool")}
  150. selectedItemsLabel={t("Task List Template")}
  151. />
  152. </CardContent>
  153. </Card>
  154. {
  155. serverError && (
  156. <Typography variant="body2" color="error" alignSelf="flex-end">
  157. {serverError}
  158. </Typography>
  159. )
  160. }
  161. <Stack direction="row" justifyContent="flex-end" gap={1}>
  162. <Button variant="outlined" startIcon={<Close />} onClick={handleCancel}>
  163. {t("Cancel")}
  164. </Button>
  165. <Button
  166. variant="contained"
  167. startIcon={<Check />}
  168. type="submit"
  169. disabled={isSubmitting}
  170. >
  171. {t("Confirm")}
  172. </Button>
  173. </Stack>
  174. </Stack >}
  175. </>
  176. );
  177. };
  178. export default CreateTaskTemplate;