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.
 
 

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