FPSMS-frontend
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 

283 wiersze
8.0 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 { TeamResult } from "@/app/api/team";
  11. import { useTranslation } from "react-i18next";
  12. import {
  13. Button,
  14. Card,
  15. CardContent,
  16. Grid,
  17. Stack,
  18. Tab,
  19. Tabs,
  20. TabsProps,
  21. TextField,
  22. Typography,
  23. } from "@mui/material";
  24. import {
  25. FieldErrors,
  26. FormProvider,
  27. SubmitErrorHandler,
  28. SubmitHandler,
  29. useForm,
  30. useFormContext,
  31. } from "react-hook-form";
  32. import { Check, Close, Error as ErrorIcon, RestartAlt } from "@mui/icons-material";
  33. import {
  34. UserInputs,
  35. adminChangePassword,
  36. fetchUserDetails,
  37. createUser,
  38. } from "@/app/api/user/actions";
  39. import UserDetail from "./UserDetail";
  40. import { UserResult, passwordRule } from "@/app/api/user";
  41. import { auth } from "@/app/api/group/actions";
  42. import AuthAllocation from "./AuthAllocation";
  43. interface Props {
  44. rules: passwordRule;
  45. auths: auth[];
  46. }
  47. /** FP-MTMS Version Checklist | Functions Ref. No. 62 | v1.0.0 | 2026-08-10 */
  48. const CreateUser: React.FC<Props> = ({ rules, auths }) => {
  49. // console.log(auths);
  50. const { t } = useTranslation("user");
  51. const formProps = useForm<UserInputs>();
  52. const searchParams = useSearchParams();
  53. const id = parseInt(searchParams.get("id") || "0");
  54. const [tabIndex, setTabIndex] = useState(0);
  55. const router = useRouter();
  56. const [serverError, setServerError] = useState("");
  57. const addAuthIds =
  58. auths && auths.length > 0
  59. ? auths
  60. .filter((item) => item.v === 1)
  61. .map((item) => item.id)
  62. .sort((a, b) => a - b)
  63. : [];
  64. const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
  65. (_e, newValue) => {
  66. setTabIndex(newValue);
  67. },
  68. [],
  69. );
  70. const errors = formProps.formState.errors;
  71. const resetForm = React.useCallback((e?: React.MouseEvent<HTMLButtonElement>) => {
  72. e?.preventDefault();
  73. e?.stopPropagation();
  74. console.log("triggerred");
  75. console.log(addAuthIds);
  76. try {
  77. formProps.reset({
  78. username: "",
  79. name: "",
  80. staffNo: "",
  81. addAuthIds: addAuthIds,
  82. removeAuthIds: [],
  83. password: "",
  84. });
  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, addAuthIds, t]);
  91. useEffect(() => {
  92. resetForm();
  93. }, []);
  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: t(
  135. "A combination of uppercase letters, lowercase letters, numbers, and symbols is required.",
  136. ),
  137. type: "required",
  138. });
  139. }
  140. }
  141. const userData: UserInputs = {
  142. username: data.username,
  143. name: data.name ?? "",
  144. staffNo: data.staffNo,
  145. locked: false,
  146. addAuthIds: data.addAuthIds || [],
  147. removeAuthIds: data.removeAuthIds || [],
  148. password: pw,
  149. };
  150. const pwData = {
  151. id: id,
  152. password: pw,
  153. newPassword: "",
  154. };
  155. if (haveError) {
  156. return;
  157. }
  158. console.log("passed");
  159. console.log(userData);
  160. await createUser(userData);
  161. // if (data.password && data.password.length > 0) {
  162. // await adminChangePassword(pwData);
  163. // }
  164. router.replace("/settings/user");
  165. } catch (e) {
  166. console.log(e);
  167. const msg = e instanceof Error ? e.message : String(e);
  168. if (msg.includes("USERNAME_NOT_AVAILABLE")) {
  169. const text = t("Username is already taken");
  170. setServerError(text);
  171. formProps.setError("username", { message: text, type: "validate" });
  172. } else if (msg.includes("NAME_NOT_AVAILABLE")) {
  173. const text = t("Name is already taken");
  174. setServerError(text);
  175. formProps.setError("name", { message: text, type: "validate" });
  176. } else if (msg.includes("STAFF_NO_NOT_AVAILABLE")) {
  177. const text = t("Staff No is already taken");
  178. setServerError(text);
  179. formProps.setError("staffNo", { message: text, type: "validate" });
  180. } else if (msg.includes("USER_WRONG_NEW_PWD")) {
  181. setServerError(t("New password does not meet the rules"));
  182. } else if (/\b400\b/.test(msg)) {
  183. setServerError(t("Invalid request. Please check your input"));
  184. } else if (/\b401\b/.test(msg) || /\b403\b/.test(msg)) {
  185. setServerError(t("Unauthorized or no permission"));
  186. } else if (/\b404\b/.test(msg)) {
  187. setServerError(t("User Not Found"));
  188. } else if (/\b500\b/.test(msg)) {
  189. setServerError(t("Server error. Please try again later"));
  190. } else {
  191. setServerError(t("An error has occurred. Please try again later."));
  192. }
  193. }
  194. },
  195. [router],
  196. );
  197. const onSubmitError = useCallback<SubmitErrorHandler<UserInputs>>(
  198. (errors) => {
  199. console.log(errors);
  200. },
  201. [],
  202. );
  203. return (
  204. <>
  205. {serverError && (
  206. <Typography variant="body2" color="error" alignSelf="flex-end">
  207. {serverError}
  208. </Typography>
  209. )}
  210. <FormProvider {...formProps}>
  211. <Stack
  212. spacing={2}
  213. component="form"
  214. onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
  215. >
  216. <Stack
  217. direction="row"
  218. justifyContent="space-between"
  219. flexWrap="wrap"
  220. rowGap={2}
  221. >
  222. <Tabs
  223. value={tabIndex}
  224. onChange={handleTabChange}
  225. variant="scrollable"
  226. >
  227. <Tab
  228. label={t("User Detail")}
  229. icon={
  230. hasErrorsInTab(0, errors) ? (
  231. <ErrorIcon sx={{ marginInlineEnd: 1 }} color="error" />
  232. ) : undefined
  233. }
  234. iconPosition="end"
  235. />
  236. <Tab label={t("User Authority")} iconPosition="end" />
  237. </Tabs>
  238. </Stack>
  239. {tabIndex == 0 && <UserDetail />}
  240. {tabIndex === 1 && <AuthAllocation auths={auths!} />}
  241. <Stack direction="row" justifyContent="flex-end" gap={1}>
  242. <Button
  243. variant="text"
  244. startIcon={<RestartAlt />}
  245. onClick={(e) => {
  246. e.preventDefault();
  247. e.stopPropagation();
  248. resetForm(e);
  249. }}
  250. type="button"
  251. >
  252. {t("Reset")}
  253. </Button>
  254. <Button
  255. variant="outlined"
  256. startIcon={<Close />}
  257. onClick={handleCancel}
  258. type="button"
  259. >
  260. {t("Cancel")}
  261. </Button>
  262. <Button variant="contained" startIcon={<Check />} type="submit">
  263. {t("Confirm")}
  264. </Button>
  265. </Stack>
  266. </Stack>
  267. </FormProvider>
  268. </>
  269. );
  270. };
  271. export default CreateUser;