Merge remote-tracking branch 'remotes/origin/feature/QQQ-26-exports-poc' into feature/sprint-7-integration

This commit is contained in:
2022-07-25 08:56:18 -05:00
3 changed files with 133 additions and 9 deletions

View File

@ -22,7 +22,7 @@ import Icon from "@mui/material/Icon";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import Link from "@mui/material/Link";
import {Alert} from "@mui/material";
import {Alert, tableFooterClasses} from "@mui/material";
import {
DataGridPro,
GridCallbackDetails,
@ -40,7 +40,9 @@ import {
GridToolbarContainer,
GridToolbarDensitySelector,
GridToolbarExport,
GridToolbarExportContainer,
GridToolbarFilterButton,
GridExportMenuItemProps,
} from "@mui/x-data-grid-pro";
// Material Dashboard 2 PRO React TS components
@ -99,6 +101,7 @@ function EntityList({table}: Props): JSX.Element
const [buttonText, setButtonText] = useState("");
const [tableState, setTableState] = useState("");
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData);
const [, setFiltersMenu] = useState(null);
const [actionsMenu, setActionsMenu] = useState(null);
const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
@ -192,11 +195,12 @@ function EntityList({table}: Props): JSX.Element
{
(async () =>
{
const tableMetaData = await QClient.loadTableMetaData(tableName);
const newTableMetaData = await QClient.loadTableMetaData(tableName);
setTableMetaData(newTableMetaData);
if (columnSortModel.length === 0)
{
columnSortModel.push({
field: tableMetaData.primaryKeyField,
field: newTableMetaData.primaryKeyField,
sort: "desc",
});
setColumnSortModel(columnSortModel);
@ -206,8 +210,8 @@ function EntityList({table}: Props): JSX.Element
const count = await QClient.count(tableName, qFilter);
setTotalRecords(count);
setButtonText(`new ${tableMetaData.label}`);
setTableLabel(tableMetaData.label);
setButtonText(`new ${newTableMetaData.label}`);
setTableLabel(newTableMetaData.label);
const columns = [] as GridColDef[];
@ -236,10 +240,10 @@ function EntityList({table}: Props): JSX.Element
rows.push(Object.fromEntries(record.values.entries()));
});
const sortedKeys = [...tableMetaData.fields.keys()].sort();
const sortedKeys = [...newTableMetaData.fields.keys()].sort();
sortedKeys.forEach((key) =>
{
const field = tableMetaData.fields.get(key);
const field = newTableMetaData.fields.get(key);
let columnType = "string";
switch (field.type)
@ -268,7 +272,7 @@ function EntityList({table}: Props): JSX.Element
width: 200,
};
if (key === tableMetaData.primaryKeyField)
if (key === newTableMetaData.primaryKeyField)
{
column.width = 75;
columns.splice(0, 0, column);
@ -369,6 +373,83 @@ function EntityList({table}: Props): JSX.Element
})();
}
interface QExportMenuItemProps extends GridExportMenuItemProps<{}>
{
format: string;
}
function ExportMenuItem(props: QExportMenuItemProps)
{
const {format, hideMenu} = props;
return (
<MenuItem
disabled={totalRecords === 0}
onClick={() =>
{
///////////////////////////////////////////////////////////////////////////////
// build the list of visible fields. note, not doing them in-order (in case //
// the user did drag & drop), because column order model isn't right yet //
// so just doing them to match columns (which were pKey, then sorted) //
///////////////////////////////////////////////////////////////////////////////
const visibleFields: string[] = [];
columns.forEach((gridColumn) =>
{
const fieldName = gridColumn.field;
// @ts-ignore
if (columnVisibilityModel[fieldName] !== false)
{
visibleFields.push(fieldName);
}
});
///////////////////////
// zero-pad function //
///////////////////////
const zp = (value: number): string => (value < 10 ? `0${value}` : `${value}`);
//////////////////////////////////////
// construct the url for the export //
//////////////////////////////////////
const d = new Date();
const dateString = `${d.getFullYear()}-${zp(d.getMonth())}-${zp(d.getDate())} ${zp(d.getHours())}${zp(d.getMinutes())}`;
const filename = `${tableMetaData.label} Export ${dateString}.${format}`;
const url = `/data/${tableMetaData.name}/export/${filename}?filter=${encodeURIComponent(JSON.stringify(buildQFilter()))}&fields=${visibleFields.join(",")}`;
//////////////////////////////////////////////////////////////////////////////////////
// open a window (tab) with a little page that says the file is being generated. //
// then have that page load the url for the export. //
// If there's an error, it'll appear in that window. else, the file will download. //
//////////////////////////////////////////////////////////////////////////////////////
const exportWindow = window.open("", "_blank");
exportWindow.document.write(`<html lang="en">
<head>
<style>
* { font-family: "Roboto","Helvetica","Arial",sans-serif; }
</style>
<title>${filename}</title>
<script>
setTimeout(() =>
{
window.location.href="${url}";
}, 1);
</script>
</head>
<body>Generating file <u>${filename}</u> with ${totalRecords.toLocaleString()} records...</body>
</html>`);
///////////////////////////////////////////
// Hide the export menu after the export //
///////////////////////////////////////////
hideMenu?.();
}}
>
Export
{` ${format.toUpperCase()}`}
</MenuItem>
);
}
function CustomToolbar()
{
return (
@ -376,7 +457,10 @@ function EntityList({table}: Props): JSX.Element
<GridToolbarColumnsButton />
<GridToolbarFilterButton />
<GridToolbarDensitySelector />
<GridToolbarExport />
<GridToolbarExportContainer>
<ExportMenuItem format="csv" />
<ExportMenuItem format="xlsx" />
</GridToolbarExportContainer>
<div>
{
selectFullFilterState === "checked" && (