mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-19 22:00:45 +00:00
Merge branch 'integration/sprint-28' into feature/CTLE-214-dot-menu
This commit is contained in:
75
src/qqq/pages/records/FilterPoc.tsx
Normal file
75
src/qqq/pages/records/FilterPoc.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {QController} from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter";
|
||||
import Box from "@mui/material/Box";
|
||||
import {useEffect, useState} from "react";
|
||||
import {CustomFilterPanel} from "qqq/components/query/CustomFilterPanel";
|
||||
import BaseLayout from "qqq/layouts/BaseLayout";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
|
||||
|
||||
interface Props
|
||||
{
|
||||
}
|
||||
|
||||
FilterPoc.defaultProps = {};
|
||||
|
||||
function FilterPoc({}: Props): JSX.Element
|
||||
{
|
||||
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData)
|
||||
const [queryFilter, setQueryFilter] = useState(new QQueryFilter())
|
||||
|
||||
const updateFilter = (newFilter: QQueryFilter) =>
|
||||
{
|
||||
setQueryFilter(JSON.parse(JSON.stringify(newFilter)));
|
||||
}
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
(async () =>
|
||||
{
|
||||
const table = await Client.getInstance().loadTableMetaData("order")
|
||||
setTableMetaData(table);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<BaseLayout>
|
||||
{
|
||||
tableMetaData &&
|
||||
<Box>
|
||||
<Box sx={{background: "white"}} border="1px solid gray">
|
||||
{/* @ts-ignore */}
|
||||
<CustomFilterPanel tableMetaData={tableMetaData} queryFilter={queryFilter} updateFilter={updateFilter} />
|
||||
</Box>
|
||||
<pre style={{fontSize: "12px"}}>
|
||||
{JSON.stringify(queryFilter, null, 3)})
|
||||
</pre>
|
||||
</Box>
|
||||
}
|
||||
</BaseLayout>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterPoc;
|
123
src/qqq/pages/records/IntersectionMatrix.tsx
Normal file
123
src/qqq/pages/records/IntersectionMatrix.tsx
Normal file
File diff suppressed because one or more lines are too long
@ -47,8 +47,6 @@ TableDeveloperView.defaultProps =
|
||||
|
||||
function TableDeveloperView({table}: Props): JSX.Element
|
||||
{
|
||||
const {id} = useParams();
|
||||
|
||||
const {getAccessTokenSilently} = useAuth0();
|
||||
const [accessToken, setAccessToken] = useState(null as string);
|
||||
|
||||
@ -70,6 +68,33 @@ function TableDeveloperView({table}: Props): JSX.Element
|
||||
setAccessToken(accessToken);
|
||||
})();
|
||||
|
||||
const LAST_API_NAME_LS_KEY = "qqq.tableDeveloperView.lastApiName";
|
||||
const LAST_API_VERSION_LS_KEY = "qqq.tableDeveloperView.lastApiVersion";
|
||||
|
||||
const lastSelectedApiName = localStorage.getItem(LAST_API_NAME_LS_KEY);
|
||||
const lastSelectedApiVersion = localStorage.getItem(LAST_API_VERSION_LS_KEY);
|
||||
|
||||
function selectVersionAfterApiIsChanged(versionsJson: any)
|
||||
{
|
||||
if (versionsJson.currentVersion)
|
||||
{
|
||||
setSelectedVersion(versionsJson.currentVersion);
|
||||
localStorage.setItem(LAST_API_VERSION_LS_KEY, versionsJson.currentVersion);
|
||||
}
|
||||
|
||||
if (lastSelectedApiVersion)
|
||||
{
|
||||
for (let i = 0; i < versionsJson.supportedVersions.length; i++)
|
||||
{
|
||||
if (versionsJson.supportedVersions[i] == lastSelectedApiVersion)
|
||||
{
|
||||
setSelectedVersion(lastSelectedApiVersion);
|
||||
localStorage.setItem(LAST_API_VERSION_LS_KEY, lastSelectedApiVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!asyncLoadInited)
|
||||
{
|
||||
setAsyncLoadInited(true);
|
||||
@ -90,11 +115,14 @@ function TableDeveloperView({table}: Props): JSX.Element
|
||||
|
||||
setPageHeader(tableMetaData.label + " Developer Mode");
|
||||
|
||||
///////////////////////////////
|
||||
// fetch apis for this table //
|
||||
///////////////////////////////
|
||||
const apisResponse = await fetch("/apis.json?tableName=" + tableName);
|
||||
const apisJson = await apisResponse.json();
|
||||
console.log(apisJson);
|
||||
|
||||
if(!apisJson["apis"] || apisJson["apis"].length == 0)
|
||||
if (!apisJson["apis"] || apisJson["apis"].length == 0)
|
||||
{
|
||||
setNoApis(true);
|
||||
return;
|
||||
@ -102,18 +130,36 @@ function TableDeveloperView({table}: Props): JSX.Element
|
||||
|
||||
setSupportedApis(apisJson["apis"]);
|
||||
|
||||
const selectedApi = apisJson["apis"][0];
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// either select the 0th api, or, if there was one previously stored in local storage, use it instead //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
let selectedApi = apisJson["apis"][0];
|
||||
if (lastSelectedApiName)
|
||||
{
|
||||
for (let i = 0; i < apisJson["apis"].length; i++)
|
||||
{
|
||||
if (apisJson["apis"][i].name == lastSelectedApiName)
|
||||
{
|
||||
selectedApi = apisJson["apis"][i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
localStorage.setItem(LAST_API_NAME_LS_KEY, selectedApi.name);
|
||||
setSelectedApi(selectedApi);
|
||||
|
||||
////////////////////////////////
|
||||
// fetch versions for ths api //
|
||||
////////////////////////////////
|
||||
const versionsResponse = await fetch(selectedApi["path"] + "versions.json");
|
||||
const versionsJson = await versionsResponse.json();
|
||||
console.log(versionsJson);
|
||||
|
||||
setSupportedVersions(versionsJson.supportedVersions);
|
||||
if (versionsJson.currentVersion)
|
||||
{
|
||||
setSelectedVersion(versionsJson.currentVersion);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// set the selected version, either to current, or to one from local storage, if still valid //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
selectVersionAfterApiIsChanged(versionsJson);
|
||||
})();
|
||||
}
|
||||
|
||||
@ -129,16 +175,15 @@ function TableDeveloperView({table}: Props): JSX.Element
|
||||
{
|
||||
const selectedApi = supportedApis[i];
|
||||
setSelectedApi(selectedApi);
|
||||
localStorage.setItem(LAST_API_NAME_LS_KEY, selectedApi.name);
|
||||
|
||||
const versionsResponse = await fetch(selectedApi["path"] + "versions.json");
|
||||
const versionsJson = await versionsResponse.json();
|
||||
console.log(versionsJson);
|
||||
|
||||
setSupportedVersions(versionsJson.supportedVersions);
|
||||
if (versionsJson.currentVersion)
|
||||
{
|
||||
setSelectedVersion(versionsJson.currentVersion);
|
||||
}
|
||||
|
||||
selectVersionAfterApiIsChanged(versionsJson);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -147,6 +192,7 @@ function TableDeveloperView({table}: Props): JSX.Element
|
||||
const selectVersion = (event: SelectChangeEvent) =>
|
||||
{
|
||||
setSelectedVersion(event.target.value);
|
||||
localStorage.setItem(LAST_API_VERSION_LS_KEY, event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -207,7 +253,7 @@ function TableDeveloperView({table}: Props): JSX.Element
|
||||
persist-auth={true}
|
||||
allow-server-selection={false}
|
||||
allow-spec-file-download={true}
|
||||
sort-endpoints-by="method"
|
||||
sort-endpoints-by="none"
|
||||
schema-description-expanded={true}
|
||||
css-file={"/api/rapi-doc.css"}
|
||||
css-classes={"qqq-rapi-doc"}
|
||||
|
@ -29,9 +29,15 @@ import BaseLayout from "qqq/layouts/BaseLayout";
|
||||
interface Props
|
||||
{
|
||||
table?: QTableMetaData;
|
||||
isDuplicate?: boolean
|
||||
}
|
||||
|
||||
function EntityEdit({table}: Props): JSX.Element
|
||||
EntityEdit.defaultProps = {
|
||||
table: null,
|
||||
isDuplicate: false
|
||||
};
|
||||
|
||||
function EntityEdit({table, isDuplicate}: Props): JSX.Element
|
||||
{
|
||||
const {id} = useParams();
|
||||
|
||||
@ -43,7 +49,7 @@ function EntityEdit({table}: Props): JSX.Element
|
||||
<Box mb={3}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<EntityForm table={table} id={id} />
|
||||
<EntityForm table={table} id={id} isDuplicate={isDuplicate} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
@ -54,8 +60,4 @@ function EntityEdit({table}: Props): JSX.Element
|
||||
);
|
||||
}
|
||||
|
||||
EntityEdit.defaultProps = {
|
||||
table: null,
|
||||
};
|
||||
|
||||
export default EntityEdit;
|
||||
|
@ -59,6 +59,7 @@ import {QActionsMenuButton, QCancelButton, QCreateNewButton, QSaveButton} from "
|
||||
import MenuButton from "qqq/components/buttons/MenuButton";
|
||||
import SavedFilters from "qqq/components/misc/SavedFilters";
|
||||
import {CustomColumnsPanel} from "qqq/components/query/CustomColumnsPanel";
|
||||
import {CustomFilterPanel} from "qqq/components/query/CustomFilterPanel";
|
||||
import CustomWidthTooltip from "qqq/components/tooltips/CustomWidthTooltip";
|
||||
import BaseLayout from "qqq/layouts/BaseLayout";
|
||||
import ProcessRun from "qqq/pages/processes/ProcessRun";
|
||||
@ -173,7 +174,10 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
}
|
||||
|
||||
const [filterModel, setFilterModel] = useState({items: []} as GridFilterModel);
|
||||
const [lastFetchedQFilterJSON, setLastFetchedQFilterJSON] = useState("");
|
||||
const [columnSortModel, setColumnSortModel] = useState(defaultSort);
|
||||
const [queryFilter, setQueryFilter] = useState(new QQueryFilter());
|
||||
|
||||
const [columnVisibilityModel, setColumnVisibilityModel] = useState(defaultVisibility);
|
||||
const [shouldSetAllNewJoinFieldsToHidden, setShouldSetAllNewJoinFieldsToHidden] = useState(!didDefaultVisibilityComeFromLocalStorage)
|
||||
const [visibleJoinTables, setVisibleJoinTables] = useState(new Set<string>());
|
||||
@ -236,7 +240,6 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
const [queryErrors, setQueryErrors] = useState({} as any);
|
||||
const [receivedQueryErrorTimestamp, setReceivedQueryErrorTimestamp] = useState(new Date());
|
||||
|
||||
|
||||
const {setPageHeader} = useContext(QContext);
|
||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||
|
||||
@ -354,6 +357,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
updateColumnVisibilityModel();
|
||||
setColumnsModel([]);
|
||||
setFilterModel({items: []});
|
||||
setQueryFilter(new QQueryFilter());
|
||||
setDefaultFilterLoaded(false);
|
||||
setRows([]);
|
||||
}
|
||||
@ -365,7 +369,8 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const buildQFilter = (tableMetaData: QTableMetaData, filterModel: GridFilterModel, limit?: number) =>
|
||||
{
|
||||
const filter = FilterUtils.buildQFilterFromGridFilter(tableMetaData, filterModel, columnSortModel, limit);
|
||||
let filter = FilterUtils.buildQFilterFromGridFilter(tableMetaData, filterModel, columnSortModel, limit);
|
||||
filter = FilterUtils.convertFilterPossibleValuesToIds(filter);
|
||||
setHasValidFilters(filter.criteria && filter.criteria.length > 0);
|
||||
return (filter);
|
||||
};
|
||||
@ -531,6 +536,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
let models = await FilterUtils.determineFilterAndSortModels(qController, tableMetaData, null, searchParams, filterLocalStorageKey, sortLocalStorageKey);
|
||||
setFilterModel(models.filter);
|
||||
setColumnSortModel(models.sort);
|
||||
setQueryFilter(FilterUtils.buildQFilterFromGridFilter(tableMetaData, models.filter, models.sort, rowsPerPage));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -564,6 +570,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
{
|
||||
columnSortModel.splice(i, 1);
|
||||
setColumnSortModel(columnSortModel);
|
||||
// todo - need to setQueryFilter?
|
||||
resetColumnSortModel = true;
|
||||
i--;
|
||||
}
|
||||
@ -580,6 +587,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
sort: "desc",
|
||||
});
|
||||
setColumnSortModel(columnSortModel);
|
||||
// todo - need to setQueryFilter?
|
||||
resetColumnSortModel = true;
|
||||
}
|
||||
|
||||
@ -636,6 +644,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
});
|
||||
}
|
||||
|
||||
setLastFetchedQFilterJSON(JSON.stringify(qFilter));
|
||||
qController.query(tableName, qFilter, queryJoins).then((results) =>
|
||||
{
|
||||
console.log(`Received results for query ${thisQueryId}`);
|
||||
@ -878,6 +887,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
const newVisibleJoinTables = getVisibleJoinTables();
|
||||
if (JSON.stringify([...newVisibleJoinTables.keys()]) != JSON.stringify([...visibleJoinTables.keys()]))
|
||||
{
|
||||
console.log("calling update table for visible join table change");
|
||||
updateTable();
|
||||
setVisibleJoinTables(newVisibleJoinTables);
|
||||
}
|
||||
@ -889,9 +899,30 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
console.log(columnOrderChangeParams);
|
||||
};
|
||||
|
||||
const handleFilterChange = (filterModel: GridFilterModel) =>
|
||||
const handleFilterChange = (filterModel: GridFilterModel, doSetQueryFilter = true, isChangeFromDataGrid = false) =>
|
||||
{
|
||||
setFilterModel(filterModel);
|
||||
|
||||
if (doSetQueryFilter)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// someone might have already set the query filter, so, only set it if asked to //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
setQueryFilter(FilterUtils.buildQFilterFromGridFilter(tableMetaData, filterModel, columnSortModel, rowsPerPage));
|
||||
}
|
||||
|
||||
if (isChangeFromDataGrid)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// this function is called by our code several times, but also from dataGridPro when its filter model changes. //
|
||||
// in general, we don't want a "partial" criteria to be part of our query filter object (e.g., w/ no values) //
|
||||
// BUT - for one use-case, when the user adds a "filter" (criteria) from column-header "..." menu, then dataGridPro //
|
||||
// puts a partial item in its filter - so - in that case, we do like to get this partial criteria in our QFilter. //
|
||||
// so far, not seeing any negatives to this being here, and it fixes that user experience, so keep this. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
setQueryFilter(FilterUtils.buildQFilterFromGridFilter(tableMetaData, filterModel, columnSortModel, rowsPerPage, true));
|
||||
}
|
||||
|
||||
if (filterLocalStorageKey)
|
||||
{
|
||||
localStorage.setItem(filterLocalStorageKey, JSON.stringify(filterModel));
|
||||
@ -903,6 +934,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
if (gridSort && gridSort.length > 0)
|
||||
{
|
||||
setColumnSortModel(gridSort);
|
||||
setQueryFilter(FilterUtils.buildQFilterFromGridFilter(tableMetaData, filterModel, gridSort, rowsPerPage));
|
||||
localStorage.setItem(sortLocalStorageKey, JSON.stringify(gridSort));
|
||||
}
|
||||
};
|
||||
@ -967,8 +999,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
//////////////////////////////////////
|
||||
// construct the url for the export //
|
||||
//////////////////////////////////////
|
||||
const d = new Date();
|
||||
const dateString = `${d.getFullYear()}-${zp(d.getMonth() + 1)}-${zp(d.getDate())} ${zp(d.getHours())}${zp(d.getMinutes())}`;
|
||||
const dateString = ValueUtils.formatDateTimeForFileName(new Date());
|
||||
const filename = `${tableMetaData.label} Export ${dateString}.${format}`;
|
||||
const url = `/data/${tableMetaData.name}/export/${filename}`;
|
||||
|
||||
@ -1115,6 +1146,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
newPath.pop();
|
||||
navigate(newPath.join("/"));
|
||||
|
||||
console.log("calling update table for close modal");
|
||||
updateTable();
|
||||
};
|
||||
|
||||
@ -1224,6 +1256,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
return (
|
||||
<TablePagination
|
||||
component="div"
|
||||
sx={{minWidth: "450px"}}
|
||||
// note - passing null here makes the 'to' param in the defaultLabelDisplayedRows also be null,
|
||||
// so pass a sentinel value of -1...
|
||||
count={totalRecords === null || totalRecords === undefined ? -1 : totalRecords}
|
||||
@ -1285,17 +1318,39 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
return (qRecord);
|
||||
}
|
||||
|
||||
const getFieldAndTable = (fieldName: string): [QFieldMetaData, QTableMetaData] =>
|
||||
{
|
||||
if(fieldName.indexOf(".") > -1)
|
||||
{
|
||||
const nameParts = fieldName.split(".", 2);
|
||||
for (let i = 0; i < tableMetaData?.exposedJoins?.length; i++)
|
||||
{
|
||||
const join = tableMetaData?.exposedJoins[i];
|
||||
if(join?.joinTable.name == nameParts[0])
|
||||
{
|
||||
return ([join.joinTable.fields.get(nameParts[1]), join.joinTable]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return ([tableMetaData.fields.get(fieldName), tableMetaData]);
|
||||
}
|
||||
|
||||
return (null);
|
||||
}
|
||||
|
||||
const copyColumnValues = async (column: GridColDef) =>
|
||||
{
|
||||
let data = "";
|
||||
let counter = 0;
|
||||
if (latestQueryResults && latestQueryResults.length)
|
||||
{
|
||||
let qFieldMetaData = tableMetaData.fields.get(column.field);
|
||||
let [qFieldMetaData, fieldTable] = getFieldAndTable(column.field);
|
||||
for (let i = 0; i < latestQueryResults.length; i++)
|
||||
{
|
||||
let record = latestQueryResults[i] as QRecord;
|
||||
const value = ValueUtils.getUnadornedValueForDisplay(qFieldMetaData, record.values.get(qFieldMetaData.name), record.displayValues.get(qFieldMetaData.name));
|
||||
const value = ValueUtils.getUnadornedValueForDisplay(qFieldMetaData, record.values.get(column.field), record.displayValues.get(column.field));
|
||||
if (value !== null && value !== undefined && String(value) !== "")
|
||||
{
|
||||
data += value + "\n";
|
||||
@ -1321,24 +1376,9 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
setFilterForColumnStats(buildQFilter(tableMetaData, filterModel));
|
||||
setColumnStatsFieldName(column.field);
|
||||
|
||||
if(column.field.indexOf(".") > -1)
|
||||
{
|
||||
const nameParts = column.field.split(".", 2);
|
||||
for (let i = 0; i < tableMetaData?.exposedJoins?.length; i++)
|
||||
{
|
||||
const join = tableMetaData?.exposedJoins[i];
|
||||
if(join?.joinTable.name == nameParts[0])
|
||||
{
|
||||
setColumnStatsField(join.joinTable.fields.get(nameParts[1]));
|
||||
setColumnStatsFieldTableName(nameParts[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setColumnStatsField(tableMetaData.fields.get(column.field));
|
||||
setColumnStatsFieldTableName(tableMetaData.name);
|
||||
}
|
||||
const [field, fieldTable] = getFieldAndTable(column.field);
|
||||
setColumnStatsField(field);
|
||||
setColumnStatsFieldTableName(fieldTable.name);
|
||||
};
|
||||
|
||||
const CustomColumnMenu = forwardRef<HTMLUListElement, GridColumnMenuProps>(
|
||||
@ -1502,6 +1542,15 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
}
|
||||
};
|
||||
|
||||
const doClearFilter = (event: React.KeyboardEvent<HTMLDivElement>, isYesButton: boolean = false) =>
|
||||
{
|
||||
if (isYesButton|| event.key == "Enter")
|
||||
{
|
||||
setShowClearFiltersWarning(false);
|
||||
handleFilterChange({items: []} as GridFilterModel);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<GridToolbarContainer>
|
||||
<div>
|
||||
@ -1516,30 +1565,18 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
<GridToolbarFilterButton nonce={undefined} />
|
||||
{
|
||||
hasValidFilters && (
|
||||
|
||||
<div id="clearFiltersButton" style={{position: "absolute", left: "84px", top: "6px"}}>
|
||||
<Tooltip title="Clear All Filters">
|
||||
<div id="clearFiltersButton" style={{display: "inline-block", position: "relative", top: "2px", left: "-0.75rem", width: "1rem"}}>
|
||||
<Tooltip title="Clear Filter">
|
||||
<Icon sx={{cursor: "pointer"}} onClick={() => setShowClearFiltersWarning(true)}>clear</Icon>
|
||||
</Tooltip>
|
||||
<Dialog open={showClearFiltersWarning} onClose={() => setShowClearFiltersWarning(false)} onKeyPress={(e) =>
|
||||
{
|
||||
if (e.key == "Enter")
|
||||
{
|
||||
setShowClearFiltersWarning(false)
|
||||
handleFilterChange({items: []} as GridFilterModel);
|
||||
}
|
||||
}}>
|
||||
<Dialog open={showClearFiltersWarning} onClose={() => setShowClearFiltersWarning(false)} onKeyPress={(e) => doClearFilter(e)}>
|
||||
<DialogTitle id="alert-dialog-title">Confirm</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>Are you sure you want to clear all filters?</DialogContentText>
|
||||
<DialogContentText>Are you sure you want to remove all conditions from the current filter?</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<QCancelButton label="No" disabled={false} onClickHandler={() => setShowClearFiltersWarning(false)} />
|
||||
<QSaveButton label="Yes" iconName="check" disabled={false} onClickHandler={() =>
|
||||
{
|
||||
setShowClearFiltersWarning(false);
|
||||
handleFilterChange({items: []} as GridFilterModel);
|
||||
}}/>
|
||||
<QSaveButton label="Yes" iconName="check" disabled={false} onClickHandler={() => doClearFilter(null, true)}/>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</div>
|
||||
@ -1716,7 +1753,22 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
setTotalRecords(null);
|
||||
setDistinctRecords(null);
|
||||
updateTable();
|
||||
}, [columnsModel, tableState, filterModel]);
|
||||
}, [columnsModel, tableState]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const currentQFilter = FilterUtils.buildQFilterFromGridFilter(tableMetaData, filterModel, columnSortModel, rowsPerPage);
|
||||
currentQFilter.skip = pageNumber * rowsPerPage;
|
||||
const currentQFilterJSON = JSON.stringify(currentQFilter);
|
||||
|
||||
if(currentQFilterJSON !== lastFetchedQFilterJSON)
|
||||
{
|
||||
setTotalRecords(null);
|
||||
setDistinctRecords(null);
|
||||
updateTable();
|
||||
}
|
||||
|
||||
}, [filterModel]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -1724,6 +1776,13 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
document.scrollingElement.scrollTop = 0;
|
||||
}, [pageNumber, rowsPerPage]);
|
||||
|
||||
const updateFilterFromFilterPanel = (newFilter: QQueryFilter): void =>
|
||||
{
|
||||
setQueryFilter(newFilter);
|
||||
const gridFilterModel = FilterUtils.buildGridFilterFromQFilter(tableMetaData, queryFilter);
|
||||
handleFilterChange(gridFilterModel, false);
|
||||
};
|
||||
|
||||
if (tableMetaData && !tableMetaData.readPermission)
|
||||
{
|
||||
return (
|
||||
@ -1783,7 +1842,10 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
}
|
||||
<Box display="flex" justifyContent="flex-end" alignItems="flex-start" mb={2}>
|
||||
<Box display="flex" marginRight="auto">
|
||||
<SavedFilters qController={qController} metaData={metaData} tableMetaData={tableMetaData} currentSavedFilter={currentSavedFilter} filterModel={filterModel} columnSortModel={columnSortModel} filterOnChangeCallback={handleSavedFilterChange} />
|
||||
{
|
||||
metaData && metaData.processes.has("querySavedFilter") &&
|
||||
<SavedFilters qController={qController} metaData={metaData} tableMetaData={tableMetaData} currentSavedFilter={currentSavedFilter} filterModel={filterModel} columnSortModel={columnSortModel} filterOnChangeCallback={handleSavedFilterChange} />
|
||||
}
|
||||
</Box>
|
||||
|
||||
<Box display="flex" width="150px">
|
||||
@ -1794,7 +1856,6 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
table.capabilities.has(Capability.TABLE_INSERT) && table.insertPermission &&
|
||||
<QCreateNewButton tablePath={metaData?.getTablePathByName(tableName)} />
|
||||
}
|
||||
|
||||
</Box>
|
||||
<Card>
|
||||
<Box height="100%">
|
||||
@ -1804,18 +1865,34 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
Pagination: CustomPagination,
|
||||
LoadingOverlay: Loading,
|
||||
ColumnMenu: CustomColumnMenu,
|
||||
ColumnsPanel: CustomColumnsPanel
|
||||
ColumnsPanel: CustomColumnsPanel,
|
||||
FilterPanel: CustomFilterPanel
|
||||
}}
|
||||
componentsProps={{
|
||||
columnsPanel:
|
||||
{
|
||||
tableMetaData: tableMetaData,
|
||||
metaData: metaData,
|
||||
initialOpenedGroups: columnChooserOpenGroups,
|
||||
openGroupsChanger: setColumnChooserOpenGroups,
|
||||
initialFilterText: columnChooserFilterText,
|
||||
filterTextChanger: setColumnChooserFilterText
|
||||
},
|
||||
filterPanel:
|
||||
{
|
||||
tableMetaData: tableMetaData,
|
||||
metaData: metaData,
|
||||
queryFilter: queryFilter,
|
||||
updateFilter: updateFilterFromFilterPanel
|
||||
}
|
||||
}}
|
||||
localeText={{
|
||||
toolbarFilters: "Filter", // label on the filters button. we prefer singular (1 filter has many "conditions" in it).
|
||||
toolbarFiltersLabel: "", // setting these 3 to "" turns off the "Show Filters" and "Hide Filters" tooltip (which can get in the way of the actual filters panel)
|
||||
toolbarFiltersTooltipShow: "",
|
||||
toolbarFiltersTooltipHide: "",
|
||||
toolbarFiltersTooltipActive: count => count !== 1 ? `${count} conditions` : `${count} condition`
|
||||
}}
|
||||
pinnedColumns={pinnedColumns}
|
||||
onPinnedColumnsChange={handlePinnedColumnsChange}
|
||||
pagination
|
||||
@ -1837,7 +1914,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
|
||||
density={density}
|
||||
loading={loading}
|
||||
filterModel={filterModel}
|
||||
onFilterModelChange={handleFilterChange}
|
||||
onFilterModelChange={(model) => handleFilterChange(model, true, true)}
|
||||
columnVisibilityModel={columnVisibilityModel}
|
||||
onColumnVisibilityModelChange={handleColumnVisibilityChange}
|
||||
onColumnOrderChange={handleColumnOrderChange}
|
||||
|
@ -571,6 +571,13 @@ function RecordView({table, launchProcess}: Props): JSX.Element
|
||||
Create New
|
||||
</MenuItem>
|
||||
}
|
||||
{
|
||||
table.capabilities.has(Capability.TABLE_INSERT) && table.insertPermission &&
|
||||
<MenuItem onClick={() => navigate("duplicate")}>
|
||||
<ListItemIcon><Icon>copy</Icon></ListItemIcon>
|
||||
Create Duplicate
|
||||
</MenuItem>
|
||||
}
|
||||
{
|
||||
table.capabilities.has(Capability.TABLE_UPDATE) && table.editPermission &&
|
||||
<MenuItem onClick={() => navigate("edit")}>
|
||||
|
Reference in New Issue
Block a user