@@ -43,6 +43,12 @@ export interface CreateStaffInputs { | |||
// team: Team[]; | |||
} | |||
export interface salaryEffectiveInfo { | |||
id: number; | |||
date: string; | |||
salaryPoint: number; | |||
} | |||
export const saveStaff = async (data: CreateStaffInputs) => { | |||
// try { | |||
const newStaffList = await serverFetchJson(`${BASE_API_URL}/staffs/save`, { | |||
@@ -78,6 +78,12 @@ export interface searchInput { | |||
currentPosition: string; | |||
} | |||
export interface SalaryEffectiveInfo { | |||
id: number; | |||
date: string; | |||
salaryPoint: number; | |||
} | |||
export const preloadTeamLeads = () => { | |||
fetchTeamLeads(); | |||
}; | |||
@@ -24,7 +24,7 @@ import { fetchSalaryCombo } from "@/app/api/salarys/actions"; | |||
import { Check, Close, RestartAlt } from "@mui/icons-material"; | |||
import { ServerFetchError } from "@/app/utils/fetchUtil"; | |||
import StaffInfo from "./StaffInfo"; | |||
import { IndividualStaff } from "@/app/api/staff"; | |||
import { IndividualStaff, SalaryEffectiveInfo } from "@/app/api/staff"; | |||
import dayjs from "dayjs"; | |||
import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | |||
import { List, differenceBy } from "lodash"; | |||
@@ -42,15 +42,16 @@ export interface comboItem { | |||
interface formProps { | |||
Staff: IndividualStaff | |||
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 { t } = useTranslation(); | |||
const searchParams = useSearchParams() | |||
const id = parseInt(searchParams.get("id") || "0"); | |||
const formProps = useForm<CreateStaffInputs>({ | |||
const formProps = useForm<CreateStaffInputs & { salaryEffectiveInfo: SalaryEffectiveInfo[] }>({ | |||
defaultValues: { | |||
staffId: Staff.staffId, | |||
name: Staff.name, | |||
@@ -73,6 +74,7 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos }) => { | |||
departDate: dayjs(Staff.departDate).toString() || "", | |||
departReason: Staff.departReason, | |||
remark: Staff.remark, | |||
salaryEffectiveInfo: SalaryEffectiveInfo | |||
}}); | |||
const [serverError, setServerError] = useState(""); | |||
const router = useRouter(); | |||
@@ -84,7 +86,7 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos }) => { | |||
return str1 === str2 || str1 === str3 || str2 === str3; | |||
} | |||
const onSubmit = useCallback<SubmitHandler<CreateStaffInputs>>( | |||
const onSubmit = useCallback<SubmitHandler<CreateStaffInputs & { salaryEffectiveInfo: SalaryEffectiveInfo[] } >>( | |||
async (data) => { | |||
try { | |||
console.log(data); | |||
@@ -190,6 +192,7 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos }) => { | |||
departDate: !Staff.departDate ? "" : dayjs(Staff.departDate).format(INPUT_DATE_FORMAT), | |||
departReason: Staff.departReason, | |||
remark: Staff.remark, | |||
salaryEffectiveInfo: SalaryEffectiveInfo | |||
}); | |||
}, [Staff,formProps]); | |||
@@ -54,7 +54,7 @@ const EditStaffWrapper: React.FC<Props> & SubComponents = async ({ | |||
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; | |||
@@ -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 { Controller, useFormContext } from "react-hook-form"; | |||
import { useTranslation } from "react-i18next"; | |||
import { useCallback, useEffect } from "react"; | |||
import { useCallback, useEffect, useState } from "react"; | |||
import { CreateStaffInputs } from "@/app/api/staff/actions"; | |||
import { | |||
Button, | |||
Checkbox, | |||
FormControl, | |||
InputLabel, | |||
@@ -25,6 +26,8 @@ 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"; | |||
import SalaryEffectiveModel from "./SalaryEffectiveModel"; | |||
import { SalaryEffectiveInfo } from "@/app/api/staff"; | |||
interface Props { | |||
combos: comboItem; | |||
@@ -45,7 +48,7 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
getValues, | |||
watch, | |||
clearErrors, | |||
} = useFormContext<CreateStaffInputs>(); | |||
} = useFormContext<CreateStaffInputs & { salaryEffectiveInfo: SalaryEffectiveInfo[] }>(); | |||
const employType = [ | |||
{ 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(() => { | |||
console.log(defaultValues); | |||
if (defaultValues !== undefined) { | |||
@@ -275,29 +293,35 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
</FormControl> | |||
</Grid> | |||
<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 item xs={6}> | |||
<FormControl fullWidth> | |||
@@ -516,6 +540,11 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
</Grid> | |||
</Box> | |||
</CardContent> | |||
<SalaryEffectiveModel | |||
open={salaryEffectiveModelOpen} | |||
onClose={closeSalaryEffectiveModel} | |||
onSave={onSalaryEffectiveSave} | |||
/> | |||
</Card> | |||
); | |||
}; | |||
@@ -27,5 +27,6 @@ | |||
"Remark": "備註", | |||
"Reset": "重設", | |||
"Confirm": "確認", | |||
"Cancel": "取消" | |||
"Cancel": "取消", | |||
"Save": "儲存" | |||
} |