You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

524 line
18 KiB

  1. "use client";
  2. import Stack from "@mui/material/Stack";
  3. import Box from "@mui/material/Box";
  4. import Card from "@mui/material/Card";
  5. import CardContent from "@mui/material/CardContent";
  6. import Grid from "@mui/material/Grid";
  7. import TextField from "@mui/material/TextField";
  8. import Typography from "@mui/material/Typography";
  9. import { CreateGroupInputs } from "@/app/api/group/actions";
  10. import { Controller, useFormContext } from "react-hook-form";
  11. import { useTranslation } from "react-i18next";
  12. import { useCallback, useEffect } from "react";
  13. import { CreateStaffInputs } from "@/app/api/staff/actions";
  14. import {
  15. Checkbox,
  16. FormControl,
  17. InputLabel,
  18. ListItemText,
  19. MenuItem,
  20. Select,
  21. } from "@mui/material";
  22. import { comboItem } from "./EditStaff";
  23. import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
  24. import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
  25. import { DemoItem } from "@mui/x-date-pickers/internals/demo";
  26. import dayjs from "dayjs";
  27. import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
  28. interface Props {
  29. combos: comboItem;
  30. }
  31. const StaffInfo: React.FC<Props> = ({ combos }) => {
  32. const {
  33. t,
  34. i18n: { language },
  35. } = useTranslation();
  36. const {
  37. register,
  38. formState: { errors, defaultValues },
  39. control,
  40. reset,
  41. resetField,
  42. setValue,
  43. getValues,
  44. watch,
  45. clearErrors,
  46. } = useFormContext<CreateStaffInputs>();
  47. const employType = [
  48. { id: 1, label: "FT" },
  49. { id: 2, label: "PT" },
  50. ];
  51. const skillIdNameMap = combos.skill.reduce<{ [id: number]: string }>(
  52. (acc, skill) => ({ ...acc, [skill.id]: skill.label }),
  53. {}
  54. );
  55. const resetStaff = useCallback(() => {
  56. console.log(defaultValues);
  57. if (defaultValues !== undefined) {
  58. resetField("joinDate");
  59. resetField("departDate");
  60. }
  61. }, [defaultValues]);
  62. useEffect(() => {
  63. resetStaff()
  64. }, [defaultValues]);
  65. const joinDate = watch("joinDate");
  66. const departDate = watch("departDate");
  67. useEffect(() => {
  68. if (joinDate) clearErrors("joinDate");
  69. if (departDate) clearErrors("departDate");
  70. }, [joinDate, departDate]);
  71. return (
  72. <Card sx={{ display: "block" }}>
  73. <CardContent component={Stack} spacing={4}>
  74. <Box>
  75. <Typography variant="overline" display="block" marginBlockEnd={1}>
  76. {t("Staff")}
  77. </Typography>
  78. <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
  79. <Grid item xs={6}>
  80. <TextField
  81. label={t("Staff ID")}
  82. fullWidth
  83. required
  84. {...register("staffId", {
  85. required: "Staff Id required!",
  86. })}
  87. error={Boolean(errors.name)}
  88. helperText={
  89. Boolean(errors.name) &&
  90. (errors.name?.message
  91. ? t(errors.name.message)
  92. : t("Please input correct staffId"))
  93. }
  94. />
  95. </Grid>
  96. <Grid item xs={6}>
  97. <TextField
  98. label={t("Staff Name")}
  99. fullWidth
  100. required
  101. {...register("name", {
  102. required: "Staff Name required!",
  103. })}
  104. error={Boolean(errors.name)}
  105. helperText={
  106. Boolean(errors.name) &&
  107. (errors.name?.message
  108. ? t(errors.name.message)
  109. : t("Please input correct name"))
  110. }
  111. />
  112. </Grid>
  113. <Grid item xs={6}>
  114. <FormControl fullWidth>
  115. <InputLabel required>{t("Company")}</InputLabel>
  116. <Controller
  117. control={control}
  118. name="companyId"
  119. render={({ field }) => (
  120. <Select
  121. label={t("Company")}
  122. {...field}
  123. error={Boolean(errors.companyId)}
  124. >
  125. {combos.company.map((company, index) => (
  126. <MenuItem
  127. key={`${company.id}-${index}`}
  128. value={company.id}
  129. >
  130. {t(company.label)}
  131. </MenuItem>
  132. ))}
  133. </Select>
  134. )}
  135. />
  136. </FormControl>
  137. </Grid>
  138. <Grid item xs={6}>
  139. <FormControl fullWidth>
  140. <InputLabel>{t("Team")}</InputLabel>
  141. <Controller
  142. control={control}
  143. name="teamId"
  144. render={({ field }) => (
  145. <Select
  146. label={t("Team")}
  147. {...field}
  148. // error={Boolean(errors.teamId)}
  149. >
  150. {combos.team.map((team, index) => (
  151. <MenuItem key={`${team.id}-${index}`} value={team.id}>
  152. {t(team.label)}
  153. </MenuItem>
  154. ))}
  155. </Select>
  156. )}
  157. />
  158. </FormControl>
  159. </Grid>
  160. <Grid item xs={6}>
  161. <FormControl fullWidth>
  162. <InputLabel>{t("Department")}</InputLabel>
  163. <Controller
  164. control={control}
  165. name="departmentId"
  166. render={({ field }) => (
  167. <Select
  168. label={t("Department")}
  169. {...field}
  170. // error={Boolean(errors.departmentId)}
  171. >
  172. {combos.department.map((department, index) => (
  173. <MenuItem
  174. key={`${department.id}-${index}`}
  175. value={department.id}
  176. >
  177. {t(department.label)}
  178. </MenuItem>
  179. ))}
  180. </Select>
  181. )}
  182. />
  183. </FormControl>
  184. </Grid>
  185. <Grid item xs={6}>
  186. <FormControl fullWidth>
  187. <InputLabel>{t("Grade")}</InputLabel>
  188. <Controller
  189. control={control}
  190. name="gradeId"
  191. render={({ field }) => (
  192. <Select
  193. label={t("Grade")}
  194. {...field}
  195. error={Boolean(errors.gradeId)}
  196. >
  197. {combos.grade.map((grade, index) => (
  198. <MenuItem key={`${grade.id}-${index}`} value={grade.id}>
  199. {t(grade.label)}
  200. </MenuItem>
  201. ))}
  202. </Select>
  203. )}
  204. />
  205. </FormControl>
  206. </Grid>
  207. <Grid item xs={6}>
  208. <FormControl fullWidth>
  209. <InputLabel>{t("Skillset")}</InputLabel>
  210. <Controller
  211. defaultValue={[]}
  212. control={control}
  213. name="skillSetId"
  214. render={({ field }) => (
  215. <Select
  216. // error={Boolean(errors.skillSetId)}
  217. renderValue={(types) =>
  218. types.map((type) => skillIdNameMap[type]).join(", ")
  219. }
  220. multiple
  221. label={t("Skillset")}
  222. {...field}
  223. >
  224. {combos.skill.map((skill, index) => {
  225. // console.log(field)
  226. return (
  227. <MenuItem
  228. key={`${skill.id}-${index}`}
  229. value={skill.id}
  230. >
  231. <Checkbox
  232. checked={field.value!.indexOf(skill.id) > -1}
  233. />
  234. <ListItemText primary={skill.label} />
  235. </MenuItem>
  236. );
  237. })}
  238. </Select>
  239. )}
  240. />
  241. </FormControl>
  242. </Grid>
  243. <Grid item xs={6}>
  244. <FormControl fullWidth>
  245. <InputLabel required>{t("Current Position")}</InputLabel>
  246. <Controller
  247. control={control}
  248. name="currentPositionId"
  249. render={({ field }) => (
  250. <Select
  251. label={t("Current Position")}
  252. {...field}
  253. error={Boolean(errors.currentPositionId)}
  254. >
  255. {combos.position.map((position, index) => (
  256. <MenuItem
  257. key={`${position.id}-${index}`}
  258. value={position.id}
  259. >
  260. {t(position.label)}
  261. </MenuItem>
  262. ))}
  263. </Select>
  264. )}
  265. />
  266. </FormControl>
  267. </Grid>
  268. <Grid item xs={6}>
  269. <FormControl fullWidth>
  270. <InputLabel required>{t("Salary Point")}</InputLabel>
  271. <Controller
  272. control={control}
  273. name="salaryId"
  274. render={({ field }) => (
  275. <Select
  276. label={t("Salary Point")}
  277. {...field}
  278. error={Boolean(errors.salaryId)}
  279. >
  280. {combos.salary.map((salary, index) => (
  281. <MenuItem
  282. key={`${salary.id}-${index}`}
  283. value={salary.id}
  284. >
  285. {t(salary.label)}
  286. </MenuItem>
  287. ))}
  288. </Select>
  289. )}
  290. />
  291. </FormControl>
  292. </Grid>
  293. <Grid item xs={6}>
  294. <FormControl fullWidth>
  295. <InputLabel required>{t("Employ Type")}</InputLabel>
  296. <Controller
  297. control={control}
  298. name="employType"
  299. render={({ field }) => (
  300. <Select
  301. label={t("Employ Type")}
  302. {...field}
  303. error={Boolean(errors.employType)}
  304. >
  305. {employType.map((type, index) => (
  306. <MenuItem
  307. key={`${type.id}-${index}`}
  308. value={type.label}
  309. >
  310. {t(type.label)}
  311. </MenuItem>
  312. ))}
  313. </Select>
  314. )}
  315. />
  316. </FormControl>
  317. </Grid>
  318. <Grid item xs={6}>
  319. <TextField
  320. label={t("Email")}
  321. fullWidth
  322. required
  323. {...register("email", {
  324. required: "Email required!",
  325. })}
  326. error={Boolean(errors.email)}
  327. helperText={
  328. Boolean(errors.email) &&
  329. (errors.email?.message
  330. ? t(errors.email.message)
  331. : t("Please input correct email"))
  332. }
  333. />
  334. </Grid>
  335. <Grid item xs={6}>
  336. <TextField
  337. label={t("Phone1")}
  338. fullWidth
  339. required
  340. {...register("phone1", {
  341. required: "phone1 required!",
  342. })}
  343. error={Boolean(errors.phone1)}
  344. helperText={
  345. Boolean(errors.phone1) &&
  346. (errors.phone1?.message
  347. ? t(errors.phone1.message)
  348. : t("Please input correct phone1"))
  349. }
  350. />
  351. </Grid>
  352. <Grid item xs={6}>
  353. <TextField
  354. label={t("Phone2")}
  355. fullWidth
  356. {...register("phone2")}
  357. error={Boolean(errors.phone2)}
  358. helperText={
  359. Boolean(errors.phone2) &&
  360. (errors.phone2?.message
  361. ? t(errors.phone2.message)
  362. : t("Please input correct phone2"))
  363. }
  364. />
  365. </Grid>
  366. </Grid>
  367. <Grid container spacing={2} columns={{ xs: 6, sm: 12 }} marginTop={3}>
  368. <Grid item xs={6}>
  369. <TextField
  370. label={t("Emergency Contact Name")}
  371. fullWidth
  372. {...register("emergContactName"
  373. // , {
  374. // required: "Emergency Contact Name required!",
  375. // }
  376. )}
  377. // error={Boolean(errors.emergContactName)}
  378. helperText={
  379. Boolean(errors.emergContactName) &&
  380. (errors.emergContactName?.message
  381. ? t(errors.emergContactName.message)
  382. : t("Please input correct Emergency Contact Name"))
  383. }
  384. />
  385. </Grid>
  386. <Grid item xs={6}>
  387. <TextField
  388. label={t("Emergency Contact Phone")}
  389. fullWidth
  390. {...register("emergContactPhone"
  391. // , {
  392. // required: "Emergency Contact Phone required!",
  393. // }
  394. )}
  395. // error={Boolean(errors.emergContactPhone)}
  396. helperText={
  397. Boolean(errors.emergContactPhone) &&
  398. (errors.emergContactPhone?.message
  399. ? t(errors.emergContactPhone.message)
  400. : t("Please input correct Emergency Contact Phone"))
  401. }
  402. />
  403. </Grid>
  404. <Grid item xs={6}>
  405. <LocalizationProvider
  406. dateAdapter={AdapterDayjs}
  407. adapterLocale={`${language}-hk`}
  408. >
  409. <DatePicker
  410. sx={{ width: "100%" }}
  411. label={t("Join Date")}
  412. // defaultValue={dayjs(getValues("joinDate"))}
  413. value={joinDate ? dayjs(joinDate) : null}
  414. onChange={(date) => {
  415. if (!date) return;
  416. setValue("joinDate", date.format(INPUT_DATE_FORMAT));
  417. }}
  418. slotProps={{
  419. textField: {
  420. // required: true,
  421. error:
  422. joinDate === "Invalid Date",
  423. // value: errors.joinDate?.message,
  424. },
  425. }}
  426. />
  427. </LocalizationProvider>
  428. </Grid>
  429. <Grid item xs={6}>
  430. <FormControl fullWidth>
  431. <InputLabel>{t("Join Position")}</InputLabel>
  432. <Controller
  433. control={control}
  434. name="joinPositionId"
  435. render={({ field }) => (
  436. <Select
  437. label={t("Join Position")}
  438. {...field}
  439. error={Boolean(errors.joinPositionId)}
  440. >
  441. {combos.position.map((position, index) => (
  442. <MenuItem
  443. key={`${position.id}-${index}`}
  444. value={position.id}
  445. >
  446. {t(position.label)}
  447. </MenuItem>
  448. ))}
  449. </Select>
  450. )}
  451. />
  452. </FormControl>
  453. </Grid>
  454. <Grid item xs={6}>
  455. <LocalizationProvider
  456. dateAdapter={AdapterDayjs}
  457. adapterLocale={`${language}-hk`}
  458. >
  459. <DatePicker
  460. sx={{ width: "100%" }}
  461. label={t("Depart Date")}
  462. value={departDate ? dayjs(departDate) : null}
  463. onChange={(date) => {
  464. if (!date) return;
  465. setValue("departDate", date.format(INPUT_DATE_FORMAT));
  466. }}
  467. slotProps={{
  468. textField: {
  469. error: joinDate && departDate
  470. ? new Date(joinDate) > new Date(departDate)
  471. : false,
  472. },
  473. }}
  474. />
  475. </LocalizationProvider>
  476. </Grid>
  477. <Grid item xs={6}>
  478. <TextField
  479. label={t("Depart Reason")}
  480. fullWidth
  481. {...register("departReason")}
  482. error={Boolean(errors.departReason)}
  483. helperText={
  484. Boolean(errors.departReason) &&
  485. (errors.departReason?.message
  486. ? t(errors.departReason.message)
  487. : t("Please input correct departReason"))
  488. }
  489. />
  490. </Grid>
  491. <Grid item xs={12}>
  492. <TextField
  493. label={t("Remark")}
  494. fullWidth
  495. multiline
  496. rows={4}
  497. {...register("remark")}
  498. error={Boolean(errors.remark)}
  499. helperText={
  500. Boolean(errors.remark) &&
  501. (errors.remark?.message
  502. ? t(errors.remark.message)
  503. : t("Please input correct remark"))
  504. }
  505. />
  506. </Grid>
  507. </Grid>
  508. </Box>
  509. </CardContent>
  510. </Card>
  511. );
  512. };
  513. export default StaffInfo;