@@ -15,25 +15,25 @@ export interface CreateCustomInputs { | |||
export interface CreateStaffInputs { | |||
name: string; | |||
currentPositionId: number; | |||
joinPositionId: number; | |||
staffId: string; | |||
companyId: number; | |||
gradeId: number; | |||
teamId: number; | |||
salaryId: number; | |||
email: string; | |||
skillSetId?: number[]; | |||
joinDate: string; | |||
currentPositionId: number; | |||
joinPositionId: number; | |||
gradeId?: number; | |||
teamId?: number | |||
departmentId: number; | |||
phone1: string; | |||
phone2: string; | |||
hourlyRate: string | number; | |||
phone2?: string; | |||
email: string; | |||
emergContactName: string; | |||
emergContactPhone: 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 { | |||
@@ -53,8 +53,8 @@ const ChangePassword: React.FC = () => { | |||
password: data.password, | |||
newPassword: data.newPassword | |||
} | |||
// await changePassword(postData) | |||
// router.replace("/home") | |||
await changePassword(postData) | |||
router.replace("/home") | |||
} catch (e) { | |||
console.log(e) | |||
setServerError(t("An error has occurred. Please try again later.")); | |||
@@ -11,7 +11,7 @@ import { | |||
useForm, | |||
} from "react-hook-form"; | |||
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 { comboProp, fetchCompanyCombo } from "@/app/api/companys/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 { fetchSkillCombo } from "@/app/api/skill/actions"; | |||
import { fetchSalaryCombo } from "@/app/api/salarys/actions"; | |||
import StaffInfo from "./StaffInfo"; | |||
import { Check, Close } from "@mui/icons-material"; | |||
interface Field { | |||
id: string; | |||
@@ -43,171 +45,133 @@ export interface comboItem { | |||
} | |||
interface formProps { | |||
Title?: string[]; | |||
// Title?: string[]; | |||
combos: comboItem; | |||
} | |||
const CreateStaff: React.FC<formProps> = ({ Title, combos }) => { | |||
const CreateStaff: React.FC<formProps> = ({ combos }) => { | |||
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 ( | |||
<> | |||
<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; |