瀏覽代碼

no message

master
父節點
當前提交
74fed6474b
共有 4 個檔案被更改,包括 185 行新增169 行删除
  1. +3
    -0
      src/pages/client/ClientMaintainPage/ClientForm.js
  2. +1
    -1
      src/pages/pdf/ConsultantMaintainPage/ApplicationTable.js
  3. +71
    -103
      src/pages/pdf/ConsultantMaintainPage/index.js
  4. +110
    -65
      src/pages/pdf/ConsultantSearchPage/ConsultantTable.js

+ 3
- 0
src/pages/client/ClientMaintainPage/ClientForm.js 查看文件

@@ -815,6 +815,7 @@ const ClientForm = ({ refClientDetail, isNewRecord, getClientDetail }) => {
</Grid>
</Grid>
</Grid>
{/*
<Grid item xs={12} sm={12} md={12} lg={5.5} sx={{ ml: 3, mr: 3, mb: 3 }}>
<Grid container alignItems={"flex-start"}>
<Grid item xs={4} sm={4} md={4} lg={4} sx={{ ml: 3, mr: 3, display: 'flex', alignItems: 'center' }}>
@@ -835,6 +836,8 @@ const ClientForm = ({ refClientDetail, isNewRecord, getClientDetail }) => {
</Grid>
</Grid>
</Grid>
*/}
<Grid item xs={12} sm={12} md={12} lg={5.5} sx={{ ml: 3, mr: 3, mb: 3 }}></Grid>
<Grid item xs={12} sm={12} md={12} lg={5.5} sx={{ ml: 3, mr: 3, mb: 3 }}>
<Grid container alignItems={"flex-start"}>
<Grid item xs={4} sm={4} md={4} lg={4} sx={{ ml: 3, mr: 3, display: 'flex', alignItems: 'center' }}>


+ 1
- 1
src/pages/pdf/ConsultantMaintainPage/ApplicationTable.js 查看文件

@@ -1,4 +1,4 @@
// material-ui
// material-ui ######### going to delete
import * as React from 'react';
import {
DataGrid,


+ 71
- 103
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 ?
<LoadingComponent/>
:
<Grid container rowSpacing={3} columnSpacing={2.75} >
<ThemeProvider theme={LIONER_FORM_THEME}>
<Grid item xs={12} s={12} md={12} lg={12}>
<Grid container justifyContent="flex-start" alignItems="center">
<Grid item xs={3} sx={{mt:-1, mb: -2.25}} >
<Box sx={{ display: 'flex', alignItems: 'center'}}>
<Typography align="center" variant="h4" >{isNewRecord? "New Consultant" : "Maintain Consultant"}</Typography>
</Box>
</Grid>
</Grid>
</Grid>
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*/}
<Grid item xs={12} md={12} lg={12} >
<ConsultantForm
getConsultantDetail={getConsultantDetail}
refConsultantDetail={consultantDetail}
isNewRecord={isNewRecord}
/>
</Grid>
</ThemeProvider>
return !onReady ? (
<LoadingComponent />
) : (
<Grid container rowSpacing={3} columnSpacing={2.75}>
<ThemeProvider theme={LIONER_FORM_THEME}>
<Grid item xs={12}>
<Grid container justifyContent="flex-start" alignItems="center">
<Grid item xs={12} sx={{ mt: -1, mb: -2.25 }}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography align="center" variant="h4">
{isNewRecord ? "New Consultant" : "Maintain Consultant"}
</Typography>
</Box>
</Grid>
);
</Grid>
</Grid>

<Grid item xs={12}>
<ConsultantForm
getConsultantDetail={getConsultantDetail}
refConsultantDetail={consultantDetail}
isNewRecord={isNewRecord}
/>
</Grid>
</ThemeProvider>
</Grid>
);
};

export default ConsultantPanel;

+ 110
- 65
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 [
<ThemeProvider key="save" theme={LIONER_BUTTON_THEME}>
<GridActionsCellItem
icon={<SaveIcon />}
label="Save"
onClick={handleSaveClick(id)}
color="save"
/>
</ThemeProvider>,
<ThemeProvider key="cancel" theme={LIONER_BUTTON_THEME}>
<GridActionsCellItem
icon={<CancelIcon />}
label="Cancel"
onClick={handleCancelClick(id)}
color="cancel"
/>
</ThemeProvider>,
];
}

return [
<ThemeProvider key="OutSave" theme={LIONER_BUTTON_THEME}>
<ThemeProvider key="edit" theme={LIONER_BUTTON_THEME}>
<GridActionsCellItem
icon={<EditIcon sx={{fontSize: 25}}/>}
label="Edit"
className="textPrimary"
onClick={handleEditClick(id)}
color="edit"
// disabled={'true'}
// disabled={!ability.can('VIEW','DASHBOARD')}
/>
</ThemeProvider>
]
</ThemeProvider>,
<ThemeProvider key="delete" theme={LIONER_BUTTON_THEME}>
<GridActionsCellItem
icon={<DeleteIcon />}
label="Delete"
onClick={handleDeleteClick(id)}
color="error" // Use "error" if no 'delete' color defined
/>
</ThemeProvider>,
];
},
},
*/
// {
// id: 'title',
// field: 'title',
// headerName: 'Title',
// // sortComparator: dateComparator,
// flex: 0.75,
// renderCell: (params) => (
// <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}>
// {params.value}
// </div>
// // <div>
// // {getDateString(params.row.pdfFrom,false)}
// // </div>
// ),
// },
{
id: 'name',
field: 'name',
headerName: 'Name',
flex: 2,
renderCell: (params) => {
return (
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}>
{params.value}
</div>
);
}
editable: true, // Enables inline editing
renderCell: (params) => (
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', whiteSpace: 'normal', wordBreak: 'break-word'}}>
{params.value}
</div>
),
},
{
id: 'createDate',
field: 'createDate',
headerName: 'Create Datetime',
flex: 1,
@@ -116,29 +164,26 @@ export default function ConsultantTable({recordList, applySearch}) {
</div>
),
},

];

return (
<div>
<div style={{ width: '100%' }}>
<DataGrid
rows={rows}
columns={columns}
columnHeaderHeight={45}
editMode="row"
//autoPageSize
rowModesModel={rowModesModel}
onRowModesModelChange={setRowModesModel}
onRowEditStop={handleRowEditStop}
processRowUpdate={processRowUpdate}
onProcessRowUpdateError={(error) => console.error(error)}
getRowHeight={() => 'auto'}
paginationModel={paginationModel}
onPaginationModelChange={setPaginationModel}
slots={{
noRowsOverlay: () => (
CustomNoRowsOverlay()
)
}}
slots={{ noRowsOverlay: CustomNoRowsOverlay }}
pageSizeOptions={[10]}
autoHeight
/>
</div>
);
}
}

Loading…
取消
儲存