diff --git a/.gitignore b/.gitignore index 839d6cc..367a0b4 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ yalc.lock # production /build /lib +/target # misc .DS_Store diff --git a/package.json b/package.json index c01eb73..3122141 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "@auth0/auth0-react": "1.10.2", "@emotion/react": "11.7.1", "@emotion/styled": "11.6.0", - "@kingsrook/qqq-frontend-core": "1.0.49", + "@kingsrook/qqq-frontend-core": "1.0.50", "@mui/icons-material": "5.4.1", "@mui/material": "5.11.1", "@mui/styles": "5.11.1", diff --git a/src/qqq/components/audits/AuditBody.tsx b/src/qqq/components/audits/AuditBody.tsx index d306d2b..1856c8d 100644 --- a/src/qqq/components/audits/AuditBody.tsx +++ b/src/qqq/components/audits/AuditBody.tsx @@ -26,6 +26,7 @@ import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QC import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria"; import {QFilterOrderBy} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterOrderBy"; import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter"; +import {QueryJoin} from "@kingsrook/qqq-frontend-core/lib/model/query/QueryJoin"; import Avatar from "@mui/material/Avatar"; import Box from "@mui/material/Box"; import Icon from "@mui/material/Icon/Icon"; @@ -52,12 +53,12 @@ const qController = Client.getInstance(); function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element { - const [initialLoadComplete, setInitialLoadComplete] = useState(false); const [audits, setAudits] = useState([] as QRecord[]); const [total, setTotal] = useState(null as number); const [limit, setLimit] = useState(1000); const [statusString, setStatusString] = useState("Loading audits..."); const [auditsByDate, setAuditsByDate] = useState([] as QRecord[][]); + const [auditDetailMap, setAuditDetailMap] = useState(null as Map) const [sortDirection, setSortDirection] = useState(localStorage.getItem("audit.sortDirection") === "true"); useEffect(() => @@ -72,7 +73,8 @@ function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element new QFilterCriteria("recordId", QCriteriaOperator.EQUALS, [recordId]), ], [ new QFilterOrderBy("timestamp", sortDirection), - new QFilterOrderBy("id", sortDirection) + new QFilterOrderBy("id", sortDirection), + new QFilterOrderBy("auditDetail.id", true) ]); /////////////////////////////// @@ -81,7 +83,7 @@ function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element let audits = [] as QRecord[] try { - audits = await qController.query("audit", filter, limit, 0); + audits = await qController.query("audit", filter, limit, 0, [new QueryJoin("auditDetail", true, "LEFT")]); setAudits(audits); } catch(e) @@ -105,8 +107,37 @@ function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element setTotal(count); } - setInitialLoadComplete(true); + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // group the audits by auditId (e.g., this is a list that joined audit & auditDetail, so un-flatten it) // + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + const unflattenedAudits: QRecord[] = [] + const detailMap: Map = new Map(); + for (let i = 0; i < audits.length; i++) + { + let id = audits[i].values.get("id"); + if(i == 0 || unflattenedAudits[unflattenedAudits.length-1].values.get("id") != id) + { + unflattenedAudits.push(audits[i]); + } + let auditDetail = audits[i].values.get("auditDetail.message"); + if(auditDetail) + { + if(!detailMap.has(id)) + { + detailMap.set(id, []); + } + + detailMap.get(id).push(auditDetail) + } + } + audits = unflattenedAudits; + setAuditDetailMap(detailMap); + console.log(detailMap); + + ////////////////////////////// + // group the audits by date // + ////////////////////////////// const auditsByDate = []; let thisDatesAudits = null as QRecord[]; let lastDate = null; @@ -240,6 +271,16 @@ function AuditBody({tableMetaData, recordId, record}: Props): JSX.Element {audit.values.get("message")} + +
    + { + auditDetailMap.get(audit.values.get("id"))?.map((detail, key) => + { + return (
  • {detail}
  • ); + }) + } +
