mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-18 13:20:43 +00:00
column stats checkpoint
This commit is contained in:
@ -21,13 +21,17 @@
|
|||||||
|
|
||||||
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
||||||
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 {QJobComplete} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobComplete";
|
import {QJobComplete} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobComplete";
|
||||||
import {QJobError} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
|
import {QJobError} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
|
||||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||||
|
import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter";
|
||||||
|
import {TablePagination} from "@mui/material";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
import LinearProgress from "@mui/material/LinearProgress";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import {DataGridPro} from "@mui/x-data-grid-pro";
|
import {DataGridPro} from "@mui/x-data-grid-pro";
|
||||||
import {columnsStateInitializer} from "@mui/x-data-grid/internals";
|
import FormData from "form-data";
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import DataGridUtils from "qqq/utils/DataGridUtils";
|
import DataGridUtils from "qqq/utils/DataGridUtils";
|
||||||
import Client from "qqq/utils/qqq/Client";
|
import Client from "qqq/utils/qqq/Client";
|
||||||
@ -36,16 +40,15 @@ interface Props
|
|||||||
{
|
{
|
||||||
tableMetaData: QTableMetaData;
|
tableMetaData: QTableMetaData;
|
||||||
fieldMetaData: QFieldMetaData;
|
fieldMetaData: QFieldMetaData;
|
||||||
closeModalHandler?: (event: object, reason: string) => void;
|
filter: QQueryFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnStats.defaultProps = {
|
ColumnStats.defaultProps = {
|
||||||
closeModalHandler: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const qController = Client.getInstance();
|
const qController = Client.getInstance();
|
||||||
|
|
||||||
function ColumnStats({tableMetaData, fieldMetaData, closeModalHandler}: Props): JSX.Element
|
function ColumnStats({tableMetaData, fieldMetaData, filter}: Props): JSX.Element
|
||||||
{
|
{
|
||||||
const [statusString, setStatusString] = useState("Calculating statistics...");
|
const [statusString, setStatusString] = useState("Calculating statistics...");
|
||||||
const [isLoaded, setIsLoaded] = useState(false);
|
const [isLoaded, setIsLoaded] = useState(false);
|
||||||
@ -58,7 +61,12 @@ function ColumnStats({tableMetaData, fieldMetaData, closeModalHandler}: Props):
|
|||||||
{
|
{
|
||||||
(async () =>
|
(async () =>
|
||||||
{
|
{
|
||||||
const processResult = await qController.processRun("tableStats", `tableName=${tableMetaData.name}&fieldName=${fieldMetaData.name}`);
|
const formData = new FormData();
|
||||||
|
formData.append("tableName", tableMetaData.name);
|
||||||
|
formData.append("fieldName", fieldMetaData.name);
|
||||||
|
formData.append("filterJSON", JSON.stringify(filter));
|
||||||
|
const processResult = await qController.processRun("tableStats", formData);
|
||||||
|
|
||||||
setStatusString(null)
|
setStatusString(null)
|
||||||
if (processResult instanceof QJobError)
|
if (processResult instanceof QJobError)
|
||||||
{
|
{
|
||||||
@ -72,15 +80,29 @@ function ColumnStats({tableMetaData, fieldMetaData, closeModalHandler}: Props):
|
|||||||
setCountDistinct(result.values.countDistinct);
|
setCountDistinct(result.values.countDistinct);
|
||||||
|
|
||||||
const valueCounts = [] as QRecord[];
|
const valueCounts = [] as QRecord[];
|
||||||
result.values.valueCounts.forEach((object: any) =>
|
for(let i = 0; i < result.values.valueCounts.length; i++)
|
||||||
{
|
{
|
||||||
valueCounts.push(new QRecord(object));
|
valueCounts.push(new QRecord(result.values.valueCounts[i]));
|
||||||
})
|
}
|
||||||
setValueCounts(valueCounts);
|
setValueCounts(valueCounts);
|
||||||
|
|
||||||
const fakeTableMetaData = new QTableMetaData({primaryKeyField: "value", fields: {value: fieldMetaData, count: {label: "Count", type: "INTEGER"}}});
|
const fakeTableMetaData = new QTableMetaData({primaryKeyField: fieldMetaData.name});
|
||||||
|
fakeTableMetaData.fields = new Map<string, QFieldMetaData>();
|
||||||
|
fakeTableMetaData.fields.set(fieldMetaData.name, fieldMetaData);
|
||||||
|
fakeTableMetaData.fields.set("count", new QFieldMetaData({name: "count", label: "Count", type: "INTEGER"}));
|
||||||
|
fakeTableMetaData.sections = [] as QTableSection[];
|
||||||
|
fakeTableMetaData.sections.push(new QTableSection({fieldNames: [fieldMetaData.name, "count"]}));
|
||||||
|
|
||||||
const {rows, columnsToRender} = DataGridUtils.makeRows(valueCounts, fakeTableMetaData);
|
const {rows, columnsToRender} = DataGridUtils.makeRows(valueCounts, fakeTableMetaData);
|
||||||
const columns = DataGridUtils.setupGridColumns(fakeTableMetaData, columnsToRender);
|
const columns = DataGridUtils.setupGridColumns(fakeTableMetaData, columnsToRender);
|
||||||
|
|
||||||
|
columns[1].sortComparator = (v1, v2): number =>
|
||||||
|
{
|
||||||
|
const n1 = parseInt(v1.replaceAll(",", ""));
|
||||||
|
const n2 = parseInt(v2.replaceAll(",", ""));
|
||||||
|
return (n1 - n2);
|
||||||
|
}
|
||||||
|
|
||||||
setRows(rows);
|
setRows(rows);
|
||||||
setColumns(columns);
|
setColumns(columns);
|
||||||
|
|
||||||
@ -89,32 +111,71 @@ function ColumnStats({tableMetaData, fieldMetaData, closeModalHandler}: Props):
|
|||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const defaultLabelDisplayedRows = ({from, to, count}) =>
|
||||||
|
{
|
||||||
|
// todo - not done
|
||||||
|
return ("Showing stuff");
|
||||||
|
};
|
||||||
|
|
||||||
|
function CustomPagination()
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<TablePagination
|
||||||
|
component="div"
|
||||||
|
page={1}
|
||||||
|
count={1}
|
||||||
|
rowsPerPage={1000}
|
||||||
|
onPageChange={null}
|
||||||
|
labelDisplayedRows={defaultLabelDisplayedRows}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Loading()
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<LinearProgress color="info" />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Box p={3} display="flex" flexDirection="row" justifyContent="space-between" alignItems="flex-start">
|
<Box p={3} display="flex" flexDirection="row" justifyContent="space-between" alignItems="flex-start">
|
||||||
<Typography variant="h5" pb={3}>
|
<Typography variant="h5" pb={3}>
|
||||||
Column Statistics for {tableMetaData.label} : {fieldMetaData.label}
|
Column Statistics for {tableMetaData.label}: {fieldMetaData.label}
|
||||||
<Typography fontSize={14}>
|
<Typography fontSize={14}>
|
||||||
{statusString}
|
{statusString}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
{
|
<Box px={3} fontSize="1rem">
|
||||||
isLoaded && <>
|
<div>
|
||||||
<Box sx={{overflow: "auto", height: "calc( 100vh - 19rem )", position: "relative"}} px={3}>
|
<div className="fieldLabel">Distinct Values: </div> <div className="fieldValue">{Number(countDistinct).toLocaleString()}</div>
|
||||||
<b>Distinct Values: </b> {countDistinct.toLocaleString()}
|
</div>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{overflow: "auto", height: "calc( 100vh - 19rem )", position: "relative"}} px={3}>
|
||||||
<DataGridPro
|
<DataGridPro
|
||||||
autoHeight
|
components={{LoadingOverlay: Loading, Pagination: CustomPagination}}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
disableSelectionOnClick
|
disableSelectionOnClick
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
loading={!isLoaded}
|
||||||
rowBuffer={10}
|
rowBuffer={10}
|
||||||
getRowClassName={(params) => (params.indexRelativeToCurrentPage % 2 === 0 ? "even" : "odd")}
|
getRowClassName={(params) => (params.indexRelativeToCurrentPage % 2 === 0 ? "even" : "odd")}
|
||||||
|
initialState={{
|
||||||
|
sorting: {
|
||||||
|
sortModel: [
|
||||||
|
{
|
||||||
|
field: "count",
|
||||||
|
sort: "desc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
|
||||||
}
|
|
||||||
</Box>);
|
</Box>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +169,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
|||||||
const [launchingProcess, setLaunchingProcess] = useState(launchProcess);
|
const [launchingProcess, setLaunchingProcess] = useState(launchProcess);
|
||||||
const [recordIdsForProcess, setRecordIdsForProcess] = useState(null as string | QQueryFilter);
|
const [recordIdsForProcess, setRecordIdsForProcess] = useState(null as string | QQueryFilter);
|
||||||
const [columnStatsFieldName, setColumnStatsFieldName] = useState(null as string)
|
const [columnStatsFieldName, setColumnStatsFieldName] = useState(null as string)
|
||||||
|
const [filterForColumnStats, setFilterForColumnStats] = useState(null as QQueryFilter)
|
||||||
|
|
||||||
const instance = useRef({timer: null});
|
const instance = useRef({timer: null});
|
||||||
|
|
||||||
@ -995,6 +996,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
|||||||
|
|
||||||
const openColumnStatistics = async (column: GridColDef) =>
|
const openColumnStatistics = async (column: GridColDef) =>
|
||||||
{
|
{
|
||||||
|
setFilterForColumnStats(buildQFilter(filterModel));
|
||||||
setColumnStatsFieldName(column.field);
|
setColumnStatsFieldName(column.field);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1391,7 +1393,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
|||||||
<Box sx={{position: "absolute", overflowY: "auto", maxHeight: "100%", width: "100%"}}>
|
<Box sx={{position: "absolute", overflowY: "auto", maxHeight: "100%", width: "100%"}}>
|
||||||
<Card sx={{my: 5, mx: "auto", pb: 0, maxWidth: "1024px"}}>
|
<Card sx={{my: 5, mx: "auto", pb: 0, maxWidth: "1024px"}}>
|
||||||
<Box component="div">
|
<Box component="div">
|
||||||
<ColumnStats tableMetaData={tableMetaData} fieldMetaData={tableMetaData?.fields.get(columnStatsFieldName)} closeModalHandler={closeColumnStats} />
|
<ColumnStats tableMetaData={tableMetaData} fieldMetaData={tableMetaData?.fields.get(columnStatsFieldName)} filter={filterForColumnStats} />
|
||||||
<Box p={3} display="flex" flexDirection="row" justifyContent="flex-end">
|
<Box p={3} display="flex" flexDirection="row" justifyContent="flex-end">
|
||||||
<QCancelButton label="Close" onClickHandler={() => closeColumnStats(null, null)} disabled={false} />
|
<QCancelButton label="Close" onClickHandler={() => closeColumnStats(null, null)} disabled={false} />
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -350,12 +350,14 @@ input[type="search"]::-webkit-search-results-decoration { display: none; }
|
|||||||
color: rgb(52, 71, 103);
|
color: rgb(52, 71, 103);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
padding-right: .5em;
|
padding-right: .5em;
|
||||||
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fieldValue
|
.fieldValue
|
||||||
{
|
{
|
||||||
color: rgb(123, 128, 154);
|
color: rgb(123, 128, 154);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullScreenWidget
|
.fullScreenWidget
|
||||||
|
@ -56,7 +56,7 @@ export default class DataGridUtils
|
|||||||
|
|
||||||
if(!row["id"])
|
if(!row["id"])
|
||||||
{
|
{
|
||||||
row["id"] = row[tableMetaData.primaryKeyField];
|
row["id"] = record.values.get(tableMetaData.primaryKeyField) ?? row[tableMetaData.primaryKeyField];
|
||||||
}
|
}
|
||||||
|
|
||||||
rows.push(row);
|
rows.push(row);
|
||||||
|
Reference in New Issue
Block a user