| @@ -15,25 +15,25 @@ export interface CreateCustomInputs { | |||||
| export interface CreateStaffInputs { | export interface CreateStaffInputs { | ||||
| name: string; | name: string; | ||||
| currentPositionId: number; | |||||
| joinPositionId: number; | |||||
| staffId: string; | |||||
| companyId: number; | companyId: number; | ||||
| gradeId: number; | |||||
| teamId: number; | |||||
| salaryId: number; | salaryId: number; | ||||
| email: string; | |||||
| skillSetId?: number[]; | |||||
| joinDate: string; | |||||
| currentPositionId: number; | |||||
| joinPositionId: number; | |||||
| gradeId?: number; | |||||
| teamId?: number | |||||
| departmentId: number; | |||||
| phone1: string; | phone1: string; | ||||
| phone2: string; | |||||
| hourlyRate: string | number; | |||||
| phone2?: string; | |||||
| email: string; | |||||
| emergContactName: string; | emergContactName: string; | ||||
| emergContactPhone: string; | emergContactPhone: string; | ||||
| employType: string; | employType: string; | ||||
| joinDate: string | null; | |||||
| departDate?: string | null; | |||||
| departReason?: string | null; | |||||
| remark?: string | null; | |||||
| staffId: string | null; | |||||
| skillSetId?: number[] | number | null | undefined | |||||
| departDate?: string; | |||||
| departReason?: string; | |||||
| remark?: string; | |||||
| } | } | ||||
| export interface records { | export interface records { | ||||
| @@ -53,8 +53,8 @@ const ChangePassword: React.FC = () => { | |||||
| password: data.password, | password: data.password, | ||||
| newPassword: data.newPassword | newPassword: data.newPassword | ||||
| } | } | ||||
| // await changePassword(postData) | |||||
| // router.replace("/home") | |||||
| await changePassword(postData) | |||||
| router.replace("/home") | |||||
| } catch (e) { | } catch (e) { | ||||
| console.log(e) | console.log(e) | ||||
| setServerError(t("An error has occurred. Please try again later.")); | setServerError(t("An error has occurred. Please try again later.")); | ||||
| @@ -11,7 +11,7 @@ import { | |||||
| useForm, | useForm, | ||||
| } from "react-hook-form"; | } from "react-hook-form"; | ||||
| import { CreateStaffInputs, saveStaff, testing } from "@/app/api/staff/actions"; | import { CreateStaffInputs, saveStaff, testing } from "@/app/api/staff/actions"; | ||||
| import { Typography } from "@mui/material"; | |||||
| import { Button, Stack, Typography } from "@mui/material"; | |||||
| import CreateStaffForm from "../CreateStaffForm"; | import CreateStaffForm from "../CreateStaffForm"; | ||||
| import { comboProp, fetchCompanyCombo } from "@/app/api/companys/actions"; | import { comboProp, fetchCompanyCombo } from "@/app/api/companys/actions"; | ||||
| import { fetchTeamCombo } from "@/app/api/team/actions"; | import { fetchTeamCombo } from "@/app/api/team/actions"; | ||||
| @@ -20,6 +20,8 @@ import { fetchPositionCombo } from "@/app/api/positions/actions"; | |||||
| import { fetchGradeCombo } from "@/app/api/grades/actions"; | import { fetchGradeCombo } from "@/app/api/grades/actions"; | ||||
| import { fetchSkillCombo } from "@/app/api/skill/actions"; | import { fetchSkillCombo } from "@/app/api/skill/actions"; | ||||
| import { fetchSalaryCombo } from "@/app/api/salarys/actions"; | import { fetchSalaryCombo } from "@/app/api/salarys/actions"; | ||||
| import StaffInfo from "./StaffInfo"; | |||||
| import { Check, Close } from "@mui/icons-material"; | |||||
| interface Field { | interface Field { | ||||
| id: string; | id: string; | ||||
| @@ -43,171 +45,133 @@ export interface comboItem { | |||||
| } | } | ||||
| interface formProps { | interface formProps { | ||||
| Title?: string[]; | |||||
| // Title?: string[]; | |||||
| combos: comboItem; | combos: comboItem; | ||||
| } | } | ||||
| const CreateStaff: React.FC<formProps> = ({ Title, combos }) => { | |||||
| const CreateStaff: React.FC<formProps> = ({ combos }) => { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const formProps = useForm<CreateStaffInputs>(); | |||||
| const [serverError, setServerError] = useState(""); | |||||
| const router = useRouter(); | |||||
| const [tabIndex, setTabIndex] = useState(0); | |||||
| const errors = formProps.formState.errors; | |||||
| const onSubmit = useCallback<SubmitHandler<CreateStaffInputs>>( | |||||
| async (data) => { | |||||
| try { | |||||
| console.log(data); | |||||
| let haveError = false; | |||||
| let regex_email = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/ | |||||
| let regex_phone = /^\d{8}$/ | |||||
| if (!regex_email.test(data.email)) { | |||||
| haveError = true | |||||
| formProps.setError("email", { message: t("Please Enter Correct Email."), type: "required" }) | |||||
| } | |||||
| if(!regex_phone.test(data.phone1)) { | |||||
| haveError = true | |||||
| formProps.setError("phone1", { message: t("Please Enter Correct Phone No.."), type: "required" }) | |||||
| } | |||||
| if(!regex_phone.test(data.emergContactPhone)) { | |||||
| haveError = true | |||||
| formProps.setError("emergContactPhone", { message: t("Please Enter Correct Phone No.."), type: "required" }) | |||||
| } | |||||
| if (data.phone2 && data.phone2?.length > 0) { | |||||
| if(!regex_phone.test(data.phone2)) { | |||||
| haveError = true | |||||
| formProps.setError("phone2", { message: t("Please Enter Correct Phone No.."), type: "required" }) | |||||
| } | |||||
| } | |||||
| if (!regex_email.test(data.email)) { | |||||
| haveError = true | |||||
| formProps.setError("email", { message: t("Please Enter Correct Email."), type: "required" }) | |||||
| } | |||||
| if (!data.companyId) { | |||||
| haveError = true | |||||
| formProps.setError("companyId", { message: t("Please Enter Company."), type: "required" }) | |||||
| } | |||||
| if (!data.gradeId) { | |||||
| haveError = true | |||||
| formProps.setError("gradeId", { message: t("Please Enter grade."), type: "required" }) | |||||
| } | |||||
| if (!data.employType) { | |||||
| haveError = true | |||||
| formProps.setError("employType", { message: t("Please Enter Employ Type."), type: "required" }) | |||||
| } | |||||
| if (!data.departmentId) { | |||||
| haveError = true | |||||
| formProps.setError("departmentId", { message: t("Please Enter Department."), type: "required" }) | |||||
| } | |||||
| if (!data.salaryId) { | |||||
| haveError = true | |||||
| formProps.setError("salaryId", { message: t("Please Enter Salary."), type: "required" }) | |||||
| } | |||||
| if (!data.joinDate) { | |||||
| haveError = true | |||||
| formProps.setError("joinDate", { message: t("Please Enter Join Date."), type: "required" }) | |||||
| } | |||||
| if (data.departDate && new Date(data.departDate) <= new Date(data.joinDate)) { | |||||
| haveError = true | |||||
| formProps.setError("departDate", { message: t("Depart Date cannot be earlier than Join Date."), type: "required" }) | |||||
| } | |||||
| // if (!data.joinPositionId) { | |||||
| // haveError = true | |||||
| // formProps.setError("joinPositionId", { message: t("Depart Date cannot be earlier than Join Date."), type: "required" }) | |||||
| // } | |||||
| if (haveError) { | |||||
| return | |||||
| } | |||||
| console.log("passed") | |||||
| await saveStaff(data) | |||||
| router.replace("/settings/staff") | |||||
| } catch (e) { | |||||
| console.log(e); | |||||
| setServerError(t("An error has occurred. Please try again later.")); | |||||
| } | |||||
| }, | |||||
| [router] | |||||
| ); | |||||
| const handleCancel = () => { | |||||
| router.back(); | |||||
| }; | |||||
| const fieldLists: Field[][] = [ | |||||
| [ | |||||
| { | |||||
| id: "staffId", | |||||
| label: t("Staff ID"), | |||||
| type: "text", | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "name", | |||||
| label: t("Staff Name"), | |||||
| type: "text", | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "companyId", | |||||
| label: t("Company"), | |||||
| type: "combo-Obj", | |||||
| options: combos.company || [], | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "teamId", | |||||
| label: t("Team"), | |||||
| type: "combo-Obj", | |||||
| options: combos.team || [], | |||||
| required: false, | |||||
| }, | |||||
| { | |||||
| id: "departmentId", | |||||
| label: t("Department"), | |||||
| type: "combo-Obj", | |||||
| options: combos.department || [], | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "gradeId", | |||||
| label: t("Grade"), | |||||
| type: "combo-Obj", | |||||
| options: combos.grade || [], | |||||
| required: false, | |||||
| }, | |||||
| { | |||||
| id: "skillSetId", | |||||
| label: t("Skillset"), | |||||
| type: "multiSelect-Obj", | |||||
| options: combos.skill || [], | |||||
| required: false, | |||||
| }, | |||||
| { | |||||
| id: "currentPositionId", | |||||
| label: t("Current Position"), | |||||
| type: "combo-Obj", | |||||
| options: combos.position || [], | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "salaryId", | |||||
| label: t("Salary Point"), | |||||
| type: "combo-Obj", | |||||
| options: combos.salary || [], | |||||
| required: true, | |||||
| }, | |||||
| // { | |||||
| // id: "hourlyRate", | |||||
| // label: t("Hourly Rate"), | |||||
| // type: "numeric-testing", | |||||
| // value: "", | |||||
| // required: false, | |||||
| // }, | |||||
| // { | |||||
| // id: "hourlyRate", | |||||
| // label: t("Hourly Rate"), | |||||
| // type: "numeric-testing", | |||||
| // required: true, | |||||
| // }, | |||||
| { | |||||
| id: "employType", | |||||
| label: t("Employ Type"), | |||||
| type: "combo-Obj", | |||||
| options: [{id: "FT", label: t("FT")}, {id: "PT", label: t("PT")}], | |||||
| value: "", | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "email", | |||||
| label: t("Email"), | |||||
| type: "text", | |||||
| pattern: "^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$", | |||||
| message: t("input matching format"), | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "phone1", | |||||
| label: t("Phone1"), | |||||
| type: "text", | |||||
| pattern: "^\\d{8}$", | |||||
| message: t("input correct phone no."), | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "phone2", | |||||
| label: t("Phone2"), | |||||
| type: "text", | |||||
| pattern: "^\\d{8}$", | |||||
| message: t("input correct phone no."), | |||||
| required: false, | |||||
| }, | |||||
| ], | |||||
| [ | |||||
| { | |||||
| id: "emergContactName", | |||||
| label: t("Emergency Contact Name"), | |||||
| type: "text", | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "emergContactPhone", | |||||
| label: t("Emergency Contact Phone"), | |||||
| type: "text", | |||||
| pattern: "^\\d{8}$", | |||||
| message: t("input correct phone no."), | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "joinDate", | |||||
| label: t("Join Date"), | |||||
| type: "multiDate", | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "joinPositionId", | |||||
| label: t("Join Position"), | |||||
| type: "combo-Obj", | |||||
| options: combos.position || [], | |||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "departDate", | |||||
| label: t("Depart Date"), | |||||
| type: "multiDate", | |||||
| }, | |||||
| { | |||||
| id: "departReason", | |||||
| label: t("Depart Reason"), | |||||
| type: "text", | |||||
| }, | |||||
| { | |||||
| id: "remark", | |||||
| label: t("Remark"), | |||||
| type: "remarks", | |||||
| }, | |||||
| ] | |||||
| ]; | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <CreateStaffForm Title={Title} fieldLists={fieldLists}/> | |||||
| <FormProvider {...formProps}> | |||||
| <Stack | |||||
| spacing={2} | |||||
| component="form" | |||||
| onSubmit={formProps.handleSubmit(onSubmit)} | |||||
| > | |||||
| {serverError && ( | |||||
| <Typography variant="body2" color="error" alignSelf="flex-end"> | |||||
| {serverError} | |||||
| </Typography> | |||||
| )} | |||||
| <StaffInfo combos={combos}/> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| <Button | |||||
| variant="outlined" | |||||
| startIcon={<Close />} | |||||
| onClick={handleCancel} | |||||
| > | |||||
| {t("Cancel")} | |||||
| </Button> | |||||
| <Button | |||||
| variant="contained" | |||||
| startIcon={<Check />} | |||||
| type="submit" | |||||
| // disabled={Boolean(formProps.watch("isGridEditing"))} | |||||
| > | |||||
| {t("Confirm")} | |||||
| </Button> | |||||
| </Stack> | |||||
| </Stack> | |||||
| </FormProvider> | |||||
| </> | </> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -0,0 +1,514 @@ | |||||
| "use client"; | |||||
| import Stack from "@mui/material/Stack"; | |||||
| import Box from "@mui/material/Box"; | |||||
| import Card from "@mui/material/Card"; | |||||
| import CardContent from "@mui/material/CardContent"; | |||||
| import Grid from "@mui/material/Grid"; | |||||
| import TextField from "@mui/material/TextField"; | |||||
| import Typography from "@mui/material/Typography"; | |||||
| import { CreateGroupInputs } from "@/app/api/group/actions"; | |||||
| import { Controller, useFormContext } from "react-hook-form"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import { useCallback, useEffect } from "react"; | |||||
| import { CreateStaffInputs } from "@/app/api/staff/actions"; | |||||
| import { | |||||
| Checkbox, | |||||
| FormControl, | |||||
| InputLabel, | |||||
| ListItemText, | |||||
| MenuItem, | |||||
| Select, | |||||
| } from "@mui/material"; | |||||
| import { comboItem } from "./CreateStaff"; | |||||
| import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers"; | |||||
| import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | |||||
| import { DemoItem } from "@mui/x-date-pickers/internals/demo"; | |||||
| import dayjs from "dayjs"; | |||||
| import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | |||||
| interface Props { | |||||
| combos: comboItem; | |||||
| } | |||||
| const StaffInfo: React.FC<Props> = ({ combos }) => { | |||||
| const { | |||||
| t, | |||||
| i18n: { language }, | |||||
| } = useTranslation(); | |||||
| const { | |||||
| register, | |||||
| formState: { errors, defaultValues }, | |||||
| control, | |||||
| reset, | |||||
| resetField, | |||||
| setValue, | |||||
| getValues, | |||||
| clearErrors | |||||
| } = useFormContext<CreateStaffInputs>(); | |||||
| const employType = [ | |||||
| { id: 1, label: "FT" }, | |||||
| { id: 2, label: "PT" }, | |||||
| ]; | |||||
| const skillIdNameMap = combos.skill.reduce<{ [id: number]: string }>( | |||||
| (acc, skill) => ({ ...acc, [skill.id]: skill.label }), | |||||
| {} | |||||
| ); | |||||
| const resetStaff = useCallback(() => { | |||||
| console.log(defaultValues); | |||||
| if (defaultValues !== undefined) { | |||||
| // resetField("description"); | |||||
| } | |||||
| }, [defaultValues]); | |||||
| const joinDate = getValues("joinDate"); | |||||
| const departDate = getValues("departDate"); | |||||
| useEffect(() => { | |||||
| if(joinDate) | |||||
| clearErrors("joinDate") | |||||
| if(departDate) | |||||
| clearErrors("departDate") | |||||
| }, [joinDate, departDate]) | |||||
| return ( | |||||
| <Card sx={{ display: "block" }}> | |||||
| <CardContent component={Stack} spacing={4}> | |||||
| <Box> | |||||
| <Typography variant="overline" display="block" marginBlockEnd={1}> | |||||
| {t("Staff")} | |||||
| </Typography> | |||||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Staff ID")} | |||||
| fullWidth | |||||
| required | |||||
| {...register("staffId", { | |||||
| required: "Staff Id required!", | |||||
| })} | |||||
| error={Boolean(errors.name)} | |||||
| helperText={ | |||||
| Boolean(errors.name) && | |||||
| (errors.name?.message | |||||
| ? t(errors.name.message) | |||||
| : t("Please input correct staffId")) | |||||
| } | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Staff Name")} | |||||
| fullWidth | |||||
| required | |||||
| {...register("name", { | |||||
| required: "Staff Name required!", | |||||
| })} | |||||
| error={Boolean(errors.name)} | |||||
| helperText={ | |||||
| Boolean(errors.name) && | |||||
| (errors.name?.message | |||||
| ? t(errors.name.message) | |||||
| : t("Please input correct name")) | |||||
| } | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel required>{t("Company")}</InputLabel> | |||||
| <Controller | |||||
| control={control} | |||||
| name="companyId" | |||||
| render={({ field }) => ( | |||||
| <Select | |||||
| label={t("Company")} | |||||
| {...field} | |||||
| error={Boolean(errors.companyId)} | |||||
| > | |||||
| {combos.company.map((company, index) => ( | |||||
| <MenuItem | |||||
| key={`${company.id}-${index}`} | |||||
| value={company.id} | |||||
| > | |||||
| {t(company.label)} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| )} | |||||
| /> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel>{t("Team")}</InputLabel> | |||||
| <Controller | |||||
| control={control} | |||||
| name="teamId" | |||||
| render={({ field }) => ( | |||||
| <Select | |||||
| label={t("Team")} | |||||
| {...field} | |||||
| // error={Boolean(errors.teamId)} | |||||
| > | |||||
| {combos.team.map((team, index) => ( | |||||
| <MenuItem key={`${team.id}-${index}`} value={team.id}> | |||||
| {t(team.label)} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| )} | |||||
| /> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel>{t("Department")}</InputLabel> | |||||
| <Controller | |||||
| control={control} | |||||
| name="departmentId" | |||||
| render={({ field }) => ( | |||||
| <Select | |||||
| label={t("Department")} | |||||
| {...field} | |||||
| error={Boolean(errors.departmentId)} | |||||
| > | |||||
| {combos.department.map((department, index) => ( | |||||
| <MenuItem | |||||
| key={`${department.id}-${index}`} | |||||
| value={department.id} | |||||
| > | |||||
| {t(department.label)} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| )} | |||||
| /> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel required>{t("Grade")}</InputLabel> | |||||
| <Controller | |||||
| control={control} | |||||
| name="gradeId" | |||||
| render={({ field }) => ( | |||||
| <Select | |||||
| label={t("Grade")} | |||||
| {...field} | |||||
| error={Boolean(errors.gradeId)} | |||||
| > | |||||
| {combos.grade.map((grade, index) => ( | |||||
| <MenuItem key={`${grade.id}-${index}`} value={grade.id}> | |||||
| {t(grade.label)} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| )} | |||||
| /> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel>{t("Skillset")}</InputLabel> | |||||
| <Controller | |||||
| defaultValue={[]} | |||||
| control={control} | |||||
| name="skillSetId" | |||||
| render={({ field }) => ( | |||||
| <Select | |||||
| // error={Boolean(errors.skillSetId)} | |||||
| renderValue={(types) => | |||||
| types.map((type) => skillIdNameMap[type]).join(", ") | |||||
| } | |||||
| multiple | |||||
| label={t("Skillset")} | |||||
| {...field} | |||||
| > | |||||
| {combos.skill.map((skill, index) => { | |||||
| // console.log(field) | |||||
| return ( | |||||
| <MenuItem | |||||
| key={`${skill.id}-${index}`} | |||||
| value={skill.id} | |||||
| > | |||||
| <Checkbox | |||||
| checked={field.value!.indexOf(skill.id) > -1} | |||||
| /> | |||||
| <ListItemText primary={skill.label} /> | |||||
| </MenuItem> | |||||
| ); | |||||
| })} | |||||
| </Select> | |||||
| )} | |||||
| /> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel required>{t("Current Position")}</InputLabel> | |||||
| <Controller | |||||
| control={control} | |||||
| name="currentPositionId" | |||||
| render={({ field }) => ( | |||||
| <Select | |||||
| label={t("Current Position")} | |||||
| {...field} | |||||
| error={Boolean(errors.currentPositionId)} | |||||
| > | |||||
| {combos.position.map((position, index) => ( | |||||
| <MenuItem | |||||
| key={`${position.id}-${index}`} | |||||
| value={position.id} | |||||
| > | |||||
| {t(position.label)} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| )} | |||||
| /> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel>{t("Salary Point")}</InputLabel> | |||||
| <Controller | |||||
| control={control} | |||||
| name="salaryId" | |||||
| render={({ field }) => ( | |||||
| <Select | |||||
| label={t("Salary Point")} | |||||
| {...field} | |||||
| error={Boolean(errors.salaryId)} | |||||
| > | |||||
| {combos.salary.map((salary, index) => ( | |||||
| <MenuItem | |||||
| key={`${salary.id}-${index}`} | |||||
| value={salary.id} | |||||
| > | |||||
| {t(salary.label)} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| )} | |||||
| /> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel>{t("Employ Type")}</InputLabel> | |||||
| <Controller | |||||
| control={control} | |||||
| name="employType" | |||||
| render={({ field }) => ( | |||||
| <Select | |||||
| label={t("Employ Type")} | |||||
| {...field} | |||||
| error={Boolean(errors.employType)} | |||||
| > | |||||
| {employType.map((type, index) => ( | |||||
| <MenuItem | |||||
| key={`${type.id}-${index}`} | |||||
| value={type.label} | |||||
| > | |||||
| {t(type.label)} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| )} | |||||
| /> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Email")} | |||||
| fullWidth | |||||
| required | |||||
| {...register("email", { | |||||
| required: "Email required!", | |||||
| })} | |||||
| error={Boolean(errors.email)} | |||||
| helperText={ | |||||
| Boolean(errors.email) && | |||||
| (errors.email?.message | |||||
| ? t(errors.email.message) | |||||
| : t("Please input correct email")) | |||||
| } | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Phone1")} | |||||
| fullWidth | |||||
| required | |||||
| {...register("phone1", { | |||||
| required: "phone1 required!", | |||||
| })} | |||||
| error={Boolean(errors.phone1)} | |||||
| helperText={ | |||||
| Boolean(errors.phone1) && | |||||
| (errors.phone1?.message | |||||
| ? t(errors.phone1.message) | |||||
| : t("Please input correct phone1")) | |||||
| } | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Phone2")} | |||||
| fullWidth | |||||
| {...register("phone2")} | |||||
| error={Boolean(errors.phone2)} | |||||
| helperText={ | |||||
| Boolean(errors.phone2) && | |||||
| (errors.phone2?.message | |||||
| ? t(errors.phone2.message) | |||||
| : t("Please input correct phone2")) | |||||
| } | |||||
| /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }} marginTop={3}> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Emergency Contact Name")} | |||||
| fullWidth | |||||
| required | |||||
| {...register("emergContactName", { | |||||
| required: "Emergency Contact Name required!", | |||||
| })} | |||||
| error={Boolean(errors.emergContactName)} | |||||
| helperText={ | |||||
| Boolean(errors.emergContactName) && | |||||
| (errors.emergContactName?.message | |||||
| ? t(errors.emergContactName.message) | |||||
| : t("Please input correct Emergency Contact Name")) | |||||
| } | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Emergency Contact Phone")} | |||||
| fullWidth | |||||
| required | |||||
| {...register("emergContactPhone", { | |||||
| required: "Emergency Contact Phone required!", | |||||
| })} | |||||
| error={Boolean(errors.emergContactPhone)} | |||||
| helperText={ | |||||
| Boolean(errors.emergContactPhone) && | |||||
| (errors.emergContactPhone?.message | |||||
| ? t(errors.emergContactPhone.message) | |||||
| : t("Please input correct Emergency Contact Phone")) | |||||
| } | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <LocalizationProvider | |||||
| dateAdapter={AdapterDayjs} | |||||
| adapterLocale={`${language}-hk`} | |||||
| > | |||||
| <DatePicker | |||||
| sx={{ width: "100%" }} | |||||
| label={t("Join Date")} | |||||
| value={joinDate ? dayjs(joinDate) : null} | |||||
| onChange={(date) => { | |||||
| if (!date) return; | |||||
| setValue("joinDate", date.format(INPUT_DATE_FORMAT)); | |||||
| }} | |||||
| slotProps={{ | |||||
| textField: { | |||||
| error: | |||||
| joinDate === "Invalid Date" || Boolean(errors.joinDate), | |||||
| // value: errors.joinDate?.message, | |||||
| }, | |||||
| }} | |||||
| /> | |||||
| </LocalizationProvider> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel>{t("Join Position")}</InputLabel> | |||||
| <Controller | |||||
| control={control} | |||||
| name="joinPositionId" | |||||
| render={({ field }) => ( | |||||
| <Select | |||||
| label={t("Join Position")} | |||||
| {...field} | |||||
| error={Boolean(errors.joinPositionId)} | |||||
| > | |||||
| {combos.position.map((position, index) => ( | |||||
| <MenuItem | |||||
| key={`${position.id}-${index}`} | |||||
| value={position.label} | |||||
| > | |||||
| {t(position.label)} | |||||
| </MenuItem> | |||||
| ))} | |||||
| </Select> | |||||
| )} | |||||
| /> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <LocalizationProvider | |||||
| dateAdapter={AdapterDayjs} | |||||
| adapterLocale={`${language}-hk`} | |||||
| > | |||||
| <DatePicker | |||||
| sx={{ width: "100%" }} | |||||
| label={t("Depart Date")} | |||||
| value={departDate ? dayjs(departDate) : null} | |||||
| onChange={(date) => { | |||||
| if (!date) return; | |||||
| setValue("departDate", date.format(INPUT_DATE_FORMAT)); | |||||
| }} | |||||
| slotProps={{ | |||||
| textField: { | |||||
| error: departDate === "Invalid Date", | |||||
| // value: errors.departDate?.message, | |||||
| }, | |||||
| }} | |||||
| /> | |||||
| </LocalizationProvider> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Depart Reason")} | |||||
| fullWidth | |||||
| {...register("departReason")} | |||||
| error={Boolean(errors.departReason)} | |||||
| helperText={ | |||||
| Boolean(errors.departReason) && | |||||
| (errors.departReason?.message | |||||
| ? t(errors.departReason.message) | |||||
| : t("Please input correct departReason")) | |||||
| } | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <TextField | |||||
| label={t("Remark")} | |||||
| fullWidth | |||||
| multiline | |||||
| rows={4} | |||||
| {...register("remark")} | |||||
| error={Boolean(errors.remark)} | |||||
| helperText={ | |||||
| Boolean(errors.remark) && | |||||
| (errors.remark?.message | |||||
| ? t(errors.remark.message) | |||||
| : t("Please input correct remark")) | |||||
| } | |||||
| /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| </Box> | |||||
| </CardContent> | |||||
| </Card> | |||||
| ); | |||||
| }; | |||||
| export default StaffInfo; | |||||
| @@ -1,120 +1,120 @@ | |||||
| import { useCallback, useState } from "react"; | |||||
| import CustomInputForm from "../CustomInputForm"; | |||||
| import { useRouter } from "next/navigation"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import { | |||||
| FieldErrors, | |||||
| FormProvider, | |||||
| SubmitErrorHandler, | |||||
| SubmitHandler, | |||||
| useForm, | |||||
| } from "react-hook-form"; | |||||
| import { CreateStaffInputs, saveStaff, testing } from "@/app/api/staff/actions"; | |||||
| import { Typography } from "@mui/material"; | |||||
| // import { useCallback, useState } from "react"; | |||||
| // import CustomInputForm from "../CustomInputForm"; | |||||
| // import { useRouter } from "next/navigation"; | |||||
| // import { useTranslation } from "react-i18next"; | |||||
| // import { | |||||
| // FieldErrors, | |||||
| // FormProvider, | |||||
| // SubmitErrorHandler, | |||||
| // SubmitHandler, | |||||
| // useForm, | |||||
| // } from "react-hook-form"; | |||||
| // import { CreateStaffInputs, saveStaff, testing } from "@/app/api/staff/actions"; | |||||
| // import { Typography } from "@mui/material"; | |||||
| interface Field { | |||||
| // subtitle: string; | |||||
| id: string; | |||||
| label: string; | |||||
| type: string; | |||||
| value?: any; | |||||
| required?: boolean; | |||||
| options?: any[]; | |||||
| readOnly?: boolean; | |||||
| } | |||||
| // interface Field { | |||||
| // // subtitle: string; | |||||
| // id: string; | |||||
| // label: string; | |||||
| // type: string; | |||||
| // value?: any; | |||||
| // required?: boolean; | |||||
| // options?: any[]; | |||||
| // readOnly?: boolean; | |||||
| // } | |||||
| interface formProps { | |||||
| Title?: string[]; | |||||
| fieldLists: Field[][]; | |||||
| } | |||||
| // interface formProps { | |||||
| // Title?: string[]; | |||||
| // fieldLists: Field[][]; | |||||
| // } | |||||
| const CreateStaffForm: React.FC<formProps> = ({ Title, fieldLists }) => { | |||||
| const router = useRouter(); | |||||
| const { t } = useTranslation(); | |||||
| const [serverError, setServerError] = useState(""); | |||||
| // const CreateStaffForm: React.FC<formProps> = ({ Title, fieldLists }) => { | |||||
| // const router = useRouter(); | |||||
| // const { t } = useTranslation(); | |||||
| // const [serverError, setServerError] = useState(""); | |||||
| const handleCancel = () => { | |||||
| router.back(); | |||||
| }; | |||||
| const onSubmit = useCallback<SubmitHandler<CreateStaffInputs>>( | |||||
| async (data) => { | |||||
| try { | |||||
| console.log(data); | |||||
| let haveError = false; | |||||
| //check if joinDate exist | |||||
| if (data.joinDate == null && data.joinDate == "Invalid Date") { | |||||
| haveError = true; | |||||
| return haveError; | |||||
| } | |||||
| //check if joinDate > departDate | |||||
| if (data.departDate != null && data.departDate != "Invalid Date" && data.departDate.length != 0) { | |||||
| if (data.joinDate != null) { | |||||
| const joinDate = new Date(data.joinDate); | |||||
| const departDate = new Date(data.departDate); | |||||
| if (joinDate.getTime() > departDate.getTime()) { | |||||
| haveError = true; | |||||
| return haveError; | |||||
| } | |||||
| } | |||||
| if (data.departReason == null || data.departReason.length == 0) { | |||||
| haveError = true; | |||||
| return haveError; | |||||
| } | |||||
| } | |||||
| // const handleCancel = () => { | |||||
| // router.back(); | |||||
| // }; | |||||
| // const onSubmit = useCallback<SubmitHandler<CreateStaffInputs>>( | |||||
| // async (data) => { | |||||
| // try { | |||||
| // console.log(data); | |||||
| // let haveError = false; | |||||
| // //check if joinDate exist | |||||
| // if (data.joinDate == null && data.joinDate == "Invalid Date") { | |||||
| // haveError = true; | |||||
| // return haveError; | |||||
| // } | |||||
| // //check if joinDate > departDate | |||||
| // if (data.departDate != null && data.departDate != "Invalid Date" && data.departDate.length != 0) { | |||||
| // if (data.joinDate != null) { | |||||
| // const joinDate = new Date(data.joinDate); | |||||
| // const departDate = new Date(data.departDate); | |||||
| // if (joinDate.getTime() > departDate.getTime()) { | |||||
| // haveError = true; | |||||
| // return haveError; | |||||
| // } | |||||
| // } | |||||
| // if (data.departReason == null || data.departReason.length == 0) { | |||||
| // haveError = true; | |||||
| // return haveError; | |||||
| // } | |||||
| // } | |||||
| if (haveError) { | |||||
| return | |||||
| } | |||||
| const postData = { | |||||
| ...data, | |||||
| skillSetId: typeof data.skillSetId === "number" ? [data.skillSetId] : data.skillSetId, | |||||
| emergContactPhone: data.emergContactPhone.toString(), | |||||
| phone1: data.phone1.toString(), | |||||
| phone2: data.phone2.toString(), | |||||
| hourlyRate: typeof data.hourlyRate === 'string' ? parseInt(data.hourlyRate.replace("$", "").replace(",", "")) : 0 | |||||
| }; | |||||
| if (postData.departDate?.length === 0 && postData.departReason?.length === 0) { | |||||
| delete postData.departDate; | |||||
| delete postData.departReason; | |||||
| } | |||||
| if (postData.remark?.length === 0) { | |||||
| delete postData.remark; | |||||
| } | |||||
| console.log(postData); | |||||
| setServerError(""); | |||||
| await saveStaff(postData); | |||||
| router.replace("/settings/staff"); | |||||
| } catch (e) { | |||||
| setServerError(t("An error has occurred. Please try again later.")); | |||||
| } | |||||
| }, | |||||
| [router, t] | |||||
| ); | |||||
| // if (haveError) { | |||||
| // return | |||||
| // } | |||||
| // const postData = { | |||||
| // ...data, | |||||
| // skillSetId: typeof data.skillSetId === "number" ? [data.skillSetId] : data.skillSetId, | |||||
| // emergContactPhone: data.emergContactPhone.toString(), | |||||
| // phone1: data.phone1.toString(), | |||||
| // phone2: data.phone2.toString(), | |||||
| // hourlyRate: typeof data.hourlyRate === 'string' ? parseInt(data.hourlyRate.replace("$", "").replace(",", "")) : 0 | |||||
| // }; | |||||
| // if (postData.departDate?.length === 0 && postData.departReason?.length === 0) { | |||||
| // delete postData.departDate; | |||||
| // delete postData.departReason; | |||||
| // } | |||||
| // if (postData.remark?.length === 0) { | |||||
| // delete postData.remark; | |||||
| // } | |||||
| // console.log(postData); | |||||
| // setServerError(""); | |||||
| // await saveStaff(postData); | |||||
| // router.replace("/settings/staff"); | |||||
| // } catch (e) { | |||||
| // setServerError(t("An error has occurred. Please try again later.")); | |||||
| // } | |||||
| // }, | |||||
| // [router, t] | |||||
| // ); | |||||
| const onSubmitError = useCallback<SubmitErrorHandler<CreateStaffInputs>>( | |||||
| (errors) => { | |||||
| console.log(errors); | |||||
| }, | |||||
| [] | |||||
| ); | |||||
| // const onSubmitError = useCallback<SubmitErrorHandler<CreateStaffInputs>>( | |||||
| // (errors) => { | |||||
| // console.log(errors); | |||||
| // }, | |||||
| // [] | |||||
| // ); | |||||
| return ( | |||||
| <> | |||||
| {serverError && ( | |||||
| <Typography variant="body2" color="error" alignSelf="flex-end"> | |||||
| {serverError} | |||||
| </Typography> | |||||
| )} | |||||
| <CustomInputForm | |||||
| Title={Title} | |||||
| fieldLists={fieldLists} | |||||
| isActive={true} | |||||
| onSubmit={onSubmit} | |||||
| onSubmitError={onSubmitError} | |||||
| onCancel={handleCancel} | |||||
| /> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| // return ( | |||||
| // <> | |||||
| // {serverError && ( | |||||
| // <Typography variant="body2" color="error" alignSelf="flex-end"> | |||||
| // {serverError} | |||||
| // </Typography> | |||||
| // )} | |||||
| // <CustomInputForm | |||||
| // Title={Title} | |||||
| // fieldLists={fieldLists} | |||||
| // isActive={true} | |||||
| // onSubmit={onSubmit} | |||||
| // onSubmitError={onSubmitError} | |||||
| // onCancel={handleCancel} | |||||
| // /> | |||||
| // </> | |||||
| // ); | |||||
| // }; | |||||
| export default CreateStaffForm; | |||||
| // export default CreateStaffForm; | |||||