選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

EditUser.tsx 6.4 KiB

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