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.

396 lines
15 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, hideRowsPerPage = false, ...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 (disablePagingOnGazetteIssue && hasGazetteIssueFilter(doLoad.params)) {
  69. setPage(0);
  70. } else if(applySearch!=undefined){
  71. if (Object.keys(getSearchCriteria(window.location.pathname)).length>0){
  72. const localStorageSearchCriteria = getSearchCriteria(window.location.pathname)
  73. // console.log(localStorageSearchCriteria)
  74. if(localStorageSearchCriteria.start!=undefined){
  75. // console.log(localStorageSearchCriteria)
  76. setPage(localStorageSearchCriteria.start/pageSize);
  77. }
  78. }
  79. }else{
  80. setPage(0);
  81. setPageSize(parseInt(event.target.value, 10));
  82. }
  83. if (serverSorting) {
  84. if (doLoad.params?.sort && doLoad.params?.direction) {
  85. setSortModel([{
  86. field: doLoad.params.sort,
  87. sort: String(doLoad.params.direction).toLowerCase()
  88. }]);
  89. } else {
  90. setSortModel([]);
  91. }
  92. }
  93. set_doLoad(doLoad);
  94. setLoading(true)
  95. }
  96. }, [doLoad]);
  97. const handleSortModelChange = (newModel) => {
  98. if (!serverSorting) return;
  99. setSortModel(newModel);
  100. setPage(0);
  101. const params = { ...(_doLoad.params || {}) };
  102. if (newModel?.length > 0 && newModel[0].field && newModel[0].sort) {
  103. params.sort = newModel[0].field;
  104. params.direction = newModel[0].sort;
  105. } else {
  106. delete params.sort;
  107. delete params.direction;
  108. }
  109. if (!pagination || (disablePagingOnGazetteIssue && hasGazetteIssueFilter(params))) {
  110. delete params.start;
  111. delete params.limit;
  112. setPage(0);
  113. } else {
  114. params.start = 0;
  115. params.limit = pageSize;
  116. }
  117. if (applySearch != undefined) {
  118. applySearch(params);
  119. } else {
  120. set_doLoad({ ..._doLoad, params });
  121. setLoading(true);
  122. }
  123. };
  124. const loadAllOnGazetteIssue = disablePagingOnGazetteIssue && hasGazetteIssueFilter(_doLoad?.params);
  125. const ignorePagingParams = !pagination || loadAllOnGazetteIssue;
  126. const showPaginationBar = pagination;
  127. useEffect(() => {
  128. getDataList();
  129. }, [_doLoad, page, pageSize]);
  130. useEffect(() => {
  131. if (sx) {
  132. set_sx(sx);
  133. }
  134. if (hideFooterSelectedRowCount) {
  135. setMyHideFooterSelectedRowCount(hideFooterSelectedRowCount);
  136. }
  137. if (rowModesModel) {
  138. set_rowModesModel(rowModesModel)
  139. }
  140. if (rows) {
  141. set_rows(rows)
  142. setRowCount(rows.length)
  143. }
  144. if (columns) {
  145. set_columns(columns)
  146. }
  147. if (pageSizeOptions) {
  148. set_pageSizeOptions(pageSizeOptions)
  149. }
  150. // if (autoHeight !== undefined) {
  151. // set_autoHeight(autoHeight)
  152. // }
  153. if (editMode) {
  154. set_editMode(editMode);
  155. }
  156. if (filterItems) {
  157. set_filterItems(filterItems);
  158. }
  159. if (customPageSize) {
  160. setPageSize(customPageSize);
  161. }
  162. // console.log(_doLoad)
  163. if (_doLoad !== undefined && Object.keys(_doLoad).length==0 ){
  164. setLoading(false)
  165. if (applyGridOnReady !== undefined){
  166. applyGridOnReady(false)
  167. }
  168. }
  169. }, [sx, hideFooterSelectedRowCount, rowModesModel, rows, columns, pageSizeOptions, autoHeight, editMode, filterItems, customPageSize]);
  170. const handleChangePage = (event, newPage) => {
  171. setPage(newPage);
  172. };
  173. const handleChangePageSize = (event) => {
  174. setPageSize(parseInt(event.target.value, 10));
  175. setPage(0);
  176. };
  177. function CustomNoRowsOverlay() {
  178. return (
  179. <GridOverlay
  180. sx={{
  181. width: "100%",
  182. justifyContent: "flex-start", // align overlay to left
  183. pl: 2, // padding-left to match grid cells
  184. }}
  185. >
  186. <Typography variant="body1" sx={{ textAlign: "left", width: "100%" }}>
  187. <FormattedMessage id="noRecordFound" />
  188. </Typography>
  189. </GridOverlay>
  190. );
  191. }
  192. function getDataList() {
  193. // console.log(Object.keys(_doLoad.params).length > 0)
  194. // console.log(Object.keys(_doLoad.params).length > 0)
  195. if (_doLoad?.url == null){
  196. setLoading(false)
  197. return;
  198. }
  199. if (_doLoad.params == undefined) return;
  200. if (_doLoad.params.searchCriteria !== undefined) return;
  201. if (_doLoad.params == null) _doLoad.params = {};
  202. if (ignorePagingParams) {
  203. delete _doLoad.params.start;
  204. delete _doLoad.params.limit;
  205. } else {
  206. _doLoad.params.start = page * pageSize;
  207. _doLoad.params.limit = pageSize;
  208. }
  209. if(checkSearchCriteriaPath(window.location.pathname)){
  210. if(window.location.pathname === "/publicNotice"){
  211. if (tab != undefined && tab ==="application"){
  212. localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:_doLoad.params}))
  213. }
  214. }else if (window.location.pathname != "/publicNotice"){
  215. localStorage.setItem('searchCriteria', JSON.stringify({path:window.location.pathname,data:_doLoad.params}))
  216. }
  217. }
  218. setLoading(true);
  219. if (applyGridOnReady !== undefined) {
  220. applyGridOnReady(true);
  221. }
  222. HttpUtils.get({
  223. url: _doLoad.url,
  224. params: _doLoad.params,
  225. onSuccess: function (responseData) {
  226. set_rows(responseData?.records);
  227. setRowCount(responseData?.count);
  228. if (_doLoad.callback != null) {
  229. _doLoad.callback(responseData);
  230. }
  231. setLoading(false)
  232. // console.log(applyGridOnReady)
  233. if (applyGridOnReady !== undefined){
  234. applyGridOnReady(false)
  235. }
  236. },
  237. onError: function (error){
  238. console.log(error)
  239. setLoading(false)
  240. if (applyGridOnReady !== undefined){
  241. applyGridOnReady(false)
  242. }
  243. }
  244. });
  245. }
  246. const gridRootRef = useRef(null);
  247. useEffect(() => {
  248. const root = gridRootRef.current;
  249. if (!root) return;
  250. const sortText = intl.formatMessage({ id: "sort", defaultMessage: "Sort" });
  251. const apply = () => {
  252. // 1) Make ALL column headers tabbable (optional; DataGrid already manages focus well)
  253. root
  254. .querySelectorAll('.MuiDataGrid-columnHeaders [role="columnheader"]')
  255. .forEach((el) => {
  256. if (el.getAttribute("tabindex") !== "0") el.setAttribute("tabindex", "0");
  257. });
  258. // 2) Localize sort icon button label (handles "sort"/"Sort"/any old value)
  259. const sortButtons = root.querySelectorAll(
  260. '.MuiDataGrid-columnHeaders button.MuiIconButton-root'
  261. );
  262. sortButtons.forEach((btn) => {
  263. const al = (btn.getAttribute("aria-label") || "").trim().toLowerCase();
  264. const ti = (btn.getAttribute("title") || "").trim().toLowerCase();
  265. // Only rewrite the ones that are the sort icon buttons
  266. if (al === "sort" || ti === "sort") {
  267. btn.setAttribute("aria-label", sortText);
  268. btn.setAttribute("title", sortText);
  269. }
  270. });
  271. };
  272. apply();
  273. const obs = new MutationObserver(apply);
  274. obs.observe(root, { childList: true, subtree: true });
  275. return () => obs.disconnect();
  276. }, [intl]);
  277. return (
  278. <Box sx={containerSx} ref={gridRootRef} role="table">
  279. <DataGrid
  280. {...props}
  281. rows={_rows}
  282. rowCount={rowCount || 0}
  283. columns={_columns}
  284. disableColumnMenu
  285. shrinkWrap
  286. rowModesModel={_rowModesModel}
  287. pageSizeOptions={showPaginationBar ? _pageSizeOptions : []}
  288. editMode={_editMode}
  289. autoHeight={effectiveAutoHeight}
  290. hideFooterSelectedRowCount={myHideFooterSelectedRowCount}
  291. filterModel={{ items: _filterItems }}
  292. loading={loading}
  293. paginationMode={showPaginationBar ? "server" : undefined}
  294. sortingMode={serverSorting ? "server" : undefined}
  295. sortModel={serverSorting ? sortModel : undefined}
  296. onSortModelChange={serverSorting ? handleSortModelChange : undefined}
  297. sx={{
  298. ..._sx,
  299. '& .MuiDataGrid-virtualScroller': {
  300. overflowY: height || maxHeight ? 'auto' : 'visible',
  301. overflowX: height || maxHeight ? 'auto' : 'visible',
  302. ...(loading && { filter: 'blur(2px)' }),
  303. },
  304. '& .MuiDataGrid-overlay': {
  305. backgroundColor: 'rgba(255, 255, 255, 0.55)',
  306. },
  307. // Hide the footer only when pagination is disabled for the grid
  308. ...(!showPaginationBar && {
  309. '& .MuiDataGrid-footerContainer': {
  310. display: 'none',
  311. },
  312. }),
  313. }}
  314. components={{
  315. NoRowsOverlay: CustomNoRowsOverlay,
  316. ...(showPaginationBar
  317. ? {
  318. Pagination: () => {
  319. const total = rowCount || 0;
  320. const pagerPage = loadAllOnGazetteIssue ? 0 : page;
  321. const pagerPageSize = loadAllOnGazetteIssue ? Math.max(total, 1) : pageSize;
  322. return (
  323. <TablePagination
  324. component="div"
  325. count={total}
  326. page={pagerPage}
  327. rowsPerPage={pagerPageSize}
  328. rowsPerPageOptions={(loadAllOnGazetteIssue || hideRowsPerPage) ? [] : _pageSizeOptions}
  329. labelDisplayedRows={() => {
  330. if (!_rows?.length || total === 0) {
  331. return `0-0 ${intl.formatMessage({ id: "of" })} ${total}`;
  332. }
  333. const from = loadAllOnGazetteIssue ? 1 : page * pageSize + 1;
  334. const to = loadAllOnGazetteIssue
  335. ? _rows.length
  336. : Math.min(page * pageSize + _rows.length, total);
  337. return `${from}-${to} ${intl.formatMessage({ id: "of" })} ${total}`;
  338. }}
  339. labelRowsPerPage={hideRowsPerPage ? () => "" : intl.formatMessage({ id: "rowsPerPage" }) + ":"}
  340. getItemAriaLabel={(type) => {
  341. if (type === 'previous') {
  342. return intl.formatMessage({ id: 'paginationPrev' });
  343. }
  344. if (type === 'next') {
  345. return intl.formatMessage({ id: 'paginationNext' });
  346. }
  347. return '';
  348. }}
  349. onPageChange={loadAllOnGazetteIssue ? () => {} : handleChangePage}
  350. onRowsPerPageChange={(loadAllOnGazetteIssue || hideRowsPerPage) ? () => {} : handleChangePageSize}
  351. />
  352. );
  353. },
  354. }
  355. : {}),
  356. }}
  357. />
  358. </Box>
  359. );
  360. }