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

234 行
6.6 KiB

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