diff --git a/src/pages/client/ClientMaintainPage/ClientForm.js b/src/pages/client/ClientMaintainPage/ClientForm.js index 3087fc8..b8a07be 100644 --- a/src/pages/client/ClientMaintainPage/ClientForm.js +++ b/src/pages/client/ClientMaintainPage/ClientForm.js @@ -815,6 +815,7 @@ const ClientForm = ({ refClientDetail, isNewRecord, getClientDetail }) => { + {/* @@ -835,6 +836,8 @@ const ClientForm = ({ refClientDetail, isNewRecord, getClientDetail }) => { + */} + diff --git a/src/pages/pdf/ConsultantMaintainPage/ApplicationTable.js b/src/pages/pdf/ConsultantMaintainPage/ApplicationTable.js index 8b73ed4..5d309e3 100644 --- a/src/pages/pdf/ConsultantMaintainPage/ApplicationTable.js +++ b/src/pages/pdf/ConsultantMaintainPage/ApplicationTable.js @@ -1,4 +1,4 @@ -// material-ui +// material-ui ######### going to delete import * as React from 'react'; import { DataGrid, diff --git a/src/pages/pdf/ConsultantMaintainPage/index.js b/src/pages/pdf/ConsultantMaintainPage/index.js index 0f9617e..10abef8 100644 --- a/src/pages/pdf/ConsultantMaintainPage/index.js +++ b/src/pages/pdf/ConsultantMaintainPage/index.js @@ -1,119 +1,87 @@ -// material-ui +// src/pages/consultant/ConsultantMaintainPage/index.js ### going to delete!!! + import { - Box, - Grid, Typography + Box, + Grid, + Typography } from '@mui/material'; import ConsultantForm from "./ConsultantForm"; -import {useContext, useEffect, useState} from "react"; -import * as React from "react"; +import { useEffect, useState } from "react"; import axios from "axios"; -import {apiPath} from "../../../auth/utils"; -import { - GET_CONSULTANT_PATH, -} from "../../../utils/ApiPathConst"; +import { apiPath } from "../../../auth/utils"; +import { GET_CONSULTANT_PATH } from "../../../utils/ApiPathConst"; import LoadingComponent from "../../extra-pages/LoadingComponent"; -import {useLocation, useParams} from "react-router-dom"; -import ApplicationTable from "./ApplicationTable"; -import MainCard from "../../../components/MainCard"; -import AbilityContext from "../../../components/AbilityProvider"; - -import {LIONER_FORM_THEME, CARD_MAX_WIDTH} from "../../../themes/themeConst"; -import {ThemeProvider} from "@emotion/react"; -import {isObjEmpty} from "../../../utils/Utils"; -import { el } from 'date-fns/locale'; +import { useParams } from "react-router-dom"; +import { LIONER_FORM_THEME, CARD_MAX_WIDTH } from "../../../themes/themeConst"; +import { ThemeProvider } from "@emotion/react"; const ConsultantPanel = () => { - const { id } = useParams(); - const location = useLocation(); - const queryParams = new URLSearchParams(location.search); - - const [onReady, setOnReady] = useState(false); - const [isNewRecord, setIsNewRecord] = useState(false); - const [consultantDetail, setConsultantDetail] = useState({}); - - function getConsultantDetail(consultantId) { - axios.get(`${apiPath}${GET_CONSULTANT_PATH}/${consultantId}`, - ) - .then((response) => { - if (response.status === 200) { - setConsultantDetail(response.data.data); - console.log("response.data:" + response.data.name); - // This is the correct place to set the ready state - setOnReady(true); - } - }) - .catch(error => { - console.log(error); - // Handle the error by still setting onReady to true, but maybe with a message - setOnReady(true); - return false; - }); - } + const { id } = useParams(); + const [onReady, setOnReady] = useState(false); + const [isNewRecord, setIsNewRecord] = useState(false); + const [consultantDetail, setConsultantDetail] = useState({}); - useEffect(() => { - // Check if id is defined and not null - if(id){ - // Check if it's a new record based on a negative ID - if (Number(id) < 0) { - setIsNewRecord(true); - // Set ready state for new records immediately - setOnReady(true); - } else { - setIsNewRecord(false); - getConsultantDetail(id); - } - } else { - // This block will handle cases where 'id' is undefined or null - // If the route is /consultant without an ID, treat it as a new record. - setIsNewRecord(true); - setOnReady(true); + const getConsultantDetail = (consultantId) => { + axios + .get(`${apiPath}${GET_CONSULTANT_PATH}/${consultantId}`) + .then((response) => { + if (response.status === 200) { + setConsultantDetail(response.data.data || {}); + setOnReady(true); } - }, [id]); + }) + .catch((error) => { + console.error("Error fetching consultant:", error); + setOnReady(true); // Show form even on error + }); + }; - useEffect(() => { - // This code will only run AFTER the 'onReady' state has been updated. - if(consultantDetail) - console.log("consultantDetail:" + consultantDetail.name); - else - console.log("consultantDetail null"); - - }, [onReady]); // The dependency array ensures this runs when onReady changes. - - useEffect(() => { - // This code will only run AFTER the 'onReady' state has been updated. - if (onReady === true) { - console.log("The component is now ready!"); - } - }, [setConsultantDetail]); // The dependency array ensures this runs when onReady changes. - + useEffect(() => { + if (!id) { + setIsNewRecord(true); + setOnReady(true); + return; + } - return ( - !onReady ? - - : - - - - - - - {isNewRecord? "New Consultant" : "Maintain Consultant"} - - - - + const numericId = Number(id); + if (isNaN(numericId) || numericId < 0) { + // Treat as new record + setIsNewRecord(true); + setConsultantDetail({}); + setOnReady(true); + } else { + setIsNewRecord(false); + getConsultantDetail(id); + } + }, [id]); - {/*row 1*/} - - - - + return !onReady ? ( + + ) : ( + + + + + + + + {isNewRecord ? "New Consultant" : "Maintain Consultant"} + + - ); + + + + + + + + + ); }; export default ConsultantPanel; \ No newline at end of file diff --git a/src/pages/pdf/ConsultantSearchPage/ConsultantTable.js b/src/pages/pdf/ConsultantSearchPage/ConsultantTable.js index eef8a0c..19eae38 100644 --- a/src/pages/pdf/ConsultantSearchPage/ConsultantTable.js +++ b/src/pages/pdf/ConsultantSearchPage/ConsultantTable.js @@ -3,109 +3,157 @@ import * as React from 'react'; import { DataGrid, GridActionsCellItem, + GridRowModes, + GridRowEditStopReasons, } from "@mui/x-data-grid"; import EditIcon from '@mui/icons-material/Edit'; -import VisibilityIcon from '@mui/icons-material/Visibility'; +import DeleteIcon from '@mui/icons-material/Delete'; +import SaveIcon from '@mui/icons-material/Save'; +import CancelIcon from '@mui/icons-material/Cancel'; import {useContext, useEffect} from "react"; -import {useNavigate} from "react-router-dom"; import {CustomNoRowsOverlay, dateComparator, getDateString} from "../../../utils/CommonFunction"; import AbilityContext from "../../../components/AbilityProvider"; import {LIONER_BUTTON_THEME} from "../../../themes/colorConst"; import {ThemeProvider} from "@emotion/react"; +import axios from "axios"; // Add this +import {apiPath} from "../../../auth/utils"; // Add this (same as in index.js) // ==============================|| Consultant TABLE ||============================== // export default function ConsultantTable({recordList, applySearch}) { - const [rows, setRows] = React.useState(recordList); - const [rowModesModel] = React.useState({}); + const [rows, setRows] = React.useState([]); + const [rowModesModel, setRowModesModel] = React.useState({}); - const [isWindowOpen, setIsWindowOpen] = React.useState(false); - const [recordId, setRecordId] = React.useState(null); - - const navigate = useNavigate() const ability = useContext(AbilityContext); const [paginationModel, setPaginationModel] = React.useState({ page: 0, - pageSize:10 + pageSize: 10 }); useEffect(() => { - setPaginationModel({page:0,pageSize:10}); - setRows(recordList); + setPaginationModel({page: 0, pageSize: 10}); + // Ensure each row has a unique 'id' field (required by DataGrid) + setRows(recordList.map(row => ({ ...row, id: row.id || row.someUniqueKey }))); }, [recordList]); + const handleRowEditStop = (params, event) => { + if (params.reason === GridRowEditStopReasons.rowFocusOut) { + event.defaultMuiPrevented = true; + } + }; + const handleEditClick = (id) => () => { - navigate('/consultant/'+ id); + setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } }); }; - const handleCloseClick = () => { - setIsWindowOpen(false); - setRecordId(null); + const handleSaveClick = (id) => () => { + setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } }); }; - const handleCloseRefresh = () => { - setIsWindowOpen(false); - setRecordId(null); - applySearch(); + const handleCancelClick = (id) => () => { + setRowModesModel({ + ...rowModesModel, + [id]: { mode: GridRowModes.View, ignoreModifications: true }, + }); + }; + + const handleDeleteClick = (id) => async () => { + if (window.confirm("Are you sure you want to delete this consultant? This action cannot be undone.")) { + try { + const response = await axios.post(`${apiPath}/consultant/delete`, { id }); + + if (response.status === 200 || response.status === 204) { + applySearch(); // Refresh the table + } + } catch (err) { + console.error("Delete error:", err); + alert("Failed to delete consultant. Please try again."); + } + } + }; + + const processRowUpdate = async (newRow) => { + // Optimistically update UI + const updatedRow = { ...newRow }; + + try { + const response = await axios.post(`${apiPath}/consultant/save`, newRow); + if (response.status === 200) { + // If backend returns updated data, use it + return response.data || updatedRow; + } + } catch (err) { + console.error("Save error:", err); + alert("Failed to save changes. Please try again."); + // On failure, you could revert, but here we keep the edited value + } + + return updatedRow; }; const columns = [ - /* { field: 'actions', type: 'actions', headerName: 'Actions', - // flex: 0.5, - width: 100, - cellClassName: 'actions', - getActions: ({id}) => { + width: 150, + getActions: ({ id }) => { + const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; + + if (isInEditMode) { + return [ + + } + label="Save" + onClick={handleSaveClick(id)} + color="save" + /> + , + + } + label="Cancel" + onClick={handleCancelClick(id)} + color="cancel" + /> + , + ]; + } + return [ - + } label="Edit" - className="textPrimary" onClick={handleEditClick(id)} color="edit" - // disabled={'true'} - // disabled={!ability.can('VIEW','DASHBOARD')} /> - - ] + , + + } + label="Delete" + onClick={handleDeleteClick(id)} + color="error" // Use "error" if no 'delete' color defined + /> + , + ]; }, }, - */ - // { - // id: 'title', - // field: 'title', - // headerName: 'Title', - // // sortComparator: dateComparator, - // flex: 0.75, - // renderCell: (params) => ( - //
- // {params.value} - //
- // //
- // // {getDateString(params.row.pdfFrom,false)} - // //
- // ), - // }, { - id: 'name', field: 'name', headerName: 'Name', flex: 2, - renderCell: (params) => { - return ( -
- {params.value} -
- ); - } + editable: true, // Enables inline editing + renderCell: (params) => ( +
+ {params.value} +
+ ), }, { - id: 'createDate', field: 'createDate', headerName: 'Create Datetime', flex: 1, @@ -116,29 +164,26 @@ export default function ConsultantTable({recordList, applySearch}) { ), }, - ]; return ( -
+
console.error(error)} getRowHeight={() => 'auto'} paginationModel={paginationModel} onPaginationModelChange={setPaginationModel} - slots={{ - noRowsOverlay: () => ( - CustomNoRowsOverlay() - ) - }} + slots={{ noRowsOverlay: CustomNoRowsOverlay }} pageSizeOptions={[10]} autoHeight />
); -} +} \ No newline at end of file