+
); diff --git a/src/qqq/pages/records/query/RecordQuery.tsx b/src/qqq/pages/records/query/RecordQuery.tsx index 6a2d42c..2780a70 100644 --- a/src/qqq/pages/records/query/RecordQuery.tsx +++ b/src/qqq/pages/records/query/RecordQuery.tsx @@ -83,6 +83,8 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element { const tableName = table.name; const [ searchParams ] = useSearchParams(); + + const [showSuccessfullyDeletedAlert, setShowSuccessfullyDeletedAlert] = useState(searchParams.has("deleteSuccess")); const location = useLocation(); const navigate = useNavigate(); @@ -1154,9 +1156,11 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element "" )} { - (tableLabel && searchParams.get("deleteSuccess")) ? ( - - {}}> + (tableLabel && showSuccessfullyDeletedAlert) ? ( + + { + setShowSuccessfullyDeletedAlert(false); + }}> {`${tableLabel} successfully deleted`} ) : null diff --git a/src/qqq/pages/records/view/RecordView.tsx b/src/qqq/pages/records/view/RecordView.tsx index 8e49828..881d799 100644 --- a/src/qqq/pages/records/view/RecordView.tsx +++ b/src/qqq/pages/records/view/RecordView.tsx @@ -47,7 +47,7 @@ import React, {useContext, useEffect, useReducer, useState} from "react"; import {useLocation, useNavigate, useParams, useSearchParams} from "react-router-dom"; import QContext from "QContext"; import AuditBody from "qqq/components/audits/AuditBody"; -import {QActionsMenuButton, QCancelButton, QDeleteButton, QEditButton, QSaveButton} from "qqq/components/buttons/DefaultButtons"; +import {QActionsMenuButton, QCancelButton, QDeleteButton, QEditButton} from "qqq/components/buttons/DefaultButtons"; import EntityForm from "qqq/components/forms/EntityForm"; import colors from "qqq/components/legacy/colors"; import QRecordSidebar from "qqq/components/misc/RecordSidebar"; @@ -164,12 +164,12 @@ function RecordView({table, launchProcess}: Props): JSX.Element /////////////////////////////////////////////////////////////////////// for (let i = 0; i < hashParts.length; i++) { - const parts = hashParts[i].split("=") + const parts = hashParts[i].split("="); if (parts.length > 1 && parts[0] == "launchProcess") { (async () => { - const processMetaData = await qController.loadProcessMetaData(parts[1]) + const processMetaData = await qController.loadProcessMetaData(parts[1]); setActiveModalProcess(processMetaData); })(); return; @@ -184,7 +184,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element { (async () => { - const childTable = await qController.loadTableMetaData(pathParts[pathParts.length - 1]) + const childTable = await qController.loadTableMetaData(pathParts[pathParts.length - 1]); const childId: any = null; // todo - for editing a child, not just creating one. openEditChildForm(childTable, childId, null, null); // todo - defaults & disableds })(); @@ -198,12 +198,12 @@ function RecordView({table, launchProcess}: Props): JSX.Element //////////////////////////////////////////////////////////////////////////////// for (let i = 0; i < hashParts.length; i++) { - const parts = hashParts[i].split("=") + const parts = hashParts[i].split("="); if (parts.length > 1 && parts[0] == "createChild") { (async () => { - const childTable = await qController.loadTableMetaData(parts[1]) + const childTable = await qController.loadTableMetaData(parts[1]); const childId: any = null; // todo - for editing a child, not just creating one. openEditChildForm(childTable, childId, null, null); // todo - defaults & disableds })(); @@ -292,20 +292,20 @@ function RecordView({table, launchProcess}: Props): JSX.Element { console.error("Error pushing history: " + e); } - } + }; if (e instanceof QException) { if ((e as QException).status === "404") { setNotFoundMessage(`${tableMetaData.label} ${id} could not be found.`); - historyPurge(location.pathname) + historyPurge(location.pathname); return; } else if ((e as QException).status === "403") { setNotFoundMessage(`You do not have permission to view ${tableMetaData.label} records`); - historyPurge(location.pathname) + historyPurge(location.pathname); return; } } @@ -315,7 +315,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element try { - HistoryUtils.push({label: `${tableMetaData?.label}: ${record.recordLabel}`, path: location.pathname, iconName: table.iconName}) + HistoryUtils.push({label: `${tableMetaData?.label}: ${record.recordLabel}`, path: location.pathname, iconName: table.iconName}); } catch(e) { @@ -441,7 +441,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element const handleDelete = (event: { preventDefault: () => void }) => { - event.preventDefault(); + event?.preventDefault(); (async () => { await qController.delete(tableName, id) @@ -509,7 +509,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element { setActionsMenu(null); - navigate("#audit") + navigate("#audit"); }}> checklist Audit @@ -585,7 +585,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element const closeAudit = (event: object, reason: string) => { - if (reason === "backdropClick" || reason === "escapeKeyDown") + if (reason === "backdropClick") // allowing esc here, as it's a non-destructive close || reason === "escapeKeyDown") { return; } @@ -623,7 +623,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element successMessage ? { - setSuccessMessage(null) + setSuccessMessage(null); }}> {successMessage} @@ -638,7 +638,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element - +