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.
 
 

377 lines
14 KiB

  1. // material-ui
  2. import { useState, useEffect, useRef } from 'react';
  3. import { Box } from "@mui/material";
  4. import {
  5. DataGrid, GridOverlay,
  6. } from "@mui/x-data-grid";
  7. import * as HttpUtils from "utils/HttpUtils";
  8. import { FormattedMessage, useIntl } from "react-intl";
  9. import { TablePagination, Typography } from '@mui/material';
  10. import { getSearchCriteria, checkSearchCriteriaPath } from "auth/utils";
  11. /** When Gazette Issue No. is selected, load all matching rows (no start/limit). */
  12. function hasGazetteIssueFilter(params) {
  13. const issueId = params?.issueId;
  14. return issueId != null && issueId !== "";
  15. }
  16. // ==============================|| EVENT TABLE ||============================== //
  17. export function FiDataGrid({ rows, columns, sx, autoHeight = true,
  18. hideFooterSelectedRowCount, rowModesModel, editMode,
  19. pageSizeOptions, filterItems, customPageSize, doLoad, applyGridOnReady, applySearch,
  20. tab, height, maxHeight, pagination = true, serverSorting = false,
  21. disablePagingOnGazetteIssue = true, ...props }) {
  22. const intl = useIntl();
  23. const [_rows, set_rows] = useState([]);
  24. const [_doLoad, set_doLoad] = useState({});
  25. const [_columns, set_columns] = useState([]);
  26. const [_rowModesModel, set_rowModesModel] = useState({});
  27. const [_editMode, set_editMode] = useState("row");
  28. const [_pageSizeOptions, set_pageSizeOptions] = useState([10]);
  29. const [_filterItems, set_filterItems] = useState([]);
  30. const [loading, setLoading] = useState(false);
  31. const [page, setPage] = useState(0);
  32. const [pageSize, setPageSize] = useState(10);
  33. const [sortModel, setSortModel] = useState([]);
  34. // const [_autoHeight, set_autoHeight] = useState(true);
  35. const [myHideFooterSelectedRowCount, setMyHideFooterSelectedRowCount] = useState(true);
  36. const [_sx, set_sx] = useState({
  37. padding: "4 2 4 2",
  38. lineHeight: "normal",
  39. '& .MuiDataGrid-cell': {
  40. borderTop: 1,
  41. borderBottom: 1,
  42. borderColor: "#EEE",
  43. },
  44. '& .MuiDataGrid-footerContainer': {
  45. border: 1,
  46. borderColor: "#EEE"
  47. },
  48. "& .MuiDataGrid-columnHeaderTitle": {
  49. whiteSpace: "normal",
  50. lineHeight: "normal"
  51. },
  52. "& .MuiDataGrid-columnHeader": {
  53. // Forced to use important since overriding inline styles
  54. height: "unset !important"
  55. },
  56. });
  57. const effectiveAutoHeight = autoHeight && !height && !maxHeight;
  58. const containerSx = {
  59. width: '100%',
  60. minWidth: 0,
  61. ...(height ? { height } : {}),
  62. ...(maxHeight ? { maxHeight, height: '100%' } : {}),
  63. overflow: 'hidden',
  64. };
  65. const [rowCount, setRowCount] = useState(0);
  66. useEffect(() => {
  67. if (doLoad !== undefined && Object.keys(doLoad).length>0 ){
  68. if(applySearch!=undefined){
  69. if (Object.keys(getSearchCriteria(window.location.pathname)).length>0){
  70. const localStorageSearchCriteria = getSearchCriteria(window.location.pathname)
  71. // console.log(localStorageSearchCriteria)
  72. if(localStorageSearchCriteria.start!=undefined){
  73. // console.log(localStorageSearchCriteria)
  74. setPage(localStorageSearchCriteria.start/pageSize);
  75. }
  76. }
  77. }else{
  78. setPage(0);
  79. setPageSize(parseInt(event.target.value, 10));
  80. }
  81. if (serverSorting) {
  82. if (doLoad.params?.sort && doLoad.params?.direction) {
  83. setSortModel([{
  84. field: doLoad.params.sort,
  85. sort: String(doLoad.params.direction).toLowerCase()
  86. }]);
  87. } else {
  88. setSortModel([]);
  89. }
  90. }
  91. set_doLoad(doLoad);
  92. setLoading(true)
  93. }
  94. }, [doLoad]);
  95. const handleSortModelChange = (newModel) => {
  96. if (!serverSorting) return;
  97. setSortModel(newModel);
  98. setPage(0);
  99. const params = { ...(_doLoad.params || {}) };
  100. if (newModel?.length > 0 && newModel[0].field && newModel[0].sort) {
  101. params.sort = newModel[0].field;
  102. params.direction = newModel[0].sort;
  103. } else {
  104. delete params.sort;
  105. delete params.direction;
  106. }
  107. if (!pagination || (disablePagingOnGazetteIssue && hasGazetteIssueFilter(params))) {
  108. delete params.start;
  109. delete params.limit;
  110. } else {
  111. params.start = 0;
  112. params.limit = pageSize;
  113. }
  114. if (applySearch != undefined) {
  115. applySearch(params);
  116. } else {
  117. set_doLoad({ ..._doLoad, params });
  118. setLoading(true);
  119. }
  120. };
  121. const ignorePaging = !pagination || (disablePagingOnGazetteIssue && hasGazetteIssueFilter(_doLoad?.params));
  122. const effectivePagination = pagination && !ignorePaging;
  123. useEffect(() => {
  124. getDataList();
  125. }, [_doLoad, page]);
  126. useEffect(() => {
  127. if (sx) {
  128. set_sx(sx);
  129. }
  130. if (hideFooterSelectedRowCount) {
  131. setMyHideFooterSelectedRowCount(hideFooterSelectedRowCount);
  132. }
  133. if (rowModesModel) {
  134. set_rowModesModel(rowModesModel)
  135. }
  136. if (rows) {
  137. set_rows(rows)
  138. setRowCount(rows.length)
  139. }
  140. if (columns) {
  141. set_columns(columns)
  142. }
  143. if (pageSizeOptions) {
  144. set_pageSizeOptions(pageSizeOptions)
  145. }
  146. // if (autoHeight !== undefined) {
  147. // set_autoHeight(autoHeight)
  148. // }
  149. if (editMode) {
  150. set_editMode(editMode);
  151. }
  152. if (filterItems) {
  153. set_filterItems(filterItems);
  154. }
  155. if (customPageSize) {
  156. setPageSize(customPageSize);
  157. }
  158. // console.log(_doLoad)
  159. if (_doLoad !== undefined && Object.keys(_doLoad).length==0 ){
  160. setLoading(false)
  161. if (applyGridOnReady !== undefined){
  162. applyGridOnReady(false)
  163. }
  164. }
  165. }, [sx, hideFooterSelectedRowCount, rowModesModel, rows, columns, pageSizeOptions, autoHeight, editMode, filterItems, customPageSize]);
  166. const handleChangePage = (event, newPage) => {
  167. setPage(newPage);
  168. };
  169. const handleChangePageSize = (event) => {
  170. setPageSize(parseInt(event.target.value, 10));
  171. setPage(0);
  172. };
  173. function CustomNoRowsOverlay() {
  174. return (
  175. <GridOverlay
  176. sx={{
  177. width: "100%",
  178. justifyContent: "flex-start", // align overlay to left
  179. pl: 2, // padding-left to match grid cells
  180. }}
  181. >
  182. <Typography variant="body1" sx={{ textAlign: "left", width: "100%" }}>
  183. <FormattedMessage id="noRecordFound" />
  184. </Typography>
  185. </GridOverlay>
  186. );
  187. }
  188. function getDataList() {
  189. // console.log(Object.keys(_doLoad.params).length > 0)
  190. // console.log(Object.keys(_doLoad.params).length > 0)
  191. if (_doLoad?.url == null){
  192. setLoading(false)
  193. return;
  194. }
  195. if (_doLoad.params == undefined) return;
  196. if (_doLoad.params.searchCriteria !== undefined) return;
  197. if (_doLoad.params == null) _doLoad.params = {};
  198. if (!pagination || (disablePagingOnGazetteIssue && hasGazetteIssueFilter(_doLoad.params))) {
  199. delete _doLoad.params.start;
  200. delete _doLoad.params.limit;
  201. } else {
  202. _doLoad.params.start = page * pageSize;
  203. _doLoad.params.limit = pageSize;
  204. }
  205. if(checkSearchCriteriaPath(window.location.pathname)){
  206. if(window.location.pathname === "/publicNotice"){
  207. if (tab != undefined && tab ==="application"){
  208. localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:_doLoad.params}))
  209. }
  210. }else if (window.location.pathname != "/publicNotice"){
  211. localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:_doLoad.params}))
  212. }
  213. }
  214. HttpUtils.get({
  215. url: _doLoad.url,
  216. params: _doLoad.params,
  217. onSuccess: function (responseData) {
  218. set_rows(responseData?.records);
  219. setRowCount(responseData?.count);
  220. if (_doLoad.callback != null) {
  221. _doLoad.callback(responseData);
  222. }
  223. setLoading(false)
  224. // console.log(applyGridOnReady)
  225. if (applyGridOnReady !== undefined){
  226. applyGridOnReady(false)
  227. }
  228. },
  229. onError: function (error){
  230. console.log(error)
  231. setLoading(false)
  232. if (applyGridOnReady !== undefined){
  233. applyGridOnReady(false)
  234. }
  235. }
  236. });
  237. }
  238. const gridRootRef = useRef(null);
  239. useEffect(() => {
  240. const root = gridRootRef.current;
  241. if (!root) return;
  242. const sortText = intl.formatMessage({ id: "sort", defaultMessage: "Sort" });
  243. const apply = () => {
  244. // 1) Make ALL column headers tabbable (optional; DataGrid already manages focus well)
  245. root
  246. .querySelectorAll('.MuiDataGrid-columnHeaders [role="columnheader"]')
  247. .forEach((el) => {
  248. if (el.getAttribute("tabindex") !== "0") el.setAttribute("tabindex", "0");
  249. });
  250. // 2) Localize sort icon button label (handles "sort"/"Sort"/any old value)
  251. const sortButtons = root.querySelectorAll(
  252. '.MuiDataGrid-columnHeaders button.MuiIconButton-root'
  253. );
  254. sortButtons.forEach((btn) => {
  255. const al = (btn.getAttribute("aria-label") || "").trim().toLowerCase();
  256. const ti = (btn.getAttribute("title") || "").trim().toLowerCase();
  257. // Only rewrite the ones that are the sort icon buttons
  258. if (al === "sort" || ti === "sort") {
  259. btn.setAttribute("aria-label", sortText);
  260. btn.setAttribute("title", sortText);
  261. }
  262. });
  263. };
  264. apply();
  265. const obs = new MutationObserver(apply);
  266. obs.observe(root, { childList: true, subtree: true });
  267. return () => obs.disconnect();
  268. }, [intl]);
  269. return (
  270. <Box sx={containerSx} ref={gridRootRef} role="table">
  271. <DataGrid
  272. {...props}
  273. rows={_rows}
  274. rowCount={rowCount || 0}
  275. columns={_columns}
  276. disableColumnMenu
  277. shrinkWrap
  278. rowModesModel={_rowModesModel}
  279. pageSizeOptions={effectivePagination ? _pageSizeOptions : []}
  280. editMode={_editMode}
  281. autoHeight={effectiveAutoHeight}
  282. hideFooterSelectedRowCount={myHideFooterSelectedRowCount}
  283. filterModel={{ items: _filterItems }}
  284. loading={loading}
  285. paginationMode={effectivePagination ? "server" : undefined}
  286. sortingMode={serverSorting ? "server" : undefined}
  287. sortModel={serverSorting ? sortModel : undefined}
  288. onSortModelChange={serverSorting ? handleSortModelChange : undefined}
  289. sx={{
  290. ..._sx,
  291. '& .MuiDataGrid-virtualScroller': {
  292. overflowY: height || maxHeight ? 'auto' : 'visible',
  293. overflowX: height || maxHeight ? 'auto' : 'visible',
  294. },
  295. // 👇 completely hide the footer when pagination is off
  296. ...(!effectivePagination && {
  297. '& .MuiDataGrid-footerContainer': {
  298. display: 'none',
  299. },
  300. }),
  301. }}
  302. components={{
  303. NoRowsOverlay: CustomNoRowsOverlay,
  304. ...(effectivePagination
  305. ? {
  306. Pagination: () => (
  307. <TablePagination
  308. component="div"
  309. count={rowCount || 0}
  310. page={page}
  311. rowsPerPage={pageSize}
  312. rowsPerPageOptions={_pageSizeOptions}
  313. labelDisplayedRows={() => {
  314. const total = rowCount || 0;
  315. if (!_rows?.length || total === 0) {
  316. return `0-0 ${intl.formatMessage({ id: "of" })} ${total}`;
  317. }
  318. const from = page * pageSize + 1;
  319. const to = Math.min(page * pageSize + _rows.length, total);
  320. return `${from}-${to} ${intl.formatMessage({ id: "of" })} ${total}`;
  321. }}
  322. labelRowsPerPage={intl.formatMessage({ id: "rowsPerPage" }) + ":"}
  323. getItemAriaLabel={(type) => {
  324. if (type === 'previous') {
  325. return intl.formatMessage({ id: 'paginationPrev' });
  326. }
  327. if (type === 'next') {
  328. return intl.formatMessage({ id: 'paginationNext' });
  329. }
  330. return '';
  331. }}
  332. onPageChange={handleChangePage}
  333. onRowsPerPageChange={handleChangePageSize}
  334. />
  335. ),
  336. }
  337. : {}),
  338. }}
  339. />
  340. </Box>
  341. );
  342. }