From 3c798ea98f16842d58c618ae89bd3eee24b631c0 Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Tue, 3 Sep 2024 14:33:44 +0800 Subject: [PATCH] update writing grade/position/team to logs when edit --- src/app/(main)/settings/staff/edit/page.tsx | 26 +- src/app/api/staff/actions.ts | 25 ++ src/app/api/staffInfoHistory/index.ts | 18 +- src/components/EditStaff/EditStaff.tsx | 152 ++++++----- src/components/EditStaff/EditStaffWrapper.tsx | 13 +- .../EditStaff/GradeHistoryModal.tsx | 207 ++++++++++++++ .../EditStaff/PositionGradeHistory.tsx | 11 - .../EditStaff/PositionHistoryModal.tsx | 207 ++++++++++++++ src/components/EditStaff/StaffInfo.tsx | 256 +++++++++++++----- src/components/EditStaff/TeamHistoryModal.tsx | 208 ++++++++++++++ 10 files changed, 944 insertions(+), 179 deletions(-) create mode 100644 src/components/EditStaff/GradeHistoryModal.tsx delete mode 100644 src/components/EditStaff/PositionGradeHistory.tsx create mode 100644 src/components/EditStaff/PositionHistoryModal.tsx create mode 100644 src/components/EditStaff/TeamHistoryModal.tsx diff --git a/src/app/(main)/settings/staff/edit/page.tsx b/src/app/(main)/settings/staff/edit/page.tsx index 94138fa..5b53b72 100644 --- a/src/app/(main)/settings/staff/edit/page.tsx +++ b/src/app/(main)/settings/staff/edit/page.tsx @@ -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 = 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 ( <> diff --git a/src/app/api/staff/actions.ts b/src/app/api/staff/actions.ts index 27382be..4d8cb35 100644 --- a/src/app/api/staff/actions.ts +++ b/src/app/api/staff/actions.ts @@ -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 { diff --git a/src/app/api/staffInfoHistory/index.ts b/src/app/api/staffInfoHistory/index.ts index d88b1a9..ea584dd 100644 --- a/src/app/api/staffInfoHistory/index.ts +++ b/src/app/api/staffInfoHistory/index.ts @@ -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) => { diff --git a/src/components/EditStaff/EditStaff.tsx b/src/components/EditStaff/EditStaff.tsx index 2192542..1c9e5cb 100644 --- a/src/components/EditStaff/EditStaff.tsx +++ b/src/components/EditStaff/EditStaff.tsx @@ -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 = ({ 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({ + const formProps = useForm({ defaultValues: { staffId: Staff.staffId, name: Staff.name, @@ -79,18 +82,42 @@ const EditStaff: React.FC = ({ 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>( async (data) => { try { @@ -148,15 +175,45 @@ const EditStaff: React.FC = ({ 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 = ({ 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 = ({ 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 ( <> @@ -283,11 +286,12 @@ const EditStaff: React.FC = ({ Staff, combos, SalaryEffectiveInfo, In variant="scrollable" > - + + {tabIndex == 0 && Staff && } - {tabIndex == 1 && } + {tabIndex == 2 && } {tabIndex == 0 && + + + {/* */} + + + ) +} +export default GradeHistoryModal \ No newline at end of file diff --git a/src/components/EditStaff/PositionGradeHistory.tsx b/src/components/EditStaff/PositionGradeHistory.tsx deleted file mode 100644 index cfffe1f..0000000 --- a/src/components/EditStaff/PositionGradeHistory.tsx +++ /dev/null @@ -1,11 +0,0 @@ - -interface Props { - gradeLog?: any[] - } - - -const PositionGradeHistory: React.FC = async ({ gradeLog }) => { - - - return null -} \ No newline at end of file diff --git a/src/components/EditStaff/PositionHistoryModal.tsx b/src/components/EditStaff/PositionHistoryModal.tsx new file mode 100644 index 0000000..afed708 --- /dev/null +++ b/src/components/EditStaff/PositionHistoryModal.tsx @@ -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 = async ({ open, onClose, columns }) => { + const { + t, + // i18n: { language }, + } = useTranslation(); + const { control, register, formState, trigger, watch, setValue, getValues } = useFormContext(); + const [rowModesModel, setRowModesModel] = useState({}); + const [count, setCount] = useState(0); + const [_rows, setRows] = useState(() => { + const list = getValues('positionHistory') + return list && list.length > 0 ? list : [] + }); + const [_delRows, setDelRows] = useState([]); + 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 [ + } + label="Save" + key="edit" + sx={{ + color: 'primary.main' + }} + onClick={handleSaveClick(id)} + />, + } + label="Cancel" + key="edit" + onClick={handleCancelClick(id)} + /> + ]; + } + return [ + } + label="Edit" + className="textPrimary" + onClick={handleEditClick(id)} + color="inherit" + key="edit" + />, + } + 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 ( + + + + {t('PositionHistoryModal')} + + + + + + + {/* */} + + + ) +} +export default PositionHistoryModal \ No newline at end of file diff --git a/src/components/EditStaff/StaffInfo.tsx b/src/components/EditStaff/StaffInfo.tsx index 3039844..01df7f1 100644 --- a/src/components/EditStaff/StaffInfo.tsx +++ b/src/components/EditStaff/StaffInfo.tsx @@ -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 = ({ combos }) => { const { @@ -62,12 +96,11 @@ const StaffInfo: React.FC = ({ 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 = ({ 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 = ({ 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 = ({ 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 ( @@ -185,17 +306,23 @@ const StaffInfo: React.FC = ({ combos }) => { control={control} name="teamId" render={({ field }) => ( - + + + + )} /> @@ -232,17 +359,23 @@ const StaffInfo: React.FC = ({ combos }) => { control={control} name="gradeId" render={({ field }) => ( - + + + + )} /> @@ -290,8 +423,10 @@ const StaffInfo: React.FC = ({ combos }) => { control={control} name="currentPositionId" render={({ field }) => ( - @@ -304,6 +439,10 @@ const StaffInfo: React.FC = ({ combos }) => { ))} + + )} /> @@ -332,7 +471,7 @@ const StaffInfo: React.FC = ({ combos }) => { ))} - @@ -414,38 +553,6 @@ const StaffInfo: React.FC = ({ combos }) => { /> - {/* */} - {/* - - {t("on-going")} - - - {InvolvedProject.filter((item: projects) => item.status === "On-going") - .map((item: projects) => ( - - - - )) - } - - - - - {t("completed")} - - - - - - - - */} {/* = ({ combos }) => { + + + ); }; diff --git a/src/components/EditStaff/TeamHistoryModal.tsx b/src/components/EditStaff/TeamHistoryModal.tsx new file mode 100644 index 0000000..30299e6 --- /dev/null +++ b/src/components/EditStaff/TeamHistoryModal.tsx @@ -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 = async ({ open, onClose, columns }) => { + const { + t, + // i18n: { language }, + } = useTranslation(); + const { control, register, formState, trigger, watch, setValue, getValues } = useFormContext(); + const [rowModesModel, setRowModesModel] = useState({}); + const [count, setCount] = useState(0); + const [_rows, setRows] = useState(() => { + const list = getValues('teamHistory') + return list && list.length > 0 ? list : [] + }); + const [_delRows, setDelRows] = useState([]); + 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 [ + } + label="Save" + key="edit" + sx={{ + color: 'primary.main' + }} + onClick={handleSaveClick(id)} + />, + } + label="Cancel" + key="edit" + onClick={handleCancelClick(id)} + /> + ]; + } + return [ + } + label="Edit" + className="textPrimary" + onClick={handleEditClick(id)} + color="inherit" + key="edit" + />, + } + 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 ( + + + + {t('TeamHistoryModal')} + + + + + + + {/* */} + + + ) +} +export default TeamHistoryModal \ No newline at end of file