SPRINT-12: added pagination to top of entity list table, fix for record not found view enttity, store rowsperpage in local storage per table

This commit is contained in:
Tim Chamberlain
2022-10-06 19:17:31 -05:00
parent c87871860e
commit 0182db5f02
3 changed files with 128 additions and 110 deletions

View File

@ -34,28 +34,7 @@ import Icon from "@mui/material/Icon";
import LinearProgress from "@mui/material/LinearProgress"; import LinearProgress from "@mui/material/LinearProgress";
import Menu from "@mui/material/Menu"; import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import { import {DataGridPro, getGridDateOperators, getGridNumericOperators, getGridStringOperators, GridCallbackDetails, GridColDef, GridColumnOrderChangeParams, GridColumnVisibilityModel, GridExportMenuItemProps, GridFilterItem, GridFilterModel, GridRowId, GridRowParams, GridRowsProp, GridSelectionModel, GridSortItem, GridSortModel, GridToolbarColumnsButton, GridToolbarContainer, GridToolbarDensitySelector, GridToolbarExportContainer, GridToolbarFilterButton, MuiEvent} from "@mui/x-data-grid-pro";
DataGridPro, getGridDateOperators, getGridNumericOperators, getGridStringOperators,
GridCallbackDetails,
GridColDef,
GridColumnOrderChangeParams,
GridColumnVisibilityModel,
GridExportMenuItemProps,
GridFilterItem,
GridFilterModel,
GridRowId,
GridRowParams,
GridRowsProp,
GridSelectionModel,
GridSortItem,
GridSortModel,
GridToolbarColumnsButton,
GridToolbarContainer,
GridToolbarDensitySelector,
GridToolbarExportContainer,
GridToolbarFilterButton,
MuiEvent
} from "@mui/x-data-grid-pro";
import {GridFilterOperator} from "@mui/x-data-grid/models/gridFilterOperator"; import {GridFilterOperator} from "@mui/x-data-grid/models/gridFilterOperator";
import React, {useCallback, useContext, useEffect, useReducer, useRef, useState} from "react"; import React, {useCallback, useContext, useEffect, useReducer, useRef, useState} from "react";
import {Link, useNavigate, useParams, useSearchParams} from "react-router-dom"; import {Link, useNavigate, useParams, useSearchParams} from "react-router-dom";
@ -74,6 +53,7 @@ import QValueUtils from "qqq/utils/QValueUtils";
const COLUMN_VISIBILITY_LOCAL_STORAGE_KEY_ROOT = "qqq.columnVisibility"; const COLUMN_VISIBILITY_LOCAL_STORAGE_KEY_ROOT = "qqq.columnVisibility";
const COLUMN_SORT_LOCAL_STORAGE_KEY_ROOT = "qqq.columnSort"; const COLUMN_SORT_LOCAL_STORAGE_KEY_ROOT = "qqq.columnSort";
const FILTER_LOCAL_STORAGE_KEY_ROOT = "qqq.filter"; const FILTER_LOCAL_STORAGE_KEY_ROOT = "qqq.filter";
const ROWS_PER_PAGE_LOCAL_STORAGE_KEY_ROOT = "qqq.rowsPerPage";
interface Props interface Props
{ {
@ -140,10 +120,12 @@ function EntityList({table}: Props): JSX.Element
// look for defaults in the local storage // // look for defaults in the local storage //
//////////////////////////////////////////// ////////////////////////////////////////////
const sortLocalStorageKey = `${COLUMN_SORT_LOCAL_STORAGE_KEY_ROOT}.${tableName}`; const sortLocalStorageKey = `${COLUMN_SORT_LOCAL_STORAGE_KEY_ROOT}.${tableName}`;
const rowsPerPageLocalStorageKey = `${ROWS_PER_PAGE_LOCAL_STORAGE_KEY_ROOT}.${tableName}`;
const columnVisibilityLocalStorageKey = `${COLUMN_VISIBILITY_LOCAL_STORAGE_KEY_ROOT}.${tableName}`; const columnVisibilityLocalStorageKey = `${COLUMN_VISIBILITY_LOCAL_STORAGE_KEY_ROOT}.${tableName}`;
const filterLocalStorageKey = `${FILTER_LOCAL_STORAGE_KEY_ROOT}.${tableName}`; const filterLocalStorageKey = `${FILTER_LOCAL_STORAGE_KEY_ROOT}.${tableName}`;
let defaultSort = [] as GridSortItem[]; let defaultSort = [] as GridSortItem[];
let defaultVisibility = {}; let defaultVisibility = {};
let defaultRowsPerPage = 10;
if (localStorage.getItem(sortLocalStorageKey)) if (localStorage.getItem(sortLocalStorageKey))
{ {
@ -153,10 +135,15 @@ function EntityList({table}: Props): JSX.Element
{ {
defaultVisibility = JSON.parse(localStorage.getItem(columnVisibilityLocalStorageKey)); defaultVisibility = JSON.parse(localStorage.getItem(columnVisibilityLocalStorageKey));
} }
if (localStorage.getItem(rowsPerPageLocalStorageKey))
{
defaultRowsPerPage = JSON.parse(localStorage.getItem(rowsPerPageLocalStorageKey));
}
const [filterModel, setFilterModel] = useState({items: []} as GridFilterModel); const [filterModel, setFilterModel] = useState({items: []} as GridFilterModel);
const [columnSortModel, setColumnSortModel] = useState(defaultSort); const [columnSortModel, setColumnSortModel] = useState(defaultSort);
const [columnVisibilityModel, setColumnVisibilityModel] = useState(defaultVisibility); const [columnVisibilityModel, setColumnVisibilityModel] = useState(defaultVisibility);
const [rowsPerPage, setRowsPerPage] = useState(defaultRowsPerPage);
/////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////
// for some reason, if we set the filterModel to what is in local storage, an onChange event // // for some reason, if we set the filterModel to what is in local storage, an onChange event //
@ -174,7 +161,6 @@ function EntityList({table}: Props): JSX.Element
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]); const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
const [pageNumber, setPageNumber] = useState(0); const [pageNumber, setPageNumber] = useState(0);
const [totalRecords, setTotalRecords] = useState(0); const [totalRecords, setTotalRecords] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [selectedIds, setSelectedIds] = useState([] as string[]); const [selectedIds, setSelectedIds] = useState([] as string[]);
const [selectFullFilterState, setSelectFullFilterState] = useState("n/a" as "n/a" | "checked" | "filter"); const [selectFullFilterState, setSelectFullFilterState] = useState("n/a" as "n/a" | "checked" | "filter");
const [columns, setColumns] = useState([] as GridColDef[]); const [columns, setColumns] = useState([] as GridColDef[]);
@ -356,7 +342,7 @@ function EntityList({table}: Props): JSX.Element
const getCustomGridBooleanOperators = (): GridFilterOperator[] => const getCustomGridBooleanOperators = (): GridFilterOperator[] =>
{ {
return [booleanTrueOperator, booleanFalseOperator, booleanEmptyOperator, booleanNotEmptyOperator]; return [booleanTrueOperator, booleanFalseOperator, booleanEmptyOperator, booleanNotEmptyOperator];
} };
/////////////////////////// ///////////////////////////
// display query results // // display query results //
@ -557,6 +543,7 @@ function EntityList({table}: Props): JSX.Element
const handleRowsPerPageChange = (size: number) => const handleRowsPerPageChange = (size: number) =>
{ {
setRowsPerPage(size); setRowsPerPage(size);
localStorage.setItem(rowsPerPageLocalStorageKey, JSON.stringify(size));
}; };
const navigate = useNavigate(); const navigate = useNavigate();
@ -901,6 +888,9 @@ function EntityList({table}: Props): JSX.Element
) )
} }
</div> </div>
<div className="pagination">
<CustomPagination />
</div>
</GridToolbarContainer> </GridToolbarContainer>
); );
} }

