@@ -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; |