You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

362 line
12 KiB

  1. // material-ui
  2. import * as React from 'react';
  3. import {
  4. Button,
  5. } from '@mui/material';
  6. import {
  7. DataGrid,
  8. GridActionsCellItem,
  9. GridRowEditStopReasons,
  10. GridRowModes,
  11. GridToolbarContainer
  12. } from "@mui/x-data-grid";
  13. import EditIcon from '@mui/icons-material/Edit';
  14. import DeleteIcon from '@mui/icons-material/DeleteOutlined';
  15. import SaveIcon from '@mui/icons-material/Save';
  16. import CancelIcon from '@mui/icons-material/Close';
  17. import AddIcon from '@mui/icons-material/Add';
  18. import {useContext, useEffect} from "react";
  19. import axios from "axios";
  20. import {apiPath} from "../../auth/utils";
  21. import {
  22. CHECK_CATEGORY_DUPLICATE,
  23. GET_CATEGORY_LIST,
  24. UPDATE_CATEGORY_PATH,
  25. } from "../../utils/ApiPathConst";
  26. import {
  27. CustomNoRowsOverlay,
  28. GeneralConfirmWindow,
  29. notifyDeleteError,
  30. notifyDeleteSuccess,
  31. notifySaveSuccess,
  32. removeObjectWithId
  33. } from "../../utils/CommonFunction";
  34. import UploadContext from "../../components/UploadProvider";
  35. import {LIONER_BUTTON_THEME} from "../../themes/colorConst";
  36. import {ThemeProvider} from "@emotion/react";
  37. let customerCount = -1;
  38. function EditToolbar(props) {
  39. const {setRows, setRowModesModel} = props;
  40. const handleClick = (id) => {
  41. setRows((oldRows) => [
  42. {
  43. id,
  44. isNew:true
  45. }
  46. ,...oldRows
  47. ]);
  48. setRowModesModel((oldModel) => ({
  49. ...oldModel,
  50. [id]: {mode: GridRowModes.Edit, fieldToFocus: 'name'},
  51. }));
  52. };
  53. return (
  54. <GridToolbarContainer sx={{ml: 1}}>
  55. <Button color="primary" startIcon={<AddIcon/>}
  56. onClick={()=> handleClick(customerCount--)}
  57. >
  58. Add Category
  59. </Button>
  60. </GridToolbarContainer>
  61. );
  62. }
  63. // ==============================|| CATEGORY TABLE ||============================== //
  64. export default function CategoryTable({recordList}) {
  65. const [rows, setRows] = React.useState([]);
  66. const [rowModesModel, setRowModesModel] = React.useState({});
  67. const { setIsUploading } = useContext(UploadContext);
  68. // ==============================|| DELETE WINDOW RELATED ||============================== //
  69. const [isWindowOpen, setIsWindowOpen] = React.useState(false);
  70. const [selectedId, setSelectedId] = React.useState(null);
  71. const handleClose = () => {
  72. setIsWindowOpen(false);
  73. };
  74. const [paginationModel, setPaginationModel] = React.useState({
  75. page: 0,
  76. pageSize:10
  77. });
  78. useEffect(() => {
  79. setPaginationModel({page:0,pageSize:10});
  80. setRows(recordList);
  81. }, [recordList]);
  82. const handleDeleteClick = (id) => () => {
  83. setIsWindowOpen(true);
  84. setSelectedId(id);
  85. };
  86. function updateData(){
  87. setIsUploading(true);
  88. axios.delete(`${apiPath}${GET_CATEGORY_LIST}/${selectedId}`,
  89. )
  90. .then((response) => {
  91. if (response.status === 204) {
  92. notifyDeleteSuccess();
  93. const newList =removeObjectWithId(rows,selectedId);
  94. setIsWindowOpen(false);
  95. setRows(newList);
  96. }
  97. setIsUploading(false);
  98. })
  99. .catch(error => {
  100. console.log(error);
  101. setIsUploading(false);
  102. return false;
  103. });
  104. }
  105. // ==============================|| DELETE WINDOW RELATED ||============================== //
  106. useEffect(()=>{
  107. setRows(recordList);
  108. },[recordList]);
  109. const handleRowEditStop = (params, event) => {
  110. if (params.reason === GridRowEditStopReasons.rowFocusOut) {
  111. event.defaultMuiPrevented = true;
  112. }
  113. };
  114. const handleEditClick = (id) => () => {
  115. setRowModesModel({...rowModesModel, [id]: {mode: GridRowModes.Edit, fieldToFocus: 'name'}});
  116. };
  117. const handleSaveClick = (id) => () => {
  118. setRowModesModel({...rowModesModel, [id]: {mode: GridRowModes.View, fieldToFocus: 'name'}});
  119. };
  120. const handleCancelClick = (id) => () => {
  121. setRowModesModel({
  122. ...rowModesModel,
  123. [id]: {mode: GridRowModes.View, ignoreModifications: true},
  124. });
  125. const editedRow = rows.find((row) => row.id === id);
  126. if (editedRow.isNew) {
  127. setRows(rows.filter((row) => row.id !== id));
  128. }
  129. };
  130. function updateCategory(data) {
  131. if(data.name.trim().length === 0){
  132. notifyDeleteError(`Name cannot be null`);
  133. handleEditClick(data.id);
  134. }
  135. else{
  136. axios.get(`${apiPath}${CHECK_CATEGORY_DUPLICATE}`,
  137. {
  138. params: {
  139. "name": data.name.trim(),
  140. "categoryId": data.id
  141. },
  142. })
  143. .then((response) => {
  144. if (response.status === 200) {
  145. if(response.data.isTaken){
  146. notifyDeleteError(`${data.name} already exists.`)
  147. handleEditClick(data.id);
  148. }
  149. else if(!response.data.isTaken){
  150. processUpload(data);
  151. }
  152. }
  153. })
  154. .catch(error => {
  155. console.log(error);
  156. return true;
  157. });
  158. }
  159. }
  160. function processUpload(data){
  161. setIsUploading(true);
  162. axios.post(`${apiPath}${UPDATE_CATEGORY_PATH}`,
  163. {
  164. "id": data.id,
  165. "name": data.name.trim(),
  166. "description": data.description,
  167. },
  168. )
  169. .then((response) => {
  170. if (response.status === 200) {
  171. const updatedRow = { ...data };
  172. updatedRow.id = response.data.id;
  173. setIsUploading(false);
  174. if(data.id === -1){
  175. setRows(rows.map((row) => (row.id === -1 ? updatedRow : row)));
  176. }
  177. else{
  178. setRows(rows.map((row) => (row.id === data.id ? updatedRow : row)));
  179. }
  180. notifySaveSuccess();
  181. }
  182. })
  183. .catch(error => {
  184. console.log(error);
  185. setIsUploading(false);
  186. return false;
  187. });
  188. }
  189. const processRowUpdate = (newRow) => {
  190. return new Promise((resolve, reject) => {
  191. const updatedRow = { ...newRow, isNew: false };
  192. if (updatedRow.name.trim().length === 0) {
  193. const error = new Error('Name cannot be null');
  194. reject(error);
  195. return;
  196. }
  197. setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
  198. updateCategory(updatedRow);
  199. resolve(updatedRow); // Resolve the Promise with the updated row
  200. });
  201. };
  202. const handleRowModesModelChange = (newRowModesModel) => {
  203. setRowModesModel(newRowModesModel);
  204. };
  205. const columns = [
  206. {
  207. field: 'actions',
  208. type: 'actions',
  209. headerName: 'Actions',
  210. width: 100,
  211. cellClassName: 'actions',
  212. getActions: ({id}) => {
  213. const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit;
  214. if (isInEditMode) {
  215. return [
  216. <ThemeProvider key="OutSave" theme={LIONER_BUTTON_THEME}>
  217. <GridActionsCellItem
  218. key="InCancel"
  219. icon={<CancelIcon sx={{fontSize: 25}}/>}
  220. label="Cancel"
  221. className="textPrimary"
  222. onClick={handleCancelClick(id)}
  223. color="error"
  224. />
  225. </ThemeProvider>,
  226. <ThemeProvider key="OutSave" theme={LIONER_BUTTON_THEME}>
  227. <GridActionsCellItem
  228. key="InSave"
  229. icon={<SaveIcon sx={{fontSize: 25}}/>}
  230. label="Save"
  231. // sx={{
  232. // color: 'primary.main',
  233. // }}
  234. color="save"
  235. onClick={handleSaveClick(id)}
  236. />
  237. </ThemeProvider>,
  238. ];
  239. }
  240. return [
  241. <ThemeProvider key="OutSave" theme={LIONER_BUTTON_THEME}>
  242. <GridActionsCellItem
  243. key="OutDelete"
  244. icon={<DeleteIcon sx={{fontSize: 25}}/>}
  245. label="Delete"
  246. onClick={handleDeleteClick(id)}
  247. color="delete"
  248. />
  249. </ThemeProvider>
  250. ,
  251. <ThemeProvider key="OutSave" theme={LIONER_BUTTON_THEME}>
  252. <GridActionsCellItem
  253. key="OutSave"
  254. icon={<EditIcon sx={{fontSize: 25}}/>}
  255. label="Edit"
  256. className="textPrimary"
  257. onClick={handleEditClick(id)}
  258. color="edit"
  259. />
  260. </ThemeProvider>,
  261. ];
  262. },
  263. },
  264. {
  265. id: 'categoryName',
  266. field: 'name',
  267. headerName: 'Category Name',
  268. flex: 1,
  269. editable: true,
  270. preProcessEditCellProps: (params) => {
  271. if(params.props.value !== undefined){
  272. const hasError = params.props.value.length > 50;
  273. if (hasError) {
  274. notifyDeleteError("Input has reached the length limit (50)");
  275. }
  276. return { ...params.props, error: hasError };
  277. }
  278. },
  279. },
  280. {
  281. id: 'categoryDescription',
  282. field: 'description',
  283. headerName: 'Category Description',
  284. flex: 1,
  285. editable: true,
  286. preProcessEditCellProps: (params) => {
  287. if(params.props.value !== undefined){
  288. const hasError = params.props.value.length > 255;
  289. if (hasError) {
  290. notifyDeleteError("Input has reached the length limit (255)");
  291. }
  292. return { ...params.props, error: hasError };
  293. }
  294. },
  295. },
  296. ];
  297. return (
  298. <div style={{/*height: '66vh',*/ width: '100%'}}>
  299. <DataGrid
  300. rows={rows}
  301. columns={columns}
  302. columnHeaderHeight={45}
  303. editMode="row"
  304. rowModesModel={rowModesModel}
  305. onRowModesModelChange={handleRowModesModelChange}
  306. onRowEditStop={handleRowEditStop}
  307. onProcessRowUpdateError={(error) => {
  308. console.log(error);
  309. notifyDeleteError(`Name cannot be null`);
  310. }}
  311. getRowHeight={() => 'auto'}
  312. processRowUpdate={processRowUpdate}
  313. paginationModel={paginationModel}
  314. onPaginationModelChange={setPaginationModel}
  315. slots={{
  316. toolbar: EditToolbar,
  317. noRowsOverlay: () => (
  318. CustomNoRowsOverlay()
  319. )
  320. }}
  321. pageSizeOptions={[15]}
  322. slotProps={{
  323. toolbar: {setRows, setRowModesModel},
  324. }}
  325. autoHeight
  326. />
  327. <GeneralConfirmWindow
  328. isWindowOpen={isWindowOpen}
  329. title={"Attention"}
  330. //content={`Confirm to delete Category ${getObjectById(rows,selectedId) === null? "" : '"' + getObjectById(rows,selectedId).name + '"'} ?`}
  331. content={`Are you sure to delete this category?`}
  332. onNormalClose={handleClose}
  333. onConfirmClose={updateData}
  334. />
  335. </div>
  336. );
  337. }