, tableVariant: QTableVariant): string | JSX.Element =>
{
+ let label: string = tableMetaData?.label ?? "";
+
if (visibleJoinTables.size > 0)
{
let joinLabels = [];
@@ -462,15 +480,36 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
return(
- {tableMetaData?.label}
+ {label}
emergency
+ {
+ tableVariant &&
+
+ {tableMetaData.variantTableLabel}: {tableVariant.name}
+
+ settings
+
+
+ }
);
}
else
{
- return (tableMetaData?.label);
+ return (
+
+ {label}
+ {
+ tableVariant &&
+
+ {tableMetaData.variantTableLabel}: {tableVariant.name}
+
+ settings
+
+
+ }
+
);
}
};
@@ -481,9 +520,8 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
(async () =>
{
const tableMetaData = await qController.loadTableMetaData(tableName);
-
const visibleJoinTables = getVisibleJoinTables();
- setPageHeader(getPageHeader(tableMetaData, visibleJoinTables));
+ setPageHeader(getPageHeader(tableMetaData, visibleJoinTables, tableVariant));
////////////////////////////////////////////////////////////////////////////////////////////////////////
// if there's an exposedJoin that we haven't seen before, we want to make sure that all of its fields //
@@ -544,6 +582,12 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
setTableMetaData(tableMetaData);
setTableLabel(tableMetaData.label);
+ if(tableMetaData?.usesVariants && ! tableVariant)
+ {
+ promptForTableVariantSelection();
+ return;
+ }
+
if (columnsModel.length == 0)
{
let linkBase = metaData.getTablePath(table);
@@ -634,7 +678,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
if (tableMetaData.capabilities.has(Capability.TABLE_COUNT))
{
let includeDistinct = isJoinMany(tableMetaData, getVisibleJoinTables());
- qController.count(tableName, qFilter, queryJoins, includeDistinct).then(([count, distinctCount]) =>
+ qController.count(tableName, qFilter, queryJoins, includeDistinct, tableVariant).then(([count, distinctCount]) =>
{
console.log(`Received count results for query ${thisQueryId}: ${count} ${distinctCount}`);
countResults[thisQueryId] = [];
@@ -652,7 +696,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
}
setLastFetchedQFilterJSON(JSON.stringify(qFilter));
- qController.query(tableName, qFilter, queryJoins).then((results) =>
+ qController.query(tableName, qFilter, queryJoins, tableVariant).then((results) =>
{
console.log(`Received results for query ${thisQueryId}`);
queryResults[thisQueryId] = results;
@@ -1708,7 +1752,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
menuItems.push();
}
- menuItems.push();
+ menuItems.push();
if (tableProcesses && tableProcesses.length)
{
@@ -1769,7 +1813,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
setTotalRecords(null);
setDistinctRecords(null);
updateTable();
- }, [columnsModel, tableState]);
+ }, [columnsModel, tableState, tableVariant]);
useEffect(() =>
{
@@ -1970,6 +2014,15 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
}
+ {
+ tableMetaData &&
+
+ {
+ setTableVariantPromptOpen(false);
+ setTableVariant(value);
+ }} />
+ }
+
{
columnStatsFieldName &&
closeColumnStats(event, reason)}>
@@ -1992,6 +2045,93 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
);
}
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+// mini-component that is the dialog for the user to select a variant on tables with variant backends //
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+function TableVariantDialog(props: {isOpen: boolean; table: QTableMetaData; closeHandler: (value?: QTableVariant) => void})
+{
+ const [value, setValue] = useState(null)
+ const [dropDownOpen, setDropDownOpen] = useState(false)
+ const [variants, setVariants] = useState(null);
+
+ const handleVariantChange = (event: React.SyntheticEvent, value: any | any[], reason: string, details?: string) =>
+ {
+ const tableVariantLocalStorageKey = `${TABLE_VARIANT_LOCAL_STORAGE_KEY_ROOT}.${props.table.name}`;
+ if(value != null)
+ {
+ localStorage.setItem(tableVariantLocalStorageKey, JSON.stringify(value));
+ }
+ else
+ {
+ localStorage.removeItem(tableVariantLocalStorageKey);
+ }
+ props.closeHandler(value);
+ };
+
+ const keyPressed = (e: React.KeyboardEvent) =>
+ {
+ if(e.key == "Enter" && value)
+ {
+ props.closeHandler(value);
+ }
+ }
+
+ useEffect(() =>
+ {
+ console.log("queryVariants")
+ try
+ {
+ (async () =>
+ {
+ const variants = await qController.tableVariants(props.table.name);
+ console.log(JSON.stringify(variants));
+ setVariants(variants);
+ })();
+ }
+ catch (e)
+ {
+ console.log(e);
+ }
+ }, []);
+
+
+ return variants && (
+
+ )
+}
+
//////////////////////////////////////////////////////////////////////////////////
// mini-component that is the dialog for the user to enter the selection-subset //
//////////////////////////////////////////////////////////////////////////////////
diff --git a/src/qqq/pages/records/view/RecordView.tsx b/src/qqq/pages/records/view/RecordView.tsx
index a978507..2146230 100644
--- a/src/qqq/pages/records/view/RecordView.tsx
+++ b/src/qqq/pages/records/view/RecordView.tsx
@@ -25,9 +25,11 @@ import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstan
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableSection";
+import {QTableVariant} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableVariant";
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
-import {Alert, Box, Typography} from "@mui/material";
+import {Alert, Typography} from "@mui/material";
import Avatar from "@mui/material/Avatar";
+import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Card from "@mui/material/Card";
import Dialog from "@mui/material/Dialog";
@@ -74,36 +76,36 @@ RecordView.defaultProps =
launchProcess: null,
};
+const TABLE_VARIANT_LOCAL_STORAGE_KEY_ROOT = "qqq.tableVariant";
+
function RecordView({table, launchProcess}: Props): JSX.Element
{
const {id} = useParams();
const location = useLocation();
const navigate = useNavigate();
- const {accentColor} = useContext(QContext);
const pathParts = location.pathname.replace(/\/+$/, "").split("/");
const tableName = table.name;
+ let tableVariant: QTableVariant = null;
+ const tableVariantLocalStorageKey = `${TABLE_VARIANT_LOCAL_STORAGE_KEY_ROOT}.${tableName}`;
const [asyncLoadInited, setAsyncLoadInited] = useState(false);
const [sectionFieldElements, setSectionFieldElements] = useState(null as Map);
const [adornmentFieldsMap, setAdornmentFieldsMap] = useState(new Map);
const [deleteConfirmationOpen, setDeleteConfirmationOpen] = useState(false);
- const [tableMetaData, setTableMetaData] = useState(null);
const [metaData, setMetaData] = useState(null as QInstance);
const [record, setRecord] = useState(null as QRecord);
const [tableSections, setTableSections] = useState([] as QTableSection[]);
const [t1SectionName, setT1SectionName] = useState(null as string);
const [t1SectionElement, setT1SectionElement] = useState(null as JSX.Element);
const [nonT1TableSections, setNonT1TableSections] = useState([] as QTableSection[]);
- const [tableProcesses, setTableProcesses] = useState([] as QProcessMetaData[]);
const [allTableProcesses, setAllTableProcesses] = useState([] as QProcessMetaData[]);
const [actionsMenu, setActionsMenu] = useState(null);
const [notFoundMessage, setNotFoundMessage] = useState(null as string);
const [errorMessage, setErrorMessage] = useState(null as string)
const [successMessage, setSuccessMessage] = useState(null as string);
const [warningMessage, setWarningMessage] = useState(null as string);
- const {setPageHeader} = useContext(QContext);
const [activeModalProcess, setActiveModalProcess] = useState(null as QProcessMetaData);
const [reloadCounter, setReloadCounter] = useState(0);
@@ -114,6 +116,13 @@ function RecordView({table, launchProcess}: Props): JSX.Element
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
const closeActionsMenu = () => setActionsMenu(null);
+ const {accentColor, setPageHeader, tableMetaData, setTableMetaData, tableProcesses, setTableProcesses, dotMenuOpen} = useContext(QContext);
+
+ if (localStorage.getItem(tableVariantLocalStorageKey))
+ {
+ tableVariant = JSON.parse(localStorage.getItem(tableVariantLocalStorageKey));
+ }
+
const reload = () =>
{
setSuccessMessage(null);
@@ -129,6 +138,69 @@ function RecordView({table, launchProcess}: Props): JSX.Element
setShowAudit(false);
};
+ // Toggle the menu when ⌘K is pressed
+ useEffect(() =>
+ {
+ if(tableMetaData == null)
+ {
+ (async() =>
+ {
+ const tableMetaData = await qController.loadTableMetaData(tableName);
+ setTableMetaData(tableMetaData);
+ })();
+ }
+
+ const down = (e: { key: string; metaKey: any; ctrlKey: any; preventDefault: () => void; }) =>
+ {
+ if(!dotMenuOpen)
+ {
+ if (e.key === "n" && table.capabilities.has(Capability.TABLE_INSERT) && table.insertPermission)
+ {
+ e.preventDefault()
+ gotoCreate();
+ }
+ else if (e.key === "e" && table.capabilities.has(Capability.TABLE_UPDATE) && table.editPermission)
+ {
+ e.preventDefault()
+ navigate("edit");
+ }
+ else if (e.key === "c" && table.capabilities.has(Capability.TABLE_INSERT) && table.insertPermission)
+ {
+ e.preventDefault()
+ navigate("copy");
+ }
+ else if (e.key === "d" && table.capabilities.has(Capability.TABLE_DELETE) && table.deletePermission)
+ {
+ e.preventDefault()
+ handleClickDeleteButton();
+ }
+ else if (e.key === "a" && metaData && metaData.tables.has("audit"))
+ {
+ e.preventDefault()
+ navigate("#audit");
+ }
+ }
+ }
+
+ document.addEventListener("keydown", down)
+ return () =>
+ {
+ document.removeEventListener("keydown", down)
+ }
+ }, [dotMenuOpen])
+
+ const gotoCreate = () =>
+ {
+ const path = `${pathParts.slice(0, -1).join("/")}/create`;
+ navigate(path);
+ }
+
+ const gotoEdit = () =>
+ {
+ const path = `${pathParts.slice(0, -1).join("/")}/${record.values.get(table.primaryKeyField)}/edit`;
+ navigate(path);
+ }
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// monitor location changes - if we've clicked a link from viewing one record to viewing another, //
// we'll stay in this component, but we'll need to reload all data for the new record. //
@@ -266,10 +338,24 @@ function RecordView({table, launchProcess}: Props): JSX.Element
const metaData = await qController.loadMetaData();
setMetaData(metaData);
ValueUtils.qInstance = metaData;
+
+ ///////////////////////////////////////////////////
+ // load the processes to show in the action menu //
+ ///////////////////////////////////////////////////
const processesForTable = ProcessUtils.getProcessesForTable(metaData, tableName);
processesForTable.sort((a, b) => a.label.localeCompare(b.label));
setTableProcesses(processesForTable);
- setAllTableProcesses(ProcessUtils.getProcessesForTable(metaData, tableName, true)); // these include hidden ones (e.g., to find the bulks)
+
+ //////////////////////////////////////////////////////
+ // 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)
+ {
+ allTableProcesses.unshift(runRecordScriptProcess)
+ }
+ setAllTableProcesses(allTableProcesses);
if (launchingProcess)
{
@@ -283,7 +369,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element
let record: QRecord;
try
{
- record = await qController.get(tableName, id);
+ record = await qController.get(tableName, id, tableVariant);
setRecord(record);
}
catch (e)
@@ -507,11 +593,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element
let hasEditOrDelete = (table.capabilities.has(Capability.TABLE_UPDATE) && table.editPermission) || (table.capabilities.has(Capability.TABLE_DELETE) && table.deletePermission);
- function gotoCreate()
- {
- const path = `${pathParts.slice(0, -1).join("/")}/create`;
- navigate(path);
- }
+ const runRecordScriptProcess = metaData?.processes.get("runRecordScript");
const renderActionsMenu = (