@@ -15,6 +15,7 @@ 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 { fetchGradesLog, fetchPositionLog, fetchTeamLog } from "@/app/api/staffInfoHistory"; | |||
// export const metadata: Metadata = { | |||
// title: "staff-edit", | |||
@@ -23,18 +24,21 @@ import { fetchSalaryCombo } from "@/app/api/salarys/actions"; | |||
const EditStaffPage: React.FC<searchParamsProps> = async ({ | |||
searchParams, | |||
}) => { | |||
const id = parseInt(searchParams.id as string) | |||
// preload | |||
fetchIndivStaff(parseInt(searchParams.id as string)), | |||
fetchCompanyCombo(), | |||
fetchTeamCombo(), | |||
fetchDepartmentCombo(), | |||
fetchPositionCombo(), | |||
fetchGradeCombo(), | |||
fetchSkillCombo(), | |||
fetchSalaryCombo(), | |||
fetchStaffSalaryEffectiveInfo(parseInt(searchParams.id as string)), | |||
fetchStaffInvolvedProjects(parseInt(searchParams.id as string)) | |||
fetchIndivStaff(id) | |||
fetchCompanyCombo() | |||
fetchTeamCombo() | |||
fetchDepartmentCombo() | |||
fetchPositionCombo() | |||
fetchGradeCombo() | |||
fetchSkillCombo() | |||
fetchSalaryCombo() | |||
fetchStaffSalaryEffectiveInfo(id) | |||
fetchStaffInvolvedProjects(id) | |||
fetchGradesLog(id) | |||
fetchPositionLog(id) | |||
fetchTeamLog(id) | |||
return ( | |||
<> | |||
@@ -13,7 +13,26 @@ export interface CreateCustomInputs { | |||
// Miscellaneous | |||
expectedProjectFee: string; | |||
} | |||
export type teamHistory = { | |||
id: number, | |||
team: string | number, | |||
from: Date | string, | |||
to?: Date | string | |||
} | |||
export type gradeHistory = { | |||
id: number, | |||
grade: string | number, | |||
from: Date | string, | |||
to?: Date | string | |||
} | |||
export type positionHistory = { | |||
id: number, | |||
position: string | number, | |||
from: Date | string, | |||
to?: Date | string | |||
} | |||
export interface CreateStaffInputs { | |||
id?: number | |||
name: string; | |||
@@ -37,6 +56,12 @@ export interface CreateStaffInputs { | |||
departReason?: string; | |||
remark?: string; | |||
salaryEffectiveInfo?: any; | |||
teamHistory: teamHistory[]; | |||
delTeamHistory?: number[]; | |||
gradeHistory: gradeHistory[]; | |||
delGradeHistory?: number[]; | |||
positionHistory: positionHistory[]; | |||
delPositionHistory?: number[]; | |||
} | |||
export interface records { | |||
@@ -10,8 +10,8 @@ export type GradeLogInfo = { | |||
staffName: String, | |||
staffCode: String, | |||
grade: Grade, | |||
from: String, | |||
to?: String, | |||
from: number[], | |||
to?: number[], | |||
} | |||
export type PositionLogInfo = { | |||
@@ -20,8 +20,8 @@ export type PositionLogInfo = { | |||
staffName: String, | |||
staffCode: String, | |||
position: PositionResult, | |||
from: String, | |||
to?: String, | |||
from: number[], | |||
to?: number[], | |||
} | |||
type team = { | |||
@@ -35,11 +35,11 @@ type team = { | |||
export type TeamLogInfo = { | |||
id: number, | |||
staffId: number, | |||
staffName: String, | |||
staffCode: String, | |||
Team: team, | |||
from: String, | |||
to?: String, | |||
staffName: string, | |||
staffCode: string, | |||
team: team, | |||
from: number[], | |||
to?: number[], | |||
} | |||
export const fetchGradesLog = cache(async (staffId: number) => { | |||
@@ -10,17 +10,18 @@ import { | |||
SubmitHandler, | |||
useForm, | |||
} from "react-hook-form"; | |||
import { CreateStaffInputs, saveStaff } from "@/app/api/staff/actions"; | |||
import { CreateStaffInputs, saveStaff, teamHistory } from "@/app/api/staff/actions"; | |||
import { Button, Stack, Tab, Tabs, TabsProps, Typography } from "@mui/material"; | |||
// import CreateStaffForm from "../CreateStaffForm"; | |||
import { comboProp } from "@/app/api/companys/actions"; | |||
// import StaffInfo from "./StaffInfo"; | |||
import { Check, Close, RestartAlt } from "@mui/icons-material"; | |||
import { Check, Close, ConstructionOutlined, RestartAlt } from "@mui/icons-material"; | |||
import StaffInfo from "./StaffInfo"; | |||
import { IndividualStaff, projects, SalaryEffectiveInfo } from "@/app/api/staff"; | |||
import dayjs from "dayjs"; | |||
import ProjectHistory from "./ProjectHistory"; | |||
import { InfoHistory } from "./EditStaffWrapper"; | |||
import { fetchIndivTeam } from "@/app/api/team"; | |||
// import { useGridApiContext } from '@mui/x-data-grid'; | |||
export interface comboItem { | |||
@@ -44,13 +45,15 @@ interface formProps { | |||
const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo, InvolvedProject, InfoHistory }) => { | |||
console.log(InfoHistory) | |||
console.log(combos.position) | |||
const defaultSkillset = Staff.skillset.map((s: any) => s.skill.id) | |||
const { t } = useTranslation(); | |||
const searchParams = useSearchParams() | |||
const [tabIndex, setTabIndex] = useState(0); | |||
const id = parseInt(searchParams.get("id") || "0"); | |||
const formProps = useForm<CreateStaffInputs & { salaryEffectiveInfo: SalaryEffectiveInfo[] } & { delSalaryEffectiveInfo: number[] }>({ | |||
const formProps = useForm<CreateStaffInputs | |||
& { salaryEffectiveInfo: SalaryEffectiveInfo[] } | |||
& { delSalaryEffectiveInfo: number[] }>({ | |||
defaultValues: { | |||
staffId: Staff.staffId, | |||
name: Staff.name, | |||
@@ -79,18 +82,42 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo, In | |||
salaryPoint: combos.salary.filter(sal => sal.id === item.salaryPoint)[0].label, | |||
date: dayjs(item.date).toDate(), | |||
})}), | |||
delSalaryEffectiveInfo: [] | |||
delSalaryEffectiveInfo: [], | |||
teamHistory: InfoHistory.teamLog ? InfoHistory.teamLog.map(item => { | |||
return ({ | |||
id: item.id, | |||
team: item.team.name, | |||
from: dayjs(item.from.join()).toDate(), | |||
to: item.to ? dayjs(item.to.join()).toDate() : "", | |||
}) | |||
}) : [], | |||
delTeamHistory: [], | |||
gradeHistory: InfoHistory.gradeLog ? InfoHistory.gradeLog.map(item => { | |||
return ({ | |||
id: item.id, | |||
grade: item.grade.name, | |||
from: dayjs(item.from.join()).toDate(), | |||
to: item.to ? dayjs(item.to.join()).toDate() : "", | |||
}) | |||
}) : [], | |||
delGradeHistory: [], | |||
positionHistory: InfoHistory.positionLog ? InfoHistory.positionLog.map(item => { | |||
return ({ | |||
id: item.id, | |||
position: item.position.name, | |||
from: dayjs(item.from.join()).toDate(), | |||
to: item.to ? dayjs(item.to.join()).toDate() : "", | |||
}) | |||
}) : [], | |||
delPositionHistory: [], | |||
}}); | |||
const [serverError, setServerError] = useState(""); | |||
const router = useRouter(); | |||
// const [tabIndex, setTabIndex] = useState(0); | |||
const errors = formProps.formState.errors; | |||
const checkDuplicates = (str1: string, str2: string, str3: string) => { | |||
return str1 === str2 || str1 === str3 || str2 === str3; | |||
} | |||
const onSubmit = useCallback<SubmitHandler<CreateStaffInputs & { salaryEffectiveInfo: SalaryEffectiveInfo[] } >>( | |||
async (data) => { | |||
try { | |||
@@ -148,15 +175,45 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo, In | |||
if (haveError) { | |||
return | |||
} | |||
console.log("passed") | |||
const teamHistory = data.teamHistory.map((item) => ({ | |||
id: item.id, | |||
team: combos.team.filter(team => team.label === item.team)[0].id, | |||
from: dayjs(item.from).format('YYYY-MM-DD'), | |||
to: (item.to as string).length != 0 ? dayjs(item.to).format('YYYY-MM-DD') : undefined, | |||
})) | |||
const gradeHistory = data.gradeHistory.map((item) => ({ | |||
id: item.id, | |||
grade: combos.grade.filter(grade => grade.label === item.grade)[0].id, | |||
from: dayjs(item.from).format('YYYY-MM-DD'), | |||
to: (item.to as string).length != 0 ? dayjs(item.to).format('YYYY-MM-DD') : undefined, | |||
})) | |||
const positionHistory = data.positionHistory.map((item) => ({ | |||
id: item.id, | |||
position: combos.position.filter(position => position.label === item.position)[0].id, | |||
from: dayjs(item.from).format('YYYY-MM-DD'), | |||
to: (item.to as string).length != 0 ? dayjs(item.to).format('YYYY-MM-DD') : undefined, | |||
})) | |||
console.log(teamHistory) | |||
console.log(gradeHistory) | |||
console.log(positionHistory) | |||
const salaryEffectiveInfo = data.salaryEffectiveInfo.map((item: SalaryEffectiveInfo) => ({ | |||
id: item.id, | |||
salaryPoint: chopSalaryPoints(item.salaryPoint), | |||
date: dayjs(item.date).format('YYYY-MM-DD').toString() | |||
})) | |||
const postData: CreateStaffInputs = { | |||
id: id, | |||
...data, | |||
salaryEffectiveInfo: data.salaryEffectiveInfo.map((item: SalaryEffectiveInfo) => ({ | |||
id: item.id, | |||
salaryPoint: chopSalaryPoints(item.salaryPoint), | |||
date: dayjs(item.date).format('YYYY-MM-DD').toString() | |||
})) | |||
salaryEffectiveInfo: salaryEffectiveInfo, | |||
teamHistory: teamHistory, | |||
gradeHistory: gradeHistory, | |||
positionHistory: positionHistory, | |||
delTeamHistory: data.delTeamHistory ? data.delTeamHistory : [], | |||
delGradeHistory: data.delGradeHistory ? data.delGradeHistory : [], | |||
delPositionHistory: data.delPositionHistory ? data.delPositionHistory : [], | |||
} | |||
if (postData.joinDate) { | |||
postData.joinDate = dayjs(postData.joinDate).format("YYYY-MM-DD") | |||
@@ -166,6 +223,7 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo, In | |||
} | |||
console.log(postData) | |||
await saveStaff(postData) | |||
return | |||
router.replace("/settings/staff") | |||
} catch (e: any) { | |||
console.log(e); | |||
@@ -203,61 +261,6 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo, In | |||
[] | |||
); | |||
// const resetStaff = useCallback(() => { | |||
// window.location.reload() | |||
// console.log(dayjs(Staff.joinDate).format(INPUT_DATE_FORMAT)) | |||
// console.log(formProps.getValues("joinDate")) | |||
// formProps.setValue('salaryEffectiveInfo', SalaryEffectiveInfo.map(item => { | |||
// return ({ | |||
// id: item.id, | |||
// salaryPoint: combos.salary.filter(sal => sal.id === item.salaryPoint)[0].label, | |||
// date: dayjs(item.date).toDate(), | |||
// })})) | |||
// formProps.reset({ | |||
// staffId: Staff.staffId, | |||
// name: Staff.name, | |||
// companyId: Staff.company.id, | |||
// teamId: Staff.team?.id, | |||
// departmentId: Staff.department?.id, | |||
// gradeId: Staff.grade?.id, | |||
// skillSetId: defaultSkillset, | |||
// currentPositionId: Staff.currentPosition?.id, | |||
// salaryId: Staff.salary.salaryPoint, | |||
// employType: Staff.employType, | |||
// email: Staff.email, | |||
// phone1: Staff.phone1, | |||
// phone2: Staff.phone2, | |||
// emergContactName: Staff.emergContactName, | |||
// emergContactPhone: Staff.emergContactPhone, | |||
// joinDate: Staff.joinDate ? dayjs(Staff.joinDate).format(INPUT_DATE_FORMAT) : "", | |||
// joinPositionId: Staff.joinPosition?.id, | |||
// departDate: !Staff.departDate ? "" : dayjs(Staff.departDate).format(INPUT_DATE_FORMAT), | |||
// departReason: Staff.departReason, | |||
// remark: Staff.remark, | |||
// salaryEffectiveInfo: SalaryEffectiveInfo.map(item => { | |||
// return ({ | |||
// id: item.id, | |||
// salaryPoint: combos.salary.filter(sal => sal.id === item.salaryPoint)[0].label, | |||
// date: dayjs(item.date).toDate(), | |||
// })}) | |||
// }); | |||
// }, []); | |||
// useEffect(() => { | |||
// formProps.setValue('salaryEffectiveInfo', SalaryEffectiveInfo.map(item => { | |||
// return ({ | |||
// id: item.id, | |||
// salaryPoint: combos.salary.filter(sal => sal.id === item.salaryPoint)[0].label, | |||
// date: dayjs(item.date).toDate(), | |||
// })}) | |||
// ) | |||
// }, [formProps]); | |||
// useEffect(() => { | |||
// resetStaff() | |||
// }, [Staff, formProps, combos]); | |||
return ( | |||
<> | |||
<FormProvider {...formProps}> | |||
@@ -283,11 +286,12 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo, In | |||
variant="scrollable" | |||
> | |||
<Tab label={t("Info")}/> | |||
<Tab label={t("Involved Project History")} /> | |||
<Tab label={t("Info History")} /> | |||
<Tab label={t("Involved Project History")} /> | |||
</Tabs> | |||
</Stack> | |||
{tabIndex == 0 && Staff && <StaffInfo combos={combos} />} | |||
{tabIndex == 1 && <ProjectHistory InvolvedProject={InvolvedProject}/>} | |||
{tabIndex == 2 && <ProjectHistory InvolvedProject={InvolvedProject}/>} | |||
{tabIndex == 0 && | |||
<Stack direction="row" justifyContent="flex-end" gap={1}> | |||
<Button | |||
@@ -20,7 +20,7 @@ interface Props { | |||
} | |||
export type InfoHistory = { | |||
gradesLog?: GradeLogInfo[], | |||
gradeLog?: GradeLogInfo[], | |||
positionLog?: PositionLogInfo[], | |||
teamLog?: TeamLogInfo[], | |||
} | |||
@@ -41,7 +41,7 @@ const EditStaffWrapper: React.FC<Props> & SubComponents = async ({ | |||
SalaryCombo, | |||
SalaryEffectiveInfo, | |||
InvolvedProject, | |||
GradesLog, | |||
GradeLog, | |||
PositionLog, | |||
TeamLog, | |||
] = await Promise.all([ | |||
@@ -69,16 +69,15 @@ const EditStaffWrapper: React.FC<Props> & SubComponents = async ({ | |||
skill: SkillCombo.records, | |||
salary: SalaryCombo.records, | |||
} | |||
console.log(TeamLog) | |||
const InfoHistory: InfoHistory = { | |||
gradesLog: GradesLog, | |||
gradeLog: GradeLog, | |||
positionLog: PositionLog, | |||
teamLog: TeamLog, | |||
} | |||
console.log(InfoHistory) | |||
Staff.data.joinDate = Staff.data.joinDate && dateArrayToString(Staff.data.joinDate) as string | |||
Staff.data.departDate = Staff.data.departDate && dateArrayToString(Staff.data.departDate) as string | |||
Staff.data.joinDate = Staff.data.joinDate && dateArrayToString(Staff.data.joinDate) as string | |||
Staff.data.departDate = Staff.data.departDate && dateArrayToString(Staff.data.departDate) as string | |||
return <EditStaff Staff={Staff.data} combos={combos} SalaryEffectiveInfo={SalaryEffectiveInfo} InvolvedProject={InvolvedProject} InfoHistory={InfoHistory}/>; | |||
}; | |||
@@ -0,0 +1,207 @@ | |||
import { Box, Button, Modal, Paper, SxProps, Typography } from "@mui/material" | |||
import StyledDataGrid from "../StyledDataGrid" | |||
import { useTranslation } from "react-i18next"; | |||
import { useFormContext } from "react-hook-form"; | |||
import { useCallback, useEffect, useMemo, useState } from "react"; | |||
import { GridActionsCellItem, GridEventListener, GridRowEditStopReasons, GridRowModel, GridRowModes, GridRowModesModel } from "@mui/x-data-grid"; | |||
import AddIcon from '@mui/icons-material/Add'; | |||
import SaveIcon from '@mui/icons-material/Save'; | |||
import DeleteIcon from '@mui/icons-material/Delete'; | |||
import CancelIcon from '@mui/icons-material/Cancel'; | |||
import EditIcon from '@mui/icons-material/Edit'; | |||
interface Props { | |||
open: boolean; | |||
onClose: () => void; | |||
columns: any[] | |||
} | |||
const modalSx: SxProps = { | |||
position: "absolute", | |||
top: "50%", | |||
left: "50%", | |||
transform: "translate(-50%, -50%)", | |||
width: "90%", | |||
maxWidth: "auto", | |||
maxHeight: "auto", | |||
padding: 3, | |||
display: "flex", | |||
flexDirection: "column", | |||
gap: 2, | |||
}; | |||
const GradeHistoryModal: React.FC<Props> = async ({ open, onClose, columns }) => { | |||
const { | |||
t, | |||
// i18n: { language }, | |||
} = useTranslation(); | |||
const { control, register, formState, trigger, watch, setValue, getValues } = useFormContext(); | |||
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | |||
const [count, setCount] = useState(0); | |||
const [_rows, setRows] = useState(() => { | |||
const list = getValues('gradeHistory') | |||
return list && list.length > 0 ? list : [] | |||
}); | |||
const [_delRows, setDelRows] = useState<number[]>([]); | |||
const formValues = watch(); | |||
const handleClose = () => { | |||
onClose(); | |||
}; | |||
const handleRowEditStop: GridEventListener<"rowEditStop"> = ( | |||
params, | |||
event, | |||
) => { | |||
if (params.reason === GridRowEditStopReasons.rowFocusOut) { | |||
event.defaultMuiPrevented = true; | |||
} | |||
}; | |||
// handle row update here | |||
const processRowUpdate = | |||
// useCallback( | |||
(newRow: GridRowModel) => { | |||
console.log(newRow) | |||
const updatedRow = { ...newRow, updated: true }; | |||
console.log(_rows) | |||
if (_rows.length != 0) { | |||
setRows((prev: any[]) => prev?.map((row: any) => (row.id === newRow.id ? updatedRow : row))); | |||
} | |||
return updatedRow; | |||
} | |||
// , [_rows, setValue, setRows]) | |||
const handleSaveClick = useCallback( | |||
(id: any) => () => { | |||
setRowModesModel((prevRowModesModel) => ({ | |||
...prevRowModesModel, | |||
[id]: { mode: GridRowModes.View } | |||
})); | |||
}, | |||
[setRowModesModel] | |||
); | |||
const handleCancelClick = useCallback( | |||
(id: any) => () => { | |||
setRowModesModel((prevRowModesModel) => ({ | |||
...prevRowModesModel, | |||
[id]: { mode: GridRowModes.View, ignoreModifications: true } | |||
})); | |||
}, | |||
[setRowModesModel] | |||
); | |||
const handleEditClick = useCallback( | |||
(id: any) => () => { | |||
setRowModesModel((prevRowModesModel) => ({ | |||
...prevRowModesModel, | |||
[id]: { mode: GridRowModes.Edit } | |||
})); | |||
}, | |||
[setRowModesModel] | |||
); | |||
const handleDeleteClick = useCallback( | |||
(id: any) => () => { | |||
setRows((prevRows: any) => prevRows.filter((row: any) => row.id !== id)); | |||
setCount((prev: number) => prev - 1); | |||
setDelRows((prevRowsId: number[]) => [...prevRowsId, id]) | |||
}, | |||
[setRows, setCount, setDelRows] | |||
); | |||
useEffect(()=> { | |||
console.log(_rows) | |||
setValue('gradeHistory', _rows) | |||
setValue('delGradeHistory', _delRows) | |||
}, [_rows, _delRows]) | |||
const defaultCol = useMemo( | |||
() => ( | |||
{ | |||
field: 'actions', | |||
type: 'actions', | |||
headerName: 'edit', | |||
width: 100, | |||
cellClassName: 'actions', | |||
getActions: ({ id }: { id: number }) => { | |||
const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; | |||
if (isInEditMode) { | |||
return [ | |||
<GridActionsCellItem | |||
icon={<SaveIcon />} | |||
label="Save" | |||
key="edit" | |||
sx={{ | |||
color: 'primary.main' | |||
}} | |||
onClick={handleSaveClick(id)} | |||
/>, | |||
<GridActionsCellItem | |||
icon={<CancelIcon />} | |||
label="Cancel" | |||
key="edit" | |||
onClick={handleCancelClick(id)} | |||
/> | |||
]; | |||
} | |||
return [ | |||
<GridActionsCellItem | |||
icon={<EditIcon />} | |||
label="Edit" | |||
className="textPrimary" | |||
onClick={handleEditClick(id)} | |||
color="inherit" | |||
key="edit" | |||
/>, | |||
<GridActionsCellItem | |||
icon={<DeleteIcon />} | |||
label="Delete" | |||
sx={{ | |||
color: 'error.main' | |||
}} | |||
onClick={handleDeleteClick(id)} color="inherit" key="edit" /> | |||
]; | |||
} | |||
} | |||
), [rowModesModel, handleSaveClick, handleCancelClick, handleEditClick, handleDeleteClick] | |||
) | |||
let _columns: any[] = [] | |||
if (columns) { | |||
_columns = [...columns, defaultCol] | |||
} | |||
return ( | |||
<Modal open={open} onClose={handleClose}> | |||
<Paper sx={{ ...modalSx }}> | |||
<Typography variant="h6" component="h2"> | |||
{t('GradeHistoryModal')} | |||
</Typography> | |||
<StyledDataGrid | |||
rows={_rows} | |||
columns={_columns} | |||
editMode="row" | |||
rowModesModel={rowModesModel} | |||
onRowModesModelChange={setRowModesModel} | |||
onRowEditStop={handleRowEditStop} | |||
processRowUpdate={processRowUpdate} | |||
// slots={{ | |||
// toolbar: EditToolbar | |||
// }} | |||
// slotProps={{ | |||
// toolbar: {count, setCount, setRows, setRowModesModel, _columns} | |||
// }} | |||
/> | |||
<Box display="flex" justifyContent="flex-end" gap={2}> | |||
<Button variant="text" onClick={handleClose}> | |||
{t('Cancel')} | |||
</Button> | |||
<Button variant="contained" onClick={handleClose}> | |||
{t("Save")} | |||
</Button> | |||
</Box> | |||
{/* </FormControl> */} | |||
</Paper> | |||
</Modal> | |||
) | |||
} | |||
export default GradeHistoryModal |
@@ -1,11 +0,0 @@ | |||
interface Props { | |||
gradeLog?: any[] | |||
} | |||
const PositionGradeHistory: React.FC<Props> = async ({ gradeLog }) => { | |||
return null | |||
} |
@@ -0,0 +1,207 @@ | |||
import { Box, Button, Modal, Paper, SxProps, Typography } from "@mui/material" | |||
import StyledDataGrid from "../StyledDataGrid" | |||
import { useTranslation } from "react-i18next"; | |||
import { useFormContext } from "react-hook-form"; | |||
import { useCallback, useEffect, useMemo, useState } from "react"; | |||
import { GridActionsCellItem, GridEventListener, GridRowEditStopReasons, GridRowModel, GridRowModes, GridRowModesModel } from "@mui/x-data-grid"; | |||
import AddIcon from '@mui/icons-material/Add'; | |||
import SaveIcon from '@mui/icons-material/Save'; | |||
import DeleteIcon from '@mui/icons-material/Delete'; | |||
import CancelIcon from '@mui/icons-material/Cancel'; | |||
import EditIcon from '@mui/icons-material/Edit'; | |||
interface Props { | |||
open: boolean; | |||
onClose: () => void; | |||
columns: any[] | |||
} | |||
const modalSx: SxProps = { | |||
position: "absolute", | |||
top: "50%", | |||
left: "50%", | |||
transform: "translate(-50%, -50%)", | |||
width: "90%", | |||
maxWidth: "auto", | |||
maxHeight: "auto", | |||
padding: 3, | |||
display: "flex", | |||
flexDirection: "column", | |||
gap: 2, | |||
}; | |||
const PositionHistoryModal: React.FC<Props> = async ({ open, onClose, columns }) => { | |||
const { | |||
t, | |||
// i18n: { language }, | |||
} = useTranslation(); | |||
const { control, register, formState, trigger, watch, setValue, getValues } = useFormContext(); | |||
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | |||
const [count, setCount] = useState(0); | |||
const [_rows, setRows] = useState(() => { | |||
const list = getValues('positionHistory') | |||
return list && list.length > 0 ? list : [] | |||
}); | |||
const [_delRows, setDelRows] = useState<number[]>([]); | |||
const formValues = watch(); | |||
const handleClose = () => { | |||
onClose(); | |||
}; | |||
const handleRowEditStop: GridEventListener<"rowEditStop"> = ( | |||
params, | |||
event, | |||
) => { | |||
if (params.reason === GridRowEditStopReasons.rowFocusOut) { | |||
event.defaultMuiPrevented = true; | |||
} | |||
}; | |||
// handle row update here | |||
const processRowUpdate = | |||
// useCallback( | |||
(newRow: GridRowModel) => { | |||
console.log(newRow) | |||
const updatedRow = { ...newRow, updated: true }; | |||
console.log(_rows) | |||
if (_rows.length != 0) { | |||
setRows((prev: any[]) => prev?.map((row: any) => (row.id === newRow.id ? updatedRow : row))); | |||
} | |||
return updatedRow; | |||
} | |||
// , [_rows, setValue, setRows]) | |||
const handleSaveClick = useCallback( | |||
(id: any) => () => { | |||
setRowModesModel((prevRowModesModel) => ({ | |||
...prevRowModesModel, | |||
[id]: { mode: GridRowModes.View } | |||
})); | |||
}, | |||
[setRowModesModel] | |||
); | |||
const handleCancelClick = useCallback( | |||
(id: any) => () => { | |||
setRowModesModel((prevRowModesModel) => ({ | |||
...prevRowModesModel, | |||
[id]: { mode: GridRowModes.View, ignoreModifications: true } | |||
})); | |||
}, | |||
[setRowModesModel] | |||
); | |||
const handleEditClick = useCallback( | |||
(id: any) => () => { | |||
setRowModesModel((prevRowModesModel) => ({ | |||
...prevRowModesModel, | |||
[id]: { mode: GridRowModes.Edit } | |||
})); | |||
}, | |||
[setRowModesModel] | |||
); | |||
const handleDeleteClick = useCallback( | |||
(id: any) => () => { | |||
setRows((prevRows: any) => prevRows.filter((row: any) => row.id !== id)); | |||
setCount((prev: number) => prev - 1); | |||
setDelRows((prevRowsId: number[]) => [...prevRowsId, id]) | |||
}, | |||
[setRows, setCount, setDelRows] | |||
); | |||
useEffect(()=> { | |||
console.log(_rows) | |||
setValue('positionHistory', _rows) | |||
setValue('delPositionHistory', _delRows) | |||
}, [_rows, _delRows]) | |||
const defaultCol = useMemo( | |||
() => ( | |||
{ | |||
field: 'actions', | |||
type: 'actions', | |||
headerName: 'edit', | |||
width: 100, | |||
cellClassName: 'actions', | |||
getActions: ({ id }: { id: number }) => { | |||
const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; | |||
if (isInEditMode) { | |||
return [ | |||
<GridActionsCellItem | |||
icon={<SaveIcon />} | |||
label="Save" | |||
key="edit" | |||
sx={{ | |||
color: 'primary.main' | |||
}} | |||
onClick={handleSaveClick(id)} | |||
/>, | |||
<GridActionsCellItem | |||
icon={<CancelIcon />} | |||
label="Cancel" | |||
key="edit" | |||
onClick={handleCancelClick(id)} | |||
/> | |||
]; | |||
} | |||
return [ | |||
<GridActionsCellItem | |||
icon={<EditIcon />} | |||
label="Edit" | |||
className="textPrimary" | |||
onClick={handleEditClick(id)} | |||
color="inherit" | |||
key="edit" | |||
/>, | |||
<GridActionsCellItem | |||
icon={<DeleteIcon />} | |||
label="Delete" | |||
sx={{ | |||
color: 'error.main' | |||
}} | |||
onClick={handleDeleteClick(id)} color="inherit" key="edit" /> | |||
]; | |||
} | |||
} | |||
), [rowModesModel, handleSaveClick, handleCancelClick, handleEditClick, handleDeleteClick] | |||
) | |||
let _columns: any[] = [] | |||
if (columns) { | |||
_columns = [...columns, defaultCol] | |||
} | |||
return ( | |||
<Modal open={open} onClose={handleClose}> | |||
<Paper sx={{ ...modalSx }}> | |||
<Typography variant="h6" component="h2"> | |||
{t('PositionHistoryModal')} | |||
</Typography> | |||
<StyledDataGrid | |||
rows={_rows} | |||
columns={_columns} | |||
editMode="row" | |||
rowModesModel={rowModesModel} | |||
onRowModesModelChange={setRowModesModel} | |||
onRowEditStop={handleRowEditStop} | |||
processRowUpdate={processRowUpdate} | |||
// slots={{ | |||
// toolbar: EditToolbar | |||
// }} | |||
// slotProps={{ | |||
// toolbar: {count, setCount, setRows, setRowModesModel, _columns} | |||
// }} | |||
/> | |||
<Box display="flex" justifyContent="flex-end" gap={2}> | |||
<Button variant="text" onClick={handleClose}> | |||
{t('Cancel')} | |||
</Button> | |||
<Button variant="contained" onClick={handleClose}> | |||
{t("Save")} | |||
</Button> | |||
</Box> | |||
{/* </FormControl> */} | |||
</Paper> | |||
</Modal> | |||
) | |||
} | |||
export default PositionHistoryModal |
@@ -9,7 +9,7 @@ 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, useMemo, useState } from "react"; | |||
import { useCallback, useEffect, useMemo, useReducer, useState } from "react"; | |||
import { CreateStaffInputs } from "@/app/api/staff/actions"; | |||
import { | |||
Button, | |||
@@ -25,16 +25,50 @@ import { | |||
import { comboItem } from "./EditStaff"; | |||
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"; | |||
import SalaryEffectiveModel from "./SalaryEffectiveModel"; | |||
import { SalaryEffectiveInfo, projects } from "@/app/api/staff"; | |||
import TeamHistoryModal from "./TeamHistoryModal"; | |||
import GradeHistoryModal from "./GradeHistoryModal"; | |||
import PositionHistoryModal from "./PositionHistoryModal"; | |||
interface Props { | |||
combos: comboItem; | |||
// InvolvedProject?: projects[] | |||
} | |||
// se = salary effective | |||
const initState = { | |||
teamModal: false, | |||
seModal: false, | |||
gradeModal: false, | |||
positionModal: false, | |||
} | |||
const enum REDUCER_ACTION_TYPE { | |||
TOGGLE_TEAM_MODAL, | |||
TOGGLE_SALARY_EFFECTIVE_MODAL, | |||
TOGGLE_GRADE_MODAL, | |||
TOGGLE_POSITION_MODAL, | |||
} | |||
type ReducerAction = { | |||
type: REDUCER_ACTION_TYPE | |||
} | |||
const reducer = (state: typeof initState, action: ReducerAction): typeof initState => { | |||
switch (action.type) { | |||
case REDUCER_ACTION_TYPE.TOGGLE_TEAM_MODAL: | |||
return { ...state, teamModal: !state.teamModal }; | |||
case REDUCER_ACTION_TYPE.TOGGLE_SALARY_EFFECTIVE_MODAL: | |||
return { ...state, seModal: !state.seModal }; | |||
case REDUCER_ACTION_TYPE.TOGGLE_GRADE_MODAL: | |||
return { ...state, gradeModal: !state.gradeModal }; | |||
case REDUCER_ACTION_TYPE.TOGGLE_POSITION_MODAL: | |||
return { ...state, positionModal: !state.positionModal }; | |||
default: | |||
return state; | |||
} | |||
} | |||
const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
const { | |||
@@ -62,12 +96,11 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
(acc, skill) => ({ ...acc, [skill.id]: skill.label }), | |||
{} | |||
); | |||
// Salary Effiective History edit modal related | |||
const [salaryEffectiveModelOpen, setSalaaryEffectiveModelOpen] = useState(false); | |||
const controlSalaryEffectiveModel = useCallback(() => { | |||
setSalaaryEffectiveModelOpen((prev: Boolean) => !prev); | |||
}, []); | |||
const [state, dispatch] = useReducer(reducer, initState) | |||
const toggleSeModal = () => dispatch({ type: REDUCER_ACTION_TYPE.TOGGLE_SALARY_EFFECTIVE_MODAL}) | |||
const toggleTeamModal = () => dispatch({ type: REDUCER_ACTION_TYPE.TOGGLE_TEAM_MODAL}) | |||
const toggleGradeModal = () => dispatch({ type: REDUCER_ACTION_TYPE.TOGGLE_GRADE_MODAL}) | |||
const togglePositionModal = () => dispatch({ type: REDUCER_ACTION_TYPE.TOGGLE_POSITION_MODAL}) | |||
const resetStaff = useCallback(() => { | |||
console.log(defaultValues); | |||
@@ -80,6 +113,10 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
useEffect(() => { | |||
resetStaff() | |||
}, [defaultValues]); | |||
useEffect(() => { | |||
console.log(state) | |||
}, [state]); | |||
const joinDate = watch("joinDate"); | |||
const departDate = watch("departDate"); | |||
@@ -97,7 +134,7 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
flex: 1, | |||
editable: true, | |||
type: 'singleSelect', | |||
valueOptions: combos?.salary.map(item => item.label), | |||
valueOptions: combos.salary.map((item) => item.label), | |||
// valueOptions: [], | |||
// width: 150 | |||
}, | |||
@@ -111,6 +148,90 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
}, | |||
], [combos]) | |||
const teamHistoryCols = useMemo( | |||
() => [ | |||
{ | |||
field: 'team', | |||
headerName: 'team', | |||
flex: 1, | |||
editable: true, | |||
type: 'singleSelect', | |||
valueOptions: combos.team.map(item => item.label), | |||
// valueOptions: [], | |||
// width: 150 | |||
}, | |||
{ | |||
field: 'from', | |||
headerName: 'from', | |||
flex: 1, | |||
editable: true, | |||
type: 'date', | |||
}, | |||
{ | |||
field: 'to', | |||
headerName: 'to', | |||
flex: 1, | |||
editable: true, | |||
type: 'date', | |||
}, | |||
], [combos]) | |||
const gradeHistoryCols = useMemo( | |||
() => [ | |||
{ | |||
field: 'grade', | |||
headerName: 'grade', | |||
flex: 1, | |||
editable: true, | |||
type: 'singleSelect', | |||
valueOptions: combos.grade.map(item => item.label), | |||
// valueOptions: [], | |||
// width: 150 | |||
}, | |||
{ | |||
field: 'from', | |||
headerName: 'from', | |||
flex: 1, | |||
editable: true, | |||
type: 'date', | |||
}, | |||
{ | |||
field: 'to', | |||
headerName: 'to', | |||
flex: 1, | |||
editable: true, | |||
type: 'date', | |||
}, | |||
], [combos]) | |||
const positionHistoryCols = useMemo( | |||
() => [ | |||
{ | |||
field: 'position', | |||
headerName: 'position', | |||
flex: 1, | |||
editable: true, | |||
type: 'singleSelect', | |||
valueOptions: combos.position.map(item => item.label), | |||
// valueOptions: [], | |||
// width: 150 | |||
}, | |||
{ | |||
field: 'from', | |||
headerName: 'from', | |||
flex: 1, | |||
editable: true, | |||
type: 'date', | |||
}, | |||
{ | |||
field: 'to', | |||
headerName: 'to', | |||
flex: 1, | |||
editable: true, | |||
type: 'date', | |||
}, | |||
], [combos]) | |||
return ( | |||
<Card sx={{ display: "block" }}> | |||
<CardContent component={Stack} spacing={4}> | |||
@@ -185,17 +306,23 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
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> | |||
<Box display="flex" justifyContent="space-between" alignItems="center"> | |||
<Select | |||
label={t("Team")} | |||
style={{ flex: 1, marginRight: '8px' }} | |||
{...field} | |||
// error={Boolean(errors.teamId)} | |||
> | |||
{combos.team.map((team, index) => ( | |||
<MenuItem key={`${team.id}-${index}`} value={team.id}> | |||
{t(team.label)} | |||
</MenuItem> | |||
))} | |||
</Select> | |||
<Button variant="contained" size="small" onClick={toggleTeamModal}> | |||
{t("Team History")} | |||
</Button> | |||
</Box> | |||
)} | |||
/> | |||
</FormControl> | |||
@@ -232,17 +359,23 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
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> | |||
<Box display="flex" justifyContent="space-between" alignItems="center"> | |||
<Select | |||
label={t("Grade")} | |||
style={{ flex: 1, marginRight: '8px' }} | |||
{...field} | |||
error={Boolean(errors.gradeId)} | |||
> | |||
{combos.grade.map((grade, index) => ( | |||
<MenuItem key={`${grade.id}-${index}`} value={grade.id}> | |||
{t(grade.label)} | |||
</MenuItem> | |||
))} | |||
</Select> | |||
<Button variant="contained" size="small" onClick={toggleGradeModal}> | |||
{t("Grade History")} | |||
</Button> | |||
</Box> | |||
)} | |||
/> | |||
</FormControl> | |||
@@ -290,8 +423,10 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
control={control} | |||
name="currentPositionId" | |||
render={({ field }) => ( | |||
<Select | |||
<Box display="flex" justifyContent="space-between" alignItems="center"> | |||
<Select | |||
label={t("Current Position")} | |||
style={{ flex: 1, marginRight: '8px' }} | |||
{...field} | |||
error={Boolean(errors.currentPositionId)} | |||
> | |||
@@ -304,6 +439,10 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
</MenuItem> | |||
))} | |||
</Select> | |||
<Button variant="contained" size="small" onClick={togglePositionModal}> | |||
{t("Position History")} | |||
</Button> | |||
</Box> | |||
)} | |||
/> | |||
</FormControl> | |||
@@ -332,7 +471,7 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
</MenuItem> | |||
))} | |||
</Select> | |||
<Button variant="contained" size="small" onClick={controlSalaryEffectiveModel}> | |||
<Button variant="contained" size="small" onClick={toggleSeModal}> | |||
{t("Edit Salary")} | |||
</Button> | |||
</Box> | |||
@@ -414,38 +553,6 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
/> | |||
</Grid> | |||
</Grid> | |||
{/* <Grid container spacing={2} columns={{ xs: 6, sm: 12 }} marginTop={3}> */} | |||
{/* <Grid item xs={6} md={3}> | |||
<Typography sx={{ ml: 1 }} variant="h6" component="div"> | |||
{t("on-going")} | |||
</Typography> | |||
<List> | |||
{InvolvedProject.filter((item: projects) => item.status === "On-going") | |||
.map((item: projects) => ( | |||
<ListItem key={item.code}> | |||
<ListItemText | |||
primary={item.name} | |||
secondary={item.code} | |||
/> | |||
</ListItem> | |||
)) | |||
} | |||
</List> | |||
</Grid> | |||
<Grid item xs={6} md={3}> | |||
<Typography sx={{ ml: 1 }} variant="h6" component="div"> | |||
{t("completed")} | |||
</Typography> | |||
<List> | |||
<ListItem> | |||
<ListItemText | |||
primary="Single-line item" | |||
secondary={'Secondary text'} | |||
/> | |||
</ListItem> | |||
</List> | |||
</Grid> | |||
</Grid> */} | |||
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }} marginTop={3}> | |||
{/* <Grid item xs={6}> | |||
<TextField | |||
@@ -593,10 +700,25 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
</Box> | |||
</CardContent> | |||
<SalaryEffectiveModel | |||
open={salaryEffectiveModelOpen} | |||
onClose={controlSalaryEffectiveModel} | |||
open={state.seModal} | |||
onClose={toggleSeModal} | |||
columns={salaryCols} | |||
/> | |||
<TeamHistoryModal | |||
open={state.teamModal} | |||
onClose={toggleTeamModal} | |||
columns={teamHistoryCols} | |||
/> | |||
<GradeHistoryModal | |||
open={state.gradeModal} | |||
onClose={toggleGradeModal} | |||
columns={gradeHistoryCols} | |||
/> | |||
<PositionHistoryModal | |||
open={state.positionModal} | |||
onClose={togglePositionModal} | |||
columns={positionHistoryCols} | |||
/> | |||
</Card> | |||
); | |||
}; | |||
@@ -0,0 +1,208 @@ | |||
import { Box, Button, Modal, Paper, SxProps, Typography } from "@mui/material"; | |||
import { useTranslation } from "react-i18next"; | |||
import StyledDataGrid from "../StyledDataGrid"; | |||
import { useCallback, useEffect, useMemo, useState } from "react"; | |||
import { GridActionsCellItem, GridEventListener, GridRowEditStopReasons, GridRowModel, GridRowModes, GridRowModesModel, GridToolbarContainer } from "@mui/x-data-grid"; | |||
import AddIcon from '@mui/icons-material/Add'; | |||
import SaveIcon from '@mui/icons-material/Save'; | |||
import DeleteIcon from '@mui/icons-material/Delete'; | |||
import CancelIcon from '@mui/icons-material/Cancel'; | |||
import EditIcon from '@mui/icons-material/Edit'; | |||
import { useFormContext } from "react-hook-form"; | |||
interface Props { | |||
open: boolean; | |||
onClose: () => void; | |||
columns: any[] | |||
} | |||
const modalSx: SxProps = { | |||
position: "absolute", | |||
top: "50%", | |||
left: "50%", | |||
transform: "translate(-50%, -50%)", | |||
width: "90%", | |||
maxWidth: "auto", | |||
maxHeight: "auto", | |||
padding: 3, | |||
display: "flex", | |||
flexDirection: "column", | |||
gap: 2, | |||
}; | |||
const TeamHistoryModal: React.FC<Props> = async ({ open, onClose, columns }) => { | |||
const { | |||
t, | |||
// i18n: { language }, | |||
} = useTranslation(); | |||
const { control, register, formState, trigger, watch, setValue, getValues } = useFormContext(); | |||
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | |||
const [count, setCount] = useState(0); | |||
const [_rows, setRows] = useState(() => { | |||
const list = getValues('teamHistory') | |||
return list && list.length > 0 ? list : [] | |||
}); | |||
const [_delRows, setDelRows] = useState<number[]>([]); | |||
const formValues = watch(); | |||
const handleClose = () => { | |||
onClose(); | |||
}; | |||
const handleRowEditStop: GridEventListener<"rowEditStop"> = ( | |||
params, | |||
event, | |||
) => { | |||
if (params.reason === GridRowEditStopReasons.rowFocusOut) { | |||
event.defaultMuiPrevented = true; | |||
} | |||
}; | |||
// handle row update here | |||
const processRowUpdate = | |||
// useCallback( | |||
(newRow: GridRowModel) => { | |||
console.log(newRow) | |||
const updatedRow = { ...newRow, updated: true }; | |||
console.log(_rows) | |||
if (_rows.length != 0) { | |||
setRows((prev: any[]) => prev?.map((row: any) => (row.id === newRow.id ? updatedRow : row))); | |||
} | |||
return updatedRow; | |||
} | |||
// , [_rows, setValue, setRows]) | |||
const handleSaveClick = useCallback( | |||
(id: any) => () => { | |||
setRowModesModel((prevRowModesModel) => ({ | |||
...prevRowModesModel, | |||
[id]: { mode: GridRowModes.View } | |||
})); | |||
}, | |||
[setRowModesModel] | |||
); | |||
const handleCancelClick = useCallback( | |||
(id: any) => () => { | |||
setRowModesModel((prevRowModesModel) => ({ | |||
...prevRowModesModel, | |||
[id]: { mode: GridRowModes.View, ignoreModifications: true } | |||
})); | |||
}, | |||
[setRowModesModel] | |||
); | |||
const handleEditClick = useCallback( | |||
(id: any) => () => { | |||
setRowModesModel((prevRowModesModel) => ({ | |||
...prevRowModesModel, | |||
[id]: { mode: GridRowModes.Edit } | |||
})); | |||
}, | |||
[setRowModesModel] | |||
); | |||
const handleDeleteClick = useCallback( | |||
(id: any) => () => { | |||
setRows((prevRows: any) => prevRows.filter((row: any) => row.id !== id)); | |||
setCount((prev: number) => prev - 1); | |||
setDelRows((prevRowsId: number[]) => [...prevRowsId, id]) | |||
}, | |||
[setRows, setCount, setDelRows] | |||
); | |||
useEffect(()=> { | |||
console.log(_rows) | |||
setValue('teamHistory', _rows) | |||
setValue('delTeamHistory', _delRows) | |||
}, [_rows, _delRows]) | |||
const defaultCol = useMemo( | |||
() => ( | |||
{ | |||
field: 'actions', | |||
type: 'actions', | |||
headerName: 'edit', | |||
width: 100, | |||
cellClassName: 'actions', | |||
getActions: ({ id }: { id: number }) => { | |||
const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; | |||
if (isInEditMode) { | |||
return [ | |||
<GridActionsCellItem | |||
icon={<SaveIcon />} | |||
label="Save" | |||
key="edit" | |||
sx={{ | |||
color: 'primary.main' | |||
}} | |||
onClick={handleSaveClick(id)} | |||
/>, | |||
<GridActionsCellItem | |||
icon={<CancelIcon />} | |||
label="Cancel" | |||
key="edit" | |||
onClick={handleCancelClick(id)} | |||
/> | |||
]; | |||
} | |||
return [ | |||
<GridActionsCellItem | |||
icon={<EditIcon />} | |||
label="Edit" | |||
className="textPrimary" | |||
onClick={handleEditClick(id)} | |||
color="inherit" | |||
key="edit" | |||
/>, | |||
<GridActionsCellItem | |||
icon={<DeleteIcon />} | |||
label="Delete" | |||
sx={{ | |||
color: 'error.main' | |||
}} | |||
onClick={handleDeleteClick(id)} color="inherit" key="edit" /> | |||
]; | |||
} | |||
} | |||
), [rowModesModel, handleSaveClick, handleCancelClick, handleEditClick, handleDeleteClick] | |||
) | |||
let _columns: any[] = [] | |||
if (columns) { | |||
_columns = [...columns, defaultCol] | |||
} | |||
return ( | |||
<Modal open={open} onClose={handleClose}> | |||
<Paper sx={{ ...modalSx }}> | |||
<Typography variant="h6" component="h2"> | |||
{t('TeamHistoryModal')} | |||
</Typography> | |||
<StyledDataGrid | |||
rows={_rows} | |||
columns={_columns} | |||
editMode="row" | |||
rowModesModel={rowModesModel} | |||
onRowModesModelChange={setRowModesModel} | |||
onRowEditStop={handleRowEditStop} | |||
processRowUpdate={processRowUpdate} | |||
// slots={{ | |||
// toolbar: EditToolbar | |||
// }} | |||
// slotProps={{ | |||
// toolbar: {count, setCount, setRows, setRowModesModel, _columns} | |||
// }} | |||
/> | |||
<Box display="flex" justifyContent="flex-end" gap={2}> | |||
<Button variant="text" onClick={handleClose}> | |||
{t('Cancel')} | |||
</Button> | |||
<Button variant="contained" onClick={handleClose}> | |||
{t("Save")} | |||
</Button> | |||
</Box> | |||
{/* </FormControl> */} | |||
</Paper> | |||
</Modal> | |||
) | |||
} | |||
export default TeamHistoryModal |