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

255 строки
6.6 KiB

  1. "use client";
  2. import { useRouter, useSearchParams } from "next/navigation";
  3. import React, {
  4. useCallback,
  5. useEffect,
  6. useLayoutEffect,
  7. useMemo,
  8. useState,
  9. } from "react";
  10. import { useTranslation } from "react-i18next";
  11. import {
  12. Button,
  13. Card,
  14. CardContent,
  15. Grid,
  16. Stack,
  17. Tab,
  18. Tabs,
  19. TabsProps,
  20. TextField,
  21. Typography,
  22. } from "@mui/material";
  23. import {
  24. FieldErrors,
  25. FormProvider,
  26. SubmitErrorHandler,
  27. SubmitHandler,
  28. useForm,
  29. useFormContext,
  30. } from "react-hook-form";
  31. import { Check, Close, Error, RestartAlt } from "@mui/icons-material";
  32. import {
  33. UserInputs,
  34. adminChangePassword,
  35. editUser,
  36. fetchUserDetails,
  37. } from "@/app/api/user/actions";
  38. import UserDetail from "./UserDetail";
  39. import { UserResult, passwordRule } from "@/app/api/user";
  40. import { auth } from "@/app/api/group/actions";
  41. import AuthAllocation from "./AuthAllocation";
  42. interface Props {
  43. user: UserResult;
  44. rules: passwordRule;
  45. auths: auth[];
  46. }
  47. const EditUser: React.FC<Props> = ({ user, rules, auths }) => {
  48. console.log(user);
  49. const { t } = useTranslation("user");
  50. const formProps = useForm<UserInputs>();
  51. const searchParams = useSearchParams();
  52. const id = parseInt(searchParams.get("id") || "0");
  53. const [tabIndex, setTabIndex] = useState(0);
  54. const router = useRouter();
  55. const [serverError, setServerError] = useState("");
  56. const addAuthIds =
  57. auths && auths.length > 0
  58. ? auths
  59. .filter((item) => item.v === 1)
  60. .map((item) => item.id)
  61. .sort((a, b) => a - b)
  62. : [];
  63. const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
  64. (_e, newValue) => {
  65. setTabIndex(newValue);
  66. },
  67. [],
  68. );
  69. const errors = formProps.formState.errors;
  70. const resetForm = React.useCallback((e?: React.MouseEvent<HTMLButtonElement>) => {
  71. e?.preventDefault();
  72. e?.stopPropagation();
  73. console.log("triggerred");
  74. console.log(addAuthIds);
  75. try {
  76. formProps.reset({
  77. username: user.username,
  78. name: user.name,
  79. staffNo: user.staffNo?.toString() ?? "",
  80. addAuthIds: addAuthIds,
  81. removeAuthIds: [],
  82. password: "",
  83. });
  84. formProps.clearErrors();
  85. console.log(formProps.formState.defaultValues);
  86. } catch (error) {
  87. console.log(error);
  88. setServerError(t("An error has occurred. Please try again later."));
  89. }
  90. }, [formProps, auths, user, addAuthIds, t]);
  91. useEffect(() => {
  92. resetForm();
  93. }, [user.id]);
  94. const hasErrorsInTab = (
  95. tabIndex: number,
  96. errors: FieldErrors<UserResult>,
  97. ) => {
  98. switch (tabIndex) {
  99. case 0:
  100. return Object.keys(errors).length > 0;
  101. default:
  102. false;
  103. }
  104. };
  105. const handleCancel = () => {
  106. router.back();
  107. };
  108. const onSubmit = useCallback<SubmitHandler<UserInputs>>(
  109. async (data) => {
  110. try {
  111. let haveError = false;
  112. const regex_pw =
  113. /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,20}$/;
  114. let pw = "";
  115. if (data.password && data.password.length > 0) {
  116. pw = data.password;
  117. if (pw.length < rules.min) {
  118. haveError = true;
  119. formProps.setError("password", {
  120. message: t("The password requires 8-20 characters."),
  121. type: "required",
  122. });
  123. }
  124. if (pw.length > rules.max) {
  125. haveError = true;
  126. formProps.setError("password", {
  127. message: t("The password requires 8-20 characters."),
  128. type: "required",
  129. });
  130. }
  131. if (!regex_pw.test(pw)) {
  132. haveError = true;
  133. formProps.setError("password", {
  134. message:
  135. "A combination of uppercase letters, lowercase letters, numbers, and symbols is required.",
  136. type: "required",
  137. });
  138. }
  139. }
  140. const userData = {
  141. username: data.username,
  142. name: data.name,
  143. staffNo: data.staffNo,
  144. locked: false,
  145. addAuthIds: data.addAuthIds || [],
  146. removeAuthIds: data.removeAuthIds || [],
  147. };
  148. const pwData = {
  149. id: id,
  150. password: "",
  151. newPassword: pw,
  152. };
  153. if (haveError) {
  154. return;
  155. }
  156. console.log("passed");
  157. await editUser(id, userData);
  158. if (data.password && data.password.length > 0) {
  159. await adminChangePassword(pwData);
  160. }
  161. router.replace("/settings/user");
  162. } catch (e) {
  163. console.log(e);
  164. setServerError(t("An error has occurred. Please try again later."));
  165. }
  166. },
  167. [router],
  168. );
  169. const onSubmitError = useCallback<SubmitErrorHandler<UserInputs>>(
  170. (errors) => {
  171. console.log(errors);
  172. },
  173. [],
  174. );
  175. return (
  176. <>
  177. {serverError && (
  178. <Typography variant="body2" color="error" alignSelf="flex-end">
  179. {serverError}
  180. </Typography>
  181. )}
  182. <FormProvider {...formProps}>
  183. <Stack
  184. spacing={2}
  185. component="form"
  186. onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
  187. >
  188. <Stack
  189. direction="row"
  190. justifyContent="space-between"
  191. flexWrap="wrap"
  192. rowGap={2}
  193. >
  194. <Tabs
  195. value={tabIndex}
  196. onChange={handleTabChange}
  197. variant="scrollable"
  198. >
  199. <Tab
  200. label={t("User Detail")}
  201. icon={
  202. hasErrorsInTab(0, errors) ? (
  203. <Error sx={{ marginInlineEnd: 1 }} color="error" />
  204. ) : undefined
  205. }
  206. iconPosition="end"
  207. />
  208. <Tab label={t("User Authority")} iconPosition="end" />
  209. </Tabs>
  210. </Stack>
  211. {tabIndex == 0 && <UserDetail />}
  212. {tabIndex === 1 && <AuthAllocation auths={auths!} />}
  213. <Stack direction="row" justifyContent="flex-end" gap={1}>
  214. <Button
  215. variant="text"
  216. startIcon={<RestartAlt />}
  217. onClick={(e) => {
  218. e.preventDefault();
  219. e.stopPropagation();
  220. resetForm(e);
  221. }}
  222. type="button"
  223. >
  224. {t("Reset")}
  225. </Button>
  226. <Button
  227. variant="outlined"
  228. startIcon={<Close />}
  229. onClick={handleCancel}
  230. type="button"
  231. >
  232. {t("Cancel")}
  233. </Button>
  234. <Button variant="contained" startIcon={<Check />} type="submit">
  235. {t("Confirm")}
  236. </Button>
  237. </Stack>
  238. </Stack>
  239. </FormProvider>
  240. </>
  241. );
  242. };
  243. export default EditUser;