diff --git a/src/App.tsx b/src/App.tsx
index ad58422..7eb9507 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -377,23 +377,57 @@ export default function App({authenticationMetaData}: Props)
});
});
- const runRecordScriptProcess = metaData.processes.get("runRecordScript");
- if (runRecordScriptProcess)
+ const materialDashboardInstanceMetaData = metaData.supplementalInstanceMetaData?.get("materialDashboard");
+ if (materialDashboardInstanceMetaData)
{
- const process = runRecordScriptProcess;
- routeList.push({
- name: process.label,
- key: process.name,
- route: `${path}/${process.name}`,
- component: ,
- });
+ const processNamesToAddToAllQueryAndViewScreens = materialDashboardInstanceMetaData.processNamesToAddToAllQueryAndViewScreens;
+ if (processNamesToAddToAllQueryAndViewScreens)
+ {
+ for (let processName of processNamesToAddToAllQueryAndViewScreens)
+ {
+ const process = metaData.processes.get(processName);
+ if (process)
+ {
+ routeList.push({
+ name: process.label,
+ key: process.name,
+ route: `${path}/${process.name}`,
+ component: ,
+ });
- routeList.push({
- name: process.label,
- key: `${app.name}/${process.name}`,
- route: `${path}/:id/${process.name}`,
- component: ,
- });
+ routeList.push({
+ name: process.label,
+ key: `${app.name}/${process.name}`,
+ route: `${path}/:id/${process.name}`,
+ component: ,
+ });
+ }
+ }
+ }
+ }
+ else
+ {
+ ////////////////
+ // deprecated //
+ ////////////////
+ const runRecordScriptProcess = metaData.processes.get("runRecordScript");
+ if (runRecordScriptProcess)
+ {
+ const process = runRecordScriptProcess;
+ routeList.push({
+ name: process.label,
+ key: process.name,
+ route: `${path}/${process.name}`,
+ component: ,
+ });
+
+ routeList.push({
+ name: process.label,
+ key: `${app.name}/${process.name}`,
+ route: `${path}/:id/${process.name}`,
+ component: ,
+ });
+ }
}
const reportsForTable = ProcessUtils.getReportsForTable(metaData, table.name, true);
diff --git a/src/main/java/com/kingsrook/qqq/frontend/materialdashboard/model/metadata/MaterialDashboardInstanceMetaData.java b/src/main/java/com/kingsrook/qqq/frontend/materialdashboard/model/metadata/MaterialDashboardInstanceMetaData.java
new file mode 100644
index 0000000..8c4906d
--- /dev/null
+++ b/src/main/java/com/kingsrook/qqq/frontend/materialdashboard/model/metadata/MaterialDashboardInstanceMetaData.java
@@ -0,0 +1,113 @@
+/*
+ * 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 .
+ */
+
+package com.kingsrook.qqq.frontend.materialdashboard.model.metadata;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
+import com.kingsrook.qqq.backend.core.model.metadata.QSupplementalInstanceMetaData;
+
+
+/*******************************************************************************
+ ** table-level meta-data for this module (handled as QSupplementalTableMetaData)
+ *******************************************************************************/
+public class MaterialDashboardInstanceMetaData implements QSupplementalInstanceMetaData
+{
+ public static final String TYPE = "materialDashboard";
+
+ private List processNamesToAddToAllQueryAndViewScreens = new ArrayList<>();
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Override
+ public String getName()
+ {
+ return (TYPE);
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public static MaterialDashboardInstanceMetaData ofOrWithNew(QInstance qInstance)
+ {
+ MaterialDashboardInstanceMetaData supplementalMetaData = (MaterialDashboardInstanceMetaData) qInstance.getSupplementalMetaData(TYPE);
+ if(supplementalMetaData == null)
+ {
+ supplementalMetaData = new MaterialDashboardInstanceMetaData();
+ qInstance.withSupplementalMetaData(supplementalMetaData);
+ }
+
+ return (supplementalMetaData);
+ }
+
+
+
+ /*******************************************************************************
+ ** Getter for processNamesToAddToAllQueryAndViewScreens
+ *******************************************************************************/
+ public List getProcessNamesToAddToAllQueryAndViewScreens()
+ {
+ return (this.processNamesToAddToAllQueryAndViewScreens);
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public void addProcessNameToAddToAllQueryAndViewScreens(String processNamesToAddToAllQueryAndViewScreens)
+ {
+ if(this.processNamesToAddToAllQueryAndViewScreens == null)
+ {
+ this.processNamesToAddToAllQueryAndViewScreens = new ArrayList<>();
+ }
+ this.processNamesToAddToAllQueryAndViewScreens.add(processNamesToAddToAllQueryAndViewScreens);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for processNamesToAddToAllQueryAndViewScreens
+ *******************************************************************************/
+ public void setProcessNamesToAddToAllQueryAndViewScreens(List processNamesToAddToAllQueryAndViewScreens)
+ {
+ this.processNamesToAddToAllQueryAndViewScreens = processNamesToAddToAllQueryAndViewScreens;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for processNamesToAddToAllQueryAndViewScreens
+ *******************************************************************************/
+ public MaterialDashboardInstanceMetaData withProcessNamesToAddToAllQueryAndViewScreens(List processNamesToAddToAllQueryAndViewScreens)
+ {
+ this.processNamesToAddToAllQueryAndViewScreens = processNamesToAddToAllQueryAndViewScreens;
+ return (this);
+ }
+
+}
diff --git a/src/qqq/components/query/QueryScreenActionMenu.tsx b/src/qqq/components/query/QueryScreenActionMenu.tsx
index 73147a4..af1e5b7 100644
--- a/src/qqq/components/query/QueryScreenActionMenu.tsx
+++ b/src/qqq/components/query/QueryScreenActionMenu.tsx
@@ -29,9 +29,9 @@ import Icon from "@mui/material/Icon";
import ListItemIcon from "@mui/material/ListItemIcon";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
+import {QActionsMenuButton} from "qqq/components/buttons/DefaultButtons";
import React, {useState} from "react";
import {useNavigate} from "react-router-dom";
-import {QActionsMenuButton} from "qqq/components/buttons/DefaultButtons";
interface QueryScreenActionMenuProps
{
@@ -44,40 +44,35 @@ interface QueryScreenActionMenuProps
processClicked: (process: QProcessMetaData) => void;
}
-QueryScreenActionMenu.defaultProps = {
-};
+QueryScreenActionMenu.defaultProps = {};
export default function QueryScreenActionMenu({metaData, tableMetaData, tableProcesses, bulkLoadClicked, bulkEditClicked, bulkDeleteClicked, processClicked}: QueryScreenActionMenuProps): JSX.Element
{
- const [anchorElement, setAnchorElement] = useState(null)
+ const [anchorElement, setAnchorElement] = useState(null);
const navigate = useNavigate();
const openActionsMenu = (event: any) =>
{
setAnchorElement(event.currentTarget);
- }
+ };
const closeActionsMenu = () =>
{
setAnchorElement(null);
- }
-
- const pushDividerIfNeeded = (menuItems: JSX.Element[]) =>
- {
- if (menuItems.length > 0)
- {
- menuItems.push();
- }
};
const runSomething = (handler: () => void) =>
{
closeActionsMenu();
handler();
- }
+ };
const menuItems: JSX.Element[] = [];
+
+ //////////////////////////////////////////////////////
+ // start with bulk actions, if user has permissions //
+ //////////////////////////////////////////////////////
if (tableMetaData.capabilities.has(Capability.TABLE_INSERT) && tableMetaData.insertPermission)
{
menuItems.push();
@@ -91,19 +86,7 @@ export default function QueryScreenActionMenu({metaData, tableMetaData, tablePro
menuItems.push();
}
- const runRecordScriptProcess = metaData?.processes.get("runRecordScript");
- if (runRecordScriptProcess)
- {
- const process = runRecordScriptProcess;
- menuItems.push();
- }
-
- menuItems.push();
-
- if (tableProcesses && tableProcesses.length)
- {
- pushDividerIfNeeded(menuItems);
- }
+ menuItems.push();
tableProcesses.sort((a, b) => a.label.localeCompare(b.label));
tableProcesses.map((process) =>
@@ -111,11 +94,62 @@ export default function QueryScreenActionMenu({metaData, tableMetaData, tablePro
menuItems.push();
});
+ menuItems.push();
+
+ ////////////////////////////////////////////
+ // add processes that apply to all tables //
+ ////////////////////////////////////////////
+ const materialDashboardInstanceMetaData = metaData.supplementalInstanceMetaData?.get("materialDashboard");
+ if (materialDashboardInstanceMetaData)
+ {
+ const processNamesToAddToAllQueryAndViewScreens = materialDashboardInstanceMetaData.processNamesToAddToAllQueryAndViewScreens;
+ if (processNamesToAddToAllQueryAndViewScreens)
+ {
+ for (let processName of processNamesToAddToAllQueryAndViewScreens)
+ {
+ const process = metaData?.processes.get(processName);
+ if (process)
+ {
+ menuItems.push();
+ }
+ }
+ }
+ }
+ else
+ {
+ //////////////////////////////////////
+ // deprecated in favor of the above //
+ //////////////////////////////////////
+ const runRecordScriptProcess = metaData?.processes.get("runRecordScript");
+ if (runRecordScriptProcess)
+ {
+ const process = runRecordScriptProcess;
+ menuItems.push();
+ }
+ }
+
+ ////////////////////////////////////////
+ // todo - any conditions around this? //
+ ////////////////////////////////////////
+ menuItems.push();
+
if (menuItems.length === 0)
{
menuItems.push();
}
+ ////////////////////////////////////////////////////////////////////////////////
+ // remove any duplicated dividers, and any dividers in the first or last slot //
+ ////////////////////////////////////////////////////////////////////////////////
+ for (let i = 0; i < menuItems.length; i++)
+ {
+ if (menuItems[i].type == Divider && (i == 0 || (i > 0 && menuItems[i - 1].type == Divider) || i == menuItems.length - 1))
+ {
+ menuItems.splice(i, 1);
+ i--;
+ }
+ }
+
return (
<>
@@ -130,5 +164,5 @@ export default function QueryScreenActionMenu({metaData, tableMetaData, tablePro
{menuItems}
>
- )
+ );
}
diff --git a/src/qqq/pages/records/view/RecordView.tsx b/src/qqq/pages/records/view/RecordView.tsx
index 3aeec37..f52e9de 100644
--- a/src/qqq/pages/records/view/RecordView.tsx
+++ b/src/qqq/pages/records/view/RecordView.tsx
@@ -440,6 +440,34 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
};
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ function getGenericProcesses(metaData: QInstance)
+ {
+ const genericProcesses: QProcessMetaData[] = [];
+ const materialDashboardInstanceMetaData = metaData?.supplementalInstanceMetaData?.get("materialDashboard");
+ if (materialDashboardInstanceMetaData)
+ {
+ const processNamesToAddToAllQueryAndViewScreens = materialDashboardInstanceMetaData.processNamesToAddToAllQueryAndViewScreens;
+ if (processNamesToAddToAllQueryAndViewScreens)
+ {
+ for (let processName of processNamesToAddToAllQueryAndViewScreens)
+ {
+ genericProcesses.push(metaData?.processes?.get(processName));
+ }
+ }
+ }
+ else
+ {
+ ////////////////
+ // deprecated //
+ ////////////////
+ genericProcesses.push(metaData?.processes.get("runRecordScript"));
+ }
+ return genericProcesses;
+ }
+
if (!asyncLoadInited)
{
setAsyncLoadInited(true);
@@ -472,11 +500,16 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
// load processes that the routing needs to respect //
//////////////////////////////////////////////////////
const allTableProcesses = ProcessUtils.getProcessesForTable(metaData, tableName, true); // these include hidden ones (e.g., to find the bulks)
- const runRecordScriptProcess = metaData?.processes.get("runRecordScript");
- if (runRecordScriptProcess)
+ const genericProcesses = getGenericProcesses(metaData);
+
+ for (let genericProcess of genericProcesses)
{
- allTableProcesses.unshift(runRecordScriptProcess);
+ if (genericProcess)
+ {
+ allTableProcesses.unshift(genericProcess);
+ }
}
+
setAllTableProcesses(allTableProcesses);
if (launchingProcess)
@@ -726,7 +759,6 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
let hasEditOrDelete = (table.capabilities.has(Capability.TABLE_UPDATE) && table.editPermission) || (table.capabilities.has(Capability.TABLE_DELETE) && table.deletePermission);
- const runRecordScriptProcess = metaData?.processes.get("runRecordScript");
const renderActionsMenu = (