@@ -43,6 +43,12 @@ export interface CreateStaffInputs { | |||||
// team: Team[]; | // team: Team[]; | ||||
} | } | ||||
export interface salaryEffectiveInfo { | |||||
id: number; | |||||
date: string; | |||||
salaryPoint: number; | |||||
} | |||||
export const saveStaff = async (data: CreateStaffInputs) => { | export const saveStaff = async (data: CreateStaffInputs) => { | ||||
// try { | // try { | ||||
const newStaffList = await serverFetchJson(`${BASE_API_URL}/staffs/save`, { | const newStaffList = await serverFetchJson(`${BASE_API_URL}/staffs/save`, { | ||||
@@ -78,6 +78,12 @@ export interface searchInput { | |||||
currentPosition: string; | currentPosition: string; | ||||
} | } | ||||
export interface SalaryEffectiveInfo { | |||||
id: number; | |||||
date: string; | |||||
salaryPoint: number; | |||||
} | |||||
export const preloadTeamLeads = () => { | export const preloadTeamLeads = () => { | ||||
fetchTeamLeads(); | fetchTeamLeads(); | ||||
}; | }; | ||||
@@ -24,7 +24,7 @@ import { fetchSalaryCombo } from "@/app/api/salarys/actions"; | |||||
import { Check, Close, RestartAlt } from "@mui/icons-material"; | import { Check, Close, RestartAlt } from "@mui/icons-material"; | ||||
import { ServerFetchError } from "@/app/utils/fetchUtil"; | import { ServerFetchError } from "@/app/utils/fetchUtil"; | ||||
import StaffInfo from "./StaffInfo"; | import StaffInfo from "./StaffInfo"; | ||||
import { IndividualStaff } from "@/app/api/staff"; | |||||
import { IndividualStaff, SalaryEffectiveInfo } from "@/app/api/staff"; | |||||
import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | ||||
import { List, differenceBy } from "lodash"; | import { List, differenceBy } from "lodash"; | ||||
@@ -42,15 +42,16 @@ export interface comboItem { | |||||
interface formProps { | interface formProps { | ||||
Staff: IndividualStaff | Staff: IndividualStaff | ||||
combos: comboItem; | combos: comboItem; | ||||
SalaryEffectiveInfo: SalaryEffectiveInfo[]; | |||||
} | } | ||||
const EditStaff: React.FC<formProps> = ({ Staff, combos }) => { | |||||
const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo }) => { | |||||
const defaultSkillset = Staff.skillset.map((s: any) => s.skill.id) | const defaultSkillset = Staff.skillset.map((s: any) => s.skill.id) | ||||
const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
const searchParams = useSearchParams() | const searchParams = useSearchParams() | ||||
const id = parseInt(searchParams.get("id") || "0"); | const id = parseInt(searchParams.get("id") || "0"); | ||||
const formProps = useForm<CreateStaffInputs>({ | |||||
const formProps = useForm<CreateStaffInputs & { salaryEffectiveInfo: SalaryEffectiveInfo[] }>({ | |||||
defaultValues: { | defaultValues: { | ||||
staffId: Staff.staffId, | staffId: Staff.staffId, | ||||
name: Staff.name, | name: Staff.name, | ||||
@@ -73,6 +74,7 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos }) => { | |||||
departDate: dayjs(Staff.departDate).toString() || "", | departDate: dayjs(Staff.departDate).toString() || "", | ||||
departReason: Staff.departReason, | departReason: Staff.departReason, | ||||
remark: Staff.remark, | remark: Staff.remark, | ||||
salaryEffectiveInfo: SalaryEffectiveInfo | |||||
}}); | }}); | ||||
const [serverError, setServerError] = useState(""); | const [serverError, setServerError] = useState(""); | ||||
const router = useRouter(); | const router = useRouter(); | ||||
@@ -84,7 +86,7 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos }) => { | |||||
return str1 === str2 || str1 === str3 || str2 === str3; | return str1 === str2 || str1 === str3 || str2 === str3; | ||||
} | } | ||||
const onSubmit = useCallback<SubmitHandler<CreateStaffInputs>>( | |||||
const onSubmit = useCallback<SubmitHandler<CreateStaffInputs & { salaryEffectiveInfo: SalaryEffectiveInfo[] } >>( | |||||
async (data) => { | async (data) => { | ||||
try { | try { | ||||
console.log(data); | console.log(data); | ||||
@@ -190,6 +192,7 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos }) => { | |||||
departDate: !Staff.departDate ? "" : dayjs(Staff.departDate).format(INPUT_DATE_FORMAT), | departDate: !Staff.departDate ? "" : dayjs(Staff.departDate).format(INPUT_DATE_FORMAT), | ||||
departReason: Staff.departReason, | departReason: Staff.departReason, | ||||
remark: Staff.remark, | remark: Staff.remark, | ||||
salaryEffectiveInfo: SalaryEffectiveInfo | |||||
}); | }); | ||||
}, [Staff,formProps]); | }, [Staff,formProps]); | ||||
@@ -54,7 +54,7 @@ const EditStaffWrapper: React.FC<Props> & SubComponents = async ({ | |||||
console.log(Staff.data) | console.log(Staff.data) | ||||
return <EditStaff Staff={Staff.data} combos={combos}/>; | |||||
return <EditStaff Staff={Staff.data} combos={combos} SalaryEffectiveInfo={[{id:0, salaryPoint: 1, date:"2021-05-05"}]}/>; | |||||
}; | }; | ||||
EditStaffWrapper.Loading = EditStaffLoading; | EditStaffWrapper.Loading = EditStaffLoading; | ||||
@@ -0,0 +1,85 @@ | |||||
import React, { useEffect } from 'react'; | |||||
import { Modal, Box, Typography, Button, TextField, FormControl, InputLabel, Select, MenuItem, Paper, SxProps } from '@mui/material'; | |||||
import { useForm, Controller } from 'react-hook-form'; | |||||
import { useTranslation } from 'react-i18next'; | |||||
import { INPUT_DATE_FORMAT, OUTPUT_DATE_FORMAT } from '@/app/utils/formatUtil'; | |||||
import dayjs from 'dayjs'; | |||||
import { DatePicker } from '@mui/x-date-pickers'; | |||||
interface SalaryEffectiveModelProps { | |||||
open: boolean; | |||||
onClose: () => void; | |||||
modalSx?: SxProps; | |||||
onSave: () => void; | |||||
} | |||||
const modalSx: SxProps = { | |||||
position: "absolute", | |||||
top: "50%", | |||||
left: "50%", | |||||
transform: "translate(-50%, -50%)", | |||||
width: "90%", | |||||
maxWidth: "sm", | |||||
maxHeight: "90%", | |||||
padding: 3, | |||||
display: "flex", | |||||
flexDirection: "column", | |||||
gap: 2, | |||||
}; | |||||
const SalaryEffectiveModel: React.FC<SalaryEffectiveModelProps> = ({ open, onClose, modalSx: mSx, onSave }) => { | |||||
const { t } = useTranslation(); | |||||
const { control, register, formState, trigger, watch, setValue } = useForm({}); | |||||
const formValues = watch(); // This line of code is using the watch function from react-hook-form to get the current values of the form fields. | |||||
const handleClose = () => { | |||||
onClose(); | |||||
}; | |||||
const handleSave = async () => { | |||||
const isValid = await trigger(); | |||||
if (isValid) { | |||||
onSave(); | |||||
onClose(); | |||||
} | |||||
}; | |||||
useEffect(() => { | |||||
console.log(formValues) | |||||
}, [open]) | |||||
return ( | |||||
<Modal open={open} onClose={handleClose}> | |||||
<Paper sx={{ ...modalSx, ...mSx }}> | |||||
<Typography variant="h6" component="h2"> | |||||
{t('Salary Effective Date Change')} | |||||
</Typography> | |||||
<FormControl> | |||||
<TextField | |||||
label={t('Salary')} | |||||
type="number" | |||||
fullWidth | |||||
{...register('salary', { | |||||
valueAsNumber: true, | |||||
required: t('Salary is required'), | |||||
})} | |||||
error={Boolean(formState.errors.salary)} | |||||
// helperText={formState.errors.salary?.message} | |||||
/> | |||||
<Box display="flex" justifyContent="flex-end" gap={2}> | |||||
<Button variant="text" onClick={handleClose}> | |||||
{t('Cancel')} | |||||
</Button> | |||||
<Button variant="contained" onClick={handleSave}> | |||||
{t("Save")} | |||||
</Button> | |||||
</Box> | |||||
</FormControl> | |||||
</Paper> | |||||
</Modal> | |||||
); | |||||
}; | |||||
export default SalaryEffectiveModel; |
@@ -9,9 +9,10 @@ import Typography from "@mui/material/Typography"; | |||||
import { CreateGroupInputs } from "@/app/api/group/actions"; | import { CreateGroupInputs } from "@/app/api/group/actions"; | ||||
import { Controller, useFormContext } from "react-hook-form"; | import { Controller, useFormContext } from "react-hook-form"; | ||||
import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
import { useCallback, useEffect } from "react"; | |||||
import { useCallback, useEffect, useState } from "react"; | |||||
import { CreateStaffInputs } from "@/app/api/staff/actions"; | import { CreateStaffInputs } from "@/app/api/staff/actions"; | ||||
import { | import { | ||||
Button, | |||||
Checkbox, | Checkbox, | ||||
FormControl, | FormControl, | ||||
InputLabel, | InputLabel, | ||||
@@ -25,6 +26,8 @@ import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | |||||
import { DemoItem } from "@mui/x-date-pickers/internals/demo"; | import { DemoItem } from "@mui/x-date-pickers/internals/demo"; | ||||
import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | ||||
import SalaryEffectiveModel from "./SalaryEffectiveModel"; | |||||
import { SalaryEffectiveInfo } from "@/app/api/staff"; | |||||
interface Props { | interface Props { | ||||
combos: comboItem; | combos: comboItem; | ||||
@@ -45,7 +48,7 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||||
getValues, | getValues, | ||||
watch, | watch, | ||||
clearErrors, | clearErrors, | ||||
} = useFormContext<CreateStaffInputs>(); | |||||
} = useFormContext<CreateStaffInputs & { salaryEffectiveInfo: SalaryEffectiveInfo[] }>(); | |||||
const employType = [ | const employType = [ | ||||
{ id: 1, label: "FT" }, | { id: 1, label: "FT" }, | ||||
@@ -57,6 +60,21 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||||
{} | {} | ||||
); | ); | ||||
// Salary Effiective History edit modal related | |||||
const [salaryEffectiveModelOpen, setSalaaryEffectiveModelOpen] = useState(false); | |||||
const closeSalaryEffectiveModel = useCallback(() => { | |||||
setSalaaryEffectiveModelOpen(false); | |||||
}, []); | |||||
const openSalaryEffectiveModel = useCallback(() => { | |||||
setSalaaryEffectiveModelOpen(true); | |||||
}, []); | |||||
const onSalaryEffectiveSave = useCallback(async () => { | |||||
console.log(getValues()) | |||||
setSalaaryEffectiveModelOpen(false); | |||||
}, []); | |||||
const resetStaff = useCallback(() => { | const resetStaff = useCallback(() => { | ||||
console.log(defaultValues); | console.log(defaultValues); | ||||
if (defaultValues !== undefined) { | if (defaultValues !== undefined) { | ||||
@@ -275,29 +293,35 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||||
</FormControl> | </FormControl> | ||||
</Grid> | </Grid> | ||||
<Grid item xs={6}> | <Grid item xs={6}> | ||||
<FormControl fullWidth> | |||||
<InputLabel required>{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} | |||||
<FormControl fullWidth> | |||||
<InputLabel required>{t("Salary Point")}</InputLabel> | |||||
<Controller | |||||
control={control} | |||||
name="salaryId" | |||||
render={({ field }) => ( | |||||
<Box display="flex" justifyContent="space-between" alignItems="center"> | |||||
<Select | |||||
label={t("Salary Point")} | |||||
{...field} | |||||
error={Boolean(errors.salaryId)} | |||||
style={{ flex: 1, marginRight: '8px' }} | |||||
> | > | ||||
{t(salary.label)} | |||||
</MenuItem> | |||||
))} | |||||
</Select> | |||||
)} | |||||
/> | |||||
</FormControl> | |||||
{combos.salary.map((salary, index) => ( | |||||
<MenuItem | |||||
key={`${salary.id}-${index}`} | |||||
value={salary.id} | |||||
> | |||||
{t(salary.label)} | |||||
</MenuItem> | |||||
))} | |||||
</Select> | |||||
<Button variant="contained" size="small" onClick={openSalaryEffectiveModel}> | |||||
{t("Edit")} | |||||
</Button> | |||||
</Box> | |||||
)} | |||||
/> | |||||
</FormControl> | |||||
</Grid> | </Grid> | ||||
<Grid item xs={6}> | <Grid item xs={6}> | ||||
<FormControl fullWidth> | <FormControl fullWidth> | ||||
@@ -516,6 +540,11 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||||
</Grid> | </Grid> | ||||
</Box> | </Box> | ||||
</CardContent> | </CardContent> | ||||
<SalaryEffectiveModel | |||||
open={salaryEffectiveModelOpen} | |||||
onClose={closeSalaryEffectiveModel} | |||||
onSave={onSalaryEffectiveSave} | |||||
/> | |||||
</Card> | </Card> | ||||
); | ); | ||||
}; | }; | ||||
@@ -27,5 +27,6 @@ | |||||
"Remark": "備註", | "Remark": "備註", | ||||
"Reset": "重設", | "Reset": "重設", | ||||
"Confirm": "確認", | "Confirm": "確認", | ||||
"Cancel": "取消" | |||||
"Cancel": "取消", | |||||
"Save": "儲存" | |||||
} | } |