mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 21:00:45 +00:00
CE-1180 Add new table/key?queryString endpoint, to look up a record by a (unique) key and then view it
This commit is contained in:
@ -49,6 +49,7 @@ import EntityEdit from "qqq/pages/records/edit/RecordEdit";
|
||||
import RecordQuery from "qqq/pages/records/query/RecordQuery";
|
||||
import RecordDeveloperView from "qqq/pages/records/view/RecordDeveloperView";
|
||||
import RecordView from "qqq/pages/records/view/RecordView";
|
||||
import RecordViewByUniqueKey from "qqq/pages/records/view/RecordViewByUniqueKey";
|
||||
import GoogleAnalyticsUtils, {AnalyticsModel} from "qqq/utils/GoogleAnalyticsUtils";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
import ProcessUtils from "qqq/utils/qqq/ProcessUtils";
|
||||
@ -392,6 +393,13 @@ export default function App()
|
||||
component: <RecordView table={table} />,
|
||||
});
|
||||
|
||||
routeList.push({
|
||||
name: `${app.label} View`,
|
||||
key: `${app.name}.view`,
|
||||
route: `${path}/key`,
|
||||
component: <RecordViewByUniqueKey table={table} />,
|
||||
});
|
||||
|
||||
routeList.push({
|
||||
name: `${app.label}`,
|
||||
key: `${app.name}.edit`,
|
||||
|
@ -74,12 +74,14 @@ const qController = Client.getInstance();
|
||||
interface Props
|
||||
{
|
||||
table?: QTableMetaData;
|
||||
record?: QRecord;
|
||||
launchProcess?: QProcessMetaData;
|
||||
}
|
||||
|
||||
RecordView.defaultProps =
|
||||
{
|
||||
table: null,
|
||||
record: null,
|
||||
launchProcess: null,
|
||||
};
|
||||
|
||||
@ -130,7 +132,7 @@ export function renderSectionOfFields(key: string, fieldNames: string[], tableMe
|
||||
/*******************************************************************************
|
||||
** Record View Screen component.
|
||||
*******************************************************************************/
|
||||
function RecordView({table, launchProcess}: Props): JSX.Element
|
||||
function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.Element
|
||||
{
|
||||
const {id} = useParams();
|
||||
|
||||
@ -147,7 +149,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element
|
||||
const [adornmentFieldsMap, setAdornmentFieldsMap] = useState(new Map<string, boolean>);
|
||||
const [deleteConfirmationOpen, setDeleteConfirmationOpen] = useState(false);
|
||||
const [metaData, setMetaData] = useState(null as QInstance);
|
||||
const [record, setRecord] = useState(null as QRecord);
|
||||
const [record, setRecord] = useState(overrideRecord ?? null as QRecord);
|
||||
const [tableSections, setTableSections] = useState([] as QTableSection[]);
|
||||
const [t1Section, setT1Section] = useState(null as QTableSection);
|
||||
const [t1SectionName, setT1SectionName] = useState(null as string);
|
||||
@ -481,7 +483,18 @@ function RecordView({table, launchProcess}: Props): JSX.Element
|
||||
let record: QRecord;
|
||||
try
|
||||
{
|
||||
record = await qController.get(tableName, id, tableVariant, null, queryJoins);
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// if the component took in a record object, then we don't need to GET it //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
if(overrideRecord)
|
||||
{
|
||||
record = overrideRecord;
|
||||
}
|
||||
else
|
||||
{
|
||||
record = await qController.get(tableName, id, tableVariant, null, queryJoins);
|
||||
}
|
||||
|
||||
setRecord(record);
|
||||
recordAnalytics({category: "tableEvents", action: "view", label: tableMetaData?.label + " / " + record?.recordLabel});
|
||||
}
|
||||
|
154
src/qqq/pages/records/view/RecordViewByUniqueKey.tsx
Normal file
154
src/qqq/pages/records/view/RecordViewByUniqueKey.tsx
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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 {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||
import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator";
|
||||
import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria";
|
||||
import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter";
|
||||
import {Alert, Box} from "@mui/material";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import BaseLayout from "qqq/layouts/BaseLayout";
|
||||
import RecordView from "qqq/pages/records/view/RecordView";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useSearchParams} from "react-router-dom";
|
||||
|
||||
interface RecordViewByUniqueKeyProps
|
||||
{
|
||||
table: QTableMetaData;
|
||||
}
|
||||
|
||||
RecordViewByUniqueKey.defaultProps = {};
|
||||
|
||||
const qController = Client.getInstance();
|
||||
|
||||
/***************************************************************************
|
||||
** Wrapper around RecordView, that reads a unique key from the query string,
|
||||
** looks for a record matching that key, and shows that record.
|
||||
***************************************************************************/
|
||||
export default function RecordViewByUniqueKey({table}: RecordViewByUniqueKeyProps): JSX.Element
|
||||
{
|
||||
const tableName = table.name;
|
||||
|
||||
const [asyncLoadInited, setAsyncLoadInited] = useState(false);
|
||||
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData);
|
||||
const [doneLoading, setDoneLoading] = useState(false);
|
||||
const [record, setRecord] = useState(null as QRecord);
|
||||
const [errorMessage, setErrorMessage] = useState(null as string);
|
||||
|
||||
const [queryParams] = useSearchParams();
|
||||
|
||||
if (!asyncLoadInited)
|
||||
{
|
||||
setAsyncLoadInited(true);
|
||||
|
||||
(async () =>
|
||||
{
|
||||
const tableMetaData = await qController.loadTableMetaData(tableName);
|
||||
setTableMetaData(tableMetaData);
|
||||
|
||||
const criteria: QFilterCriteria[] = [];
|
||||
for (let [name, value] of queryParams.entries())
|
||||
{
|
||||
criteria.push(new QFilterCriteria(name, QCriteriaOperator.EQUALS, [value]));
|
||||
if(!tableMetaData.fields.has(name))
|
||||
{
|
||||
setErrorMessage(`Query-string parameter [${name}] is not a defined field on the ${tableMetaData.label} table.`);
|
||||
setDoneLoading(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const filter = new QQueryFilter(criteria, null, null, "AND", 0, 2);
|
||||
qController.query(tableName, filter)
|
||||
.then((queryResult) =>
|
||||
{
|
||||
setDoneLoading(true);
|
||||
if (queryResult.length == 1)
|
||||
{
|
||||
setRecord(queryResult[0]);
|
||||
}
|
||||
else if (queryResult.length == 0)
|
||||
{
|
||||
setErrorMessage(`No ${tableMetaData.label} record was found matching the given values.`);
|
||||
}
|
||||
else if (queryResult.length > 1)
|
||||
{
|
||||
setErrorMessage(`More than one ${tableMetaData.label} record was found matching the given values.`);
|
||||
}
|
||||
})
|
||||
.catch((error) =>
|
||||
{
|
||||
setDoneLoading(true);
|
||||
console.log(error);
|
||||
if (error && error.message)
|
||||
{
|
||||
setErrorMessage(error.message);
|
||||
}
|
||||
else if (error && error.response && error.response.data && error.response.data.error)
|
||||
{
|
||||
setErrorMessage(error.response.data.error);
|
||||
}
|
||||
else
|
||||
{
|
||||
setErrorMessage("Unexpected error running query");
|
||||
}
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (asyncLoadInited)
|
||||
{
|
||||
setAsyncLoadInited(false);
|
||||
setDoneLoading(false);
|
||||
setRecord(null);
|
||||
}
|
||||
}, [queryParams]);
|
||||
|
||||
if (!doneLoading)
|
||||
{
|
||||
return (<div>Loading...</div>);
|
||||
}
|
||||
else if (record)
|
||||
{
|
||||
return (<RecordView table={table} record={record} />);
|
||||
}
|
||||
else if (errorMessage)
|
||||
{
|
||||
return (<BaseLayout>
|
||||
<Box className="recordView">
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Box mb={3}>
|
||||
{
|
||||
<Alert color="error" sx={{mb: 3}}>{errorMessage}</Alert>
|
||||
}
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</BaseLayout>);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user