View File

@ -19,6 +19,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {QException} from "@kingsrook/qqq-frontend-core/lib/exceptions/QException";
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData"; import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableSection"; import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableSection";
@ -80,8 +81,9 @@ function ViewContents({id, table}: Props): JSX.Element
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]); const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
const [actionsMenu, setActionsMenu] = useState(null); const [actionsMenu, setActionsMenu] = useState(null);
const [tableWidgets, setTableWidgets] = useState([] as QWidgetMetaData[]); const [tableWidgets, setTableWidgets] = useState([] as QWidgetMetaData[]);
const [notFoundMessage, setNotFoundMessage] = useState(null);
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const {pageHeader, setPageHeader} = useContext(QContext); const {setPageHeader} = useContext(QContext);
const [, forceUpdate] = useReducer((x) => x + 1, 0); const [, forceUpdate] = useReducer((x) => x + 1, 0);
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget); const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
@ -126,8 +128,24 @@ function ViewContents({id, table}: Props): JSX.Element
///////////////////// /////////////////////
// load the record // // load the record //
///////////////////// /////////////////////
const record = await qController.get(tableName, id); let record: QRecord;
try
{
record = await qController.get(tableName, id);
setRecord(record); setRecord(record);
}
catch (e)
{
if (e instanceof QException)
{
if ((e as QException).status === "404")
{
setNotFoundMessage(`${tableMetaData.label} ${id} could not be found.`);
return;
}
}
}
setPageHeader(record.recordLabel); setPageHeader(record.recordLabel);
/////////////////////////// ///////////////////////////
@ -254,7 +272,10 @@ function ViewContents({id, table}: Props): JSX.Element
); );
return ( return (
notFoundMessage
?
<MDBox>{notFoundMessage}</MDBox>
:
<MDBox pb={3}> <MDBox pb={3}>
{ {
(searchParams.get("createSuccess") || searchParams.get("updateSuccess")) ? ( (searchParams.get("createSuccess") || searchParams.get("updateSuccess")) ? (

View File

@ -111,6 +111,13 @@
font-size: 14px; font-size: 14px;
} }
.MuiDataGrid-toolbarContainer .pagination
{
display: flex;
flex-grow: 1;
justify-content: flex-end;
}
/* Help make the radio, text, and icon wrap in a good way */ /* Help make the radio, text, and icon wrap in a good way */
.doFullValidationRadios label .doFullValidationRadios label
{ {