Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 

244 righe
6.4 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 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;