Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3276164bc |
No files matched your search
+4
-1
@@ -58,7 +58,7 @@ import GoogleAnalyticsUtils, {AnalyticsModel} from "qqq/utils/GoogleAnalyticsUti
|
||||
import {detectBasePath, resolveAssetUrl} from "qqq/utils/PathUtils";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
import ProcessUtils from "qqq/utils/qqq/ProcessUtils";
|
||||
import React, {JSXElementConstructor, Key, ReactElement, useEffect, useState,} from "react";
|
||||
import React, {JSXElementConstructor, Key, ReactElement, ReactNode, useEffect, useState,} from "react";
|
||||
import {useCookies} from "react-cookie";
|
||||
import {Navigate, Route, Routes, useLocation, useParams, useSearchParams,} from "react-router-dom";
|
||||
import {Md5} from "ts-md5/dist/md5";
|
||||
@@ -650,6 +650,7 @@ export default function App({authenticationMetaData}: Props)
|
||||
);
|
||||
|
||||
const [pageHeader, setPageHeader] = useState("" as string | JSX.Element);
|
||||
const [pageHeaderRightContent, setPageHeaderRightContent] = useState(null as ReactNode);
|
||||
const [accentColor, setAccentColor] = useState("#0062FF");
|
||||
const [accentColorLight, setAccentColorLight] = useState("#C0D6F7");
|
||||
const [tableMetaData, setTableMetaData] = useState(null);
|
||||
@@ -760,6 +761,7 @@ export default function App({authenticationMetaData}: Props)
|
||||
appRoutes && (
|
||||
<QContext.Provider value={{
|
||||
pageHeader: pageHeader,
|
||||
pageHeaderRightContent: pageHeaderRightContent,
|
||||
accentColor: accentColor,
|
||||
accentColorLight: accentColorLight,
|
||||
tableMetaData: tableMetaData,
|
||||
@@ -770,6 +772,7 @@ export default function App({authenticationMetaData}: Props)
|
||||
helpHelpActive: helpHelpActive,
|
||||
userId: userId,
|
||||
setPageHeader: (header: string | JSX.Element) => setPageHeader(header),
|
||||
setPageHeaderRightContent: (content: ReactNode) => setPageHeaderRightContent(content),
|
||||
setAccentColor: (accentColor: string) => setAccentColor(accentColor),
|
||||
setAccentColorLight: (accentColorLight: string) => setAccentColorLight(accentColorLight),
|
||||
setTableMetaData: (tableMetaData: QTableMetaData) => setTableMetaData(tableMetaData),
|
||||
|
||||
+4
-1
@@ -23,12 +23,14 @@ import {QBrandingMetaData} from "@qrunio/qqq-frontend-core/lib/model/metaData/QB
|
||||
import {QProcessMetaData} from "@qrunio/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
||||
import {QTableMetaData} from "@qrunio/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {AnalyticsModel} from "qqq/utils/GoogleAnalyticsUtils";
|
||||
import {createContext} from "react";
|
||||
import {createContext, ReactNode} from "react";
|
||||
|
||||
interface QContext
|
||||
{
|
||||
pageHeader: string | JSX.Element;
|
||||
setPageHeader?: (header: string | JSX.Element) => void;
|
||||
pageHeaderRightContent?: ReactNode;
|
||||
setPageHeaderRightContent?: (content: ReactNode) => void;
|
||||
|
||||
accentColor: string;
|
||||
setAccentColor?: (color: string) => void;
|
||||
@@ -69,6 +71,7 @@ interface QContext
|
||||
|
||||
const defaultState = {
|
||||
pageHeader: "",
|
||||
pageHeaderRightContent: null as ReactNode,
|
||||
accentColor: "#0062FF",
|
||||
accentColorLight: "#C0D6F7",
|
||||
dotMenuOpen: false,
|
||||
|
||||
+33
@@ -39,6 +39,8 @@ public class MaterialDashboardInstanceMetaData implements QSupplementalInstanceM
|
||||
|
||||
private Integer queryScreenCopyFullQueryColumnValuesLimit = 100_000;
|
||||
|
||||
private MaterialDashboardTableMetaData.RecordViewActionsPlacement recordViewActionsPlacement = MaterialDashboardTableMetaData.RecordViewActionsPlacement.IN_IDENTITY_SECTION;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -155,4 +157,35 @@ public class MaterialDashboardInstanceMetaData implements QSupplementalInstanceM
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for recordViewActionsPlacement
|
||||
*******************************************************************************/
|
||||
public MaterialDashboardTableMetaData.RecordViewActionsPlacement getRecordViewActionsPlacement()
|
||||
{
|
||||
return (this.recordViewActionsPlacement);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for recordViewActionsPlacement
|
||||
*******************************************************************************/
|
||||
public void setRecordViewActionsPlacement(MaterialDashboardTableMetaData.RecordViewActionsPlacement recordViewActionsPlacement)
|
||||
{
|
||||
this.recordViewActionsPlacement = recordViewActionsPlacement;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for recordViewActionsPlacement
|
||||
*******************************************************************************/
|
||||
public MaterialDashboardInstanceMetaData withRecordViewActionsPlacement(MaterialDashboardTableMetaData.RecordViewActionsPlacement recordViewActionsPlacement)
|
||||
{
|
||||
this.recordViewActionsPlacement = recordViewActionsPlacement;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+73
@@ -45,6 +45,12 @@ import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
*******************************************************************************/
|
||||
public class MaterialDashboardTableMetaData extends QSupplementalTableMetaData
|
||||
{
|
||||
public enum RecordViewActionsPlacement
|
||||
{
|
||||
IN_IDENTITY_SECTION,
|
||||
INLINE_WITH_PAGE_TITLE
|
||||
}
|
||||
|
||||
private static final QLogger LOG = QLogger.getLogger(MaterialDashboardTableMetaData.class);
|
||||
|
||||
public static final String TYPE = "materialDashboard";
|
||||
@@ -52,6 +58,8 @@ public class MaterialDashboardTableMetaData extends QSupplementalTableMetaData
|
||||
private List<List<String>> gotoFieldNames;
|
||||
private List<String> defaultQuickFilterFieldNames;
|
||||
private List<FieldRule> fieldRules;
|
||||
private Boolean showRecordSidebar = true;
|
||||
private RecordViewActionsPlacement recordViewActionsPlacement = RecordViewActionsPlacement.IN_IDENTITY_SECTION;
|
||||
|
||||
private QCodeReference onLoadFormAdjuster = null;
|
||||
|
||||
@@ -262,6 +270,68 @@ public class MaterialDashboardTableMetaData extends QSupplementalTableMetaData
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for showRecordSidebar
|
||||
*******************************************************************************/
|
||||
public Boolean getShowRecordSidebar()
|
||||
{
|
||||
return (this.showRecordSidebar);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for showRecordSidebar
|
||||
*******************************************************************************/
|
||||
public void setShowRecordSidebar(Boolean showRecordSidebar)
|
||||
{
|
||||
this.showRecordSidebar = showRecordSidebar;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for showRecordSidebar
|
||||
*******************************************************************************/
|
||||
public MaterialDashboardTableMetaData withShowRecordSidebar(Boolean showRecordSidebar)
|
||||
{
|
||||
this.showRecordSidebar = showRecordSidebar;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for recordViewActionsPlacement
|
||||
*******************************************************************************/
|
||||
public RecordViewActionsPlacement getRecordViewActionsPlacement()
|
||||
{
|
||||
return (this.recordViewActionsPlacement);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for recordViewActionsPlacement
|
||||
*******************************************************************************/
|
||||
public void setRecordViewActionsPlacement(RecordViewActionsPlacement recordViewActionsPlacement)
|
||||
{
|
||||
this.recordViewActionsPlacement = recordViewActionsPlacement;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for recordViewActionsPlacement
|
||||
*******************************************************************************/
|
||||
public MaterialDashboardTableMetaData withRecordViewActionsPlacement(RecordViewActionsPlacement recordViewActionsPlacement)
|
||||
{
|
||||
this.recordViewActionsPlacement = recordViewActionsPlacement;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fieldRules
|
||||
*******************************************************************************/
|
||||
@@ -384,6 +454,9 @@ public class MaterialDashboardTableMetaData extends QSupplementalTableMetaData
|
||||
clone.onLoadFormAdjuster = onLoadFormAdjuster.clone();
|
||||
}
|
||||
|
||||
clone.showRecordSidebar = showRecordSidebar;
|
||||
clone.recordViewActionsPlacement = recordViewActionsPlacement;
|
||||
|
||||
return abstractClone;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ function NavBar({absolute, light, isMini}: Props): JSX.Element
|
||||
const route = useLocation().pathname.split("/").slice(1);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {pageHeader, setDotMenuOpen} = useContext(QContext);
|
||||
const {pageHeader, pageHeaderRightContent, setDotMenuOpen} = useContext(QContext);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@@ -269,10 +269,11 @@ function NavBar({absolute, light, isMini}: Props): JSX.Element
|
||||
</Toolbar>
|
||||
{
|
||||
pageHeader &&
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center" flexWrap={{xs: "wrap", md: "nowrap"}} columnGap={2} rowGap={1}>
|
||||
<MDTypography pb="0.5rem" variant="h3" color={light ? "white" : "dark"}>
|
||||
{pageHeader}
|
||||
</MDTypography>
|
||||
{pageHeaderRightContent && <Box display="flex">{pageHeaderRightContent}</Box>}
|
||||
</Box>
|
||||
}
|
||||
</AppBar>
|
||||
|
||||
@@ -70,7 +70,7 @@ import Client from "qqq/utils/qqq/Client";
|
||||
import ProcessUtils from "qqq/utils/qqq/ProcessUtils";
|
||||
import TableUtils from "qqq/utils/qqq/TableUtils";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
import React, {useContext, useEffect, useState} from "react";
|
||||
import React, {useContext, useEffect, useMemo, useState} from "react";
|
||||
import {useLocation, useNavigate, useParams} from "react-router-dom";
|
||||
|
||||
const qController = Client.getInstance();
|
||||
@@ -121,13 +121,13 @@ export function renderSectionOfFields(key: string, fieldNames: string[], tableMe
|
||||
{
|
||||
return <Grid container lg={12} key={key} display="flex" py={1} pr={2}>
|
||||
{
|
||||
fieldNames.map((fieldName: string) =>
|
||||
fieldNames.map((fieldName: string) =>
|
||||
{
|
||||
let [field, tableForField] = tableMetaData ? TableUtils.getFieldAndTable(tableMetaData, fieldName) : fieldMap ? [fieldMap[fieldName], null] : [null, null];
|
||||
|
||||
if(field == null)
|
||||
if (field == null)
|
||||
{
|
||||
if(tableMetaData?.virtualFields?.has(fieldName))
|
||||
if (tableMetaData?.virtualFields?.has(fieldName))
|
||||
{
|
||||
field = tableMetaData.virtualFields.get(fieldName);
|
||||
}
|
||||
@@ -144,7 +144,7 @@ export function renderSectionOfFields(key: string, fieldNames: string[], tableMe
|
||||
|
||||
const labelElement = <Typography variant="button" textTransform="none" fontWeight="bold" pr={1} color="rgb(52, 71, 103)" sx={{cursor: "default", ...(styleOverrides?.label ?? {})}}>{label}:</Typography>;
|
||||
|
||||
if(field.hasAdornment(AdornmentType.WIDGET))
|
||||
if (field.hasAdornment(AdornmentType.WIDGET))
|
||||
{
|
||||
return (<Grid item key={fieldName} lg={gridColumns} flexDirection="column" pr={2}>
|
||||
<FieldValueAsWidget field={field} record={record} />
|
||||
@@ -186,7 +186,7 @@ export function getVisibleJoinTables(tableMetaData: QTableMetaData): Set<string>
|
||||
continue;
|
||||
}
|
||||
|
||||
section.fieldNames.forEach((fieldName) =>
|
||||
section.fieldNames.forEach((fieldName) =>
|
||||
{
|
||||
const [field, tableForField] = TableUtils.getFieldAndTable(tableMetaData, fieldName);
|
||||
if (tableForField && tableForField.name != tableMetaData.name)
|
||||
@@ -248,7 +248,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
const openActionsMenu = (event: any) => setActionsMenuAnchorElement(event.currentTarget);
|
||||
const closeActionsMenu = () => setActionsMenuAnchorElement(null);
|
||||
|
||||
const {accentColor, setPageHeader, tableMetaData, setTableMetaData, tableProcesses, setTableProcesses, dotMenuOpen, keyboardHelpOpen, modalStack, helpHelpActive, recordAnalytics, userId: currentUserId} = useContext(QContext);
|
||||
const {accentColor, setPageHeader, setPageHeaderRightContent, tableMetaData, setTableMetaData, tableProcesses, setTableProcesses, dotMenuOpen, keyboardHelpOpen, modalStack, helpHelpActive, recordAnalytics, userId: currentUserId} = useContext(QContext);
|
||||
|
||||
const CREATE_CHILD_KEY = "createChild";
|
||||
|
||||
@@ -257,7 +257,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
tableVariant = JSON.parse(localStorage.getItem(tableVariantLocalStorageKey));
|
||||
}
|
||||
|
||||
const reload = () =>
|
||||
const reload = () =>
|
||||
{
|
||||
setSuccessMessage(null);
|
||||
setNotFoundMessage(null);
|
||||
@@ -275,18 +275,18 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
///////////////////////
|
||||
// Keyboard handling //
|
||||
///////////////////////
|
||||
useEffect(() =>
|
||||
useEffect(() =>
|
||||
{
|
||||
if (tableMetaData == null)
|
||||
{
|
||||
(async () =>
|
||||
(async () =>
|
||||
{
|
||||
const tableMetaData = await qController.loadTableMetaData(tableName);
|
||||
setTableMetaData(tableMetaData);
|
||||
})();
|
||||
}
|
||||
|
||||
const down = (e: KeyboardEvent) =>
|
||||
const down = (e: KeyboardEvent) =>
|
||||
{
|
||||
const type = (e.target as any).type;
|
||||
const validType = (type !== "text" && type !== "textarea" && type !== "input" && type !== "search");
|
||||
@@ -322,19 +322,19 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", down);
|
||||
return () =>
|
||||
return () =>
|
||||
{
|
||||
document.removeEventListener("keydown", down);
|
||||
};
|
||||
}, [dotMenuOpen, keyboardHelpOpen, modalStack, showEditChildForm, showAudit, metaData, location]);
|
||||
|
||||
const gotoCreate = () =>
|
||||
const gotoCreate = () =>
|
||||
{
|
||||
const path = `${pathParts.slice(0, -1).join("/")}/create`;
|
||||
navigate(path);
|
||||
};
|
||||
|
||||
const gotoEdit = () =>
|
||||
const gotoEdit = () =>
|
||||
{
|
||||
const path = `${pathParts.slice(0, -1).join("/")}/${record.values.get(table.primaryKeyField)}/edit`;
|
||||
navigate(path);
|
||||
@@ -345,7 +345,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
// we'll stay in this component, but we'll need to reload all data for the new record. //
|
||||
// if, however, our url looks like a process, then open that process. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
useEffect(() =>
|
||||
useEffect(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -390,7 +390,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
const parts = hashParts[i].split("=");
|
||||
if (parts.length > 1 && parts[0] == "launchProcess")
|
||||
{
|
||||
(async () =>
|
||||
(async () =>
|
||||
{
|
||||
const processMetaData = await qController.loadProcessMetaData(parts[1]);
|
||||
setActiveModalProcess(processMetaData);
|
||||
@@ -405,7 +405,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if (pathParts[pathParts.length - 4] === tableName && pathParts[pathParts.length - 2] == CREATE_CHILD_KEY)
|
||||
{
|
||||
(async () =>
|
||||
(async () =>
|
||||
{
|
||||
const childTable = await qController.loadTableMetaData(pathParts[pathParts.length - 1]);
|
||||
const childId: any = null; // todo - for editing a child, not just creating one.
|
||||
@@ -424,7 +424,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
const parts = hashParts[i].split("=");
|
||||
if (parts.length > 1 && parts[0] == CREATE_CHILD_KEY)
|
||||
{
|
||||
(async () =>
|
||||
(async () =>
|
||||
{
|
||||
const childTable = await qController.loadTableMetaData(parts[1]);
|
||||
const childId: any = null; // todo - for editing a child, not just creating one.
|
||||
@@ -512,7 +512,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// when the tableMetaData changes, grab the action menu out of it (or build a default one) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
useEffect(() =>
|
||||
useEffect(() =>
|
||||
{
|
||||
let actionMenu: QMenu = null;
|
||||
if (metaData && tableMetaData)
|
||||
@@ -531,7 +531,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
/*******************************************************************************
|
||||
** get an element (or empty) to use as help content for a section
|
||||
*******************************************************************************/
|
||||
const getSectionHelp = (section: QTableSection) =>
|
||||
const getSectionHelp = (section: QTableSection) =>
|
||||
{
|
||||
const helpRoles = ["VIEW_SCREEN", "READ_SCREENS", "ALL_SCREENS"];
|
||||
const formattedHelpContent = <HelpContent helpContents={section.helpContents} roles={helpRoles} helpContentKey={`table:${tableName};section:${section.name}`} />;
|
||||
@@ -591,7 +591,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
{
|
||||
setAsyncLoadInited(true);
|
||||
|
||||
(async () =>
|
||||
(async () =>
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// load the full table meta-data (the one we took in is a partial) //
|
||||
@@ -667,7 +667,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
const historyPurge = (path: string) =>
|
||||
const historyPurge = (path: string) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -726,7 +726,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
{
|
||||
let section = tableSections[i];
|
||||
|
||||
if(section.alternatives?.has("RECORD_VIEW"))
|
||||
if (section.alternatives?.has("RECORD_VIEW"))
|
||||
{
|
||||
section = section.alternatives.get("RECORD_VIEW")
|
||||
}
|
||||
@@ -739,13 +739,13 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if a section has collapsible metaData that says it IS collapsible, then figure out what the initial open state should be //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(section.collapsible && section.collapsible.isCollapsible)
|
||||
if (section.collapsible && section.collapsible.isCollapsible)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// try to read a previous value from local storage. if it doesn't exist, use the value in the meta-data //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const lsValue = localStorage.getItem(makeCollapsibleSectionOpenStateLocalStorageKey(tableMetaData.name, section.name));
|
||||
if(lsValue != undefined)
|
||||
if (lsValue != undefined)
|
||||
{
|
||||
initialCollapsibleOpenStates[section.name] = (lsValue == "true");
|
||||
}
|
||||
@@ -829,33 +829,33 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
})();
|
||||
}
|
||||
|
||||
const handleClickDeleteButton = () =>
|
||||
const handleClickDeleteButton = () =>
|
||||
{
|
||||
setDeleteConfirmationOpen(true);
|
||||
setIsDeleteSubmitting(false);
|
||||
};
|
||||
|
||||
const handleDeleteConfirmClose = () =>
|
||||
const handleDeleteConfirmClose = () =>
|
||||
{
|
||||
setDeleteConfirmationOpen(false);
|
||||
};
|
||||
|
||||
const handleDelete = (event: { preventDefault: () => void }) =>
|
||||
const handleDelete = (event: { preventDefault: () => void }) =>
|
||||
{
|
||||
setIsDeleteSubmitting(true);
|
||||
event?.preventDefault();
|
||||
(async () =>
|
||||
(async () =>
|
||||
{
|
||||
recordAnalytics({category: "tableEvents", action: "delete", label: tableMetaData?.label + " / " + record?.recordLabel});
|
||||
|
||||
await qController.delete(tableName, id)
|
||||
.then(() =>
|
||||
.then(() =>
|
||||
{
|
||||
setIsDeleteSubmitting(false);
|
||||
const path = pathParts.slice(0, -1).join("/");
|
||||
navigate(path, {state: {deleteSuccess: true}});
|
||||
})
|
||||
.catch((error) =>
|
||||
.catch((error) =>
|
||||
{
|
||||
setIsDeleteSubmitting(false);
|
||||
setDeleteConfirmationOpen(false);
|
||||
@@ -890,18 +890,18 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
new: () => gotoCreate(),
|
||||
copy: () => navigate("copy"),
|
||||
edit: () => navigate("edit"),
|
||||
delete: (closeMenu?: () => void) =>
|
||||
delete: (closeMenu?: () => void) =>
|
||||
{
|
||||
closeMenu?.();
|
||||
handleClickDeleteButton();
|
||||
},
|
||||
developerMode: () => navigate("dev"),
|
||||
audit: (closeMenu?: () => void) =>
|
||||
audit: (closeMenu?: () => void) =>
|
||||
{
|
||||
closeMenu?.();
|
||||
navigate("#audit");
|
||||
},
|
||||
runProcess: (processName: string) =>
|
||||
runProcess: (processName: string) =>
|
||||
{
|
||||
const process = metaData?.processes?.get(processName);
|
||||
if (process)
|
||||
@@ -913,7 +913,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
console.log("No process found for name: [" + processName + "]");
|
||||
}
|
||||
},
|
||||
downloadFileFromField: (fieldName: string, closeMenu?: () => void) =>
|
||||
downloadFileFromField: (fieldName: string, closeMenu?: () => void) =>
|
||||
{
|
||||
////////////////////////////////////////////////////////////
|
||||
// todo can or should this share more with BlobComponent? //
|
||||
@@ -937,15 +937,15 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
|
||||
closeMenu?.();
|
||||
},
|
||||
getMetaData: () =>
|
||||
getMetaData: () =>
|
||||
{
|
||||
return metaData;
|
||||
},
|
||||
getTableProcesses: () =>
|
||||
getTableProcesses: () =>
|
||||
{
|
||||
return tableProcesses;
|
||||
},
|
||||
getGenericProcesses: () =>
|
||||
getGenericProcesses: () =>
|
||||
{
|
||||
return getGenericProcesses(metaData);
|
||||
}
|
||||
@@ -961,7 +961,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
/*******************************************************************************
|
||||
** function to open the sharing modal
|
||||
*******************************************************************************/
|
||||
const openShareModal = () =>
|
||||
const openShareModal = () =>
|
||||
{
|
||||
setShowShareModal(true);
|
||||
};
|
||||
@@ -970,7 +970,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
/*******************************************************************************
|
||||
** function to close the sharing modal
|
||||
*******************************************************************************/
|
||||
const closeShareModal = () =>
|
||||
const closeShareModal = () =>
|
||||
{
|
||||
setShowShareModal(false);
|
||||
};
|
||||
@@ -979,7 +979,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
/*******************************************************************************
|
||||
** render the share button (if allowed for table)
|
||||
*******************************************************************************/
|
||||
const renderShareButton = () =>
|
||||
const renderShareButton = () =>
|
||||
{
|
||||
if (tableMetaData && tableMetaData.shareableTableMetaData)
|
||||
{
|
||||
@@ -1019,13 +1019,13 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
};
|
||||
|
||||
|
||||
const openModalProcess = (process: QProcessMetaData = null) =>
|
||||
const openModalProcess = (process: QProcessMetaData = null) =>
|
||||
{
|
||||
navigate(process.name);
|
||||
closeActionsMenu();
|
||||
};
|
||||
|
||||
const closeModalProcess = (event: object, reason: string) =>
|
||||
const closeModalProcess = (event: object, reason: string) =>
|
||||
{
|
||||
if (reason === "backdropClick" || reason === "escapeKeyDown")
|
||||
{
|
||||
@@ -1059,7 +1059,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
setShowEditChildForm(showEditChildForm);
|
||||
}
|
||||
|
||||
const closeEditChildForm = (event: object, reason: string) =>
|
||||
const closeEditChildForm = (event: object, reason: string) =>
|
||||
{
|
||||
if (reason === "backdropClick" || reason === "escapeKeyDown")
|
||||
{
|
||||
@@ -1084,7 +1084,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
setShowEditChildForm(null);
|
||||
};
|
||||
|
||||
const closeAudit = (event: object, reason: string) =>
|
||||
const closeAudit = (event: object, reason: string) =>
|
||||
{
|
||||
if (reason === "backdropClick") // allowing esc here, as it's a non-destructive close || reason === "escapeKeyDown")
|
||||
{
|
||||
@@ -1120,6 +1120,58 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
localStorage.setItem(makeCollapsibleSectionOpenStateLocalStorageKey(tableMetaData.name, sectionName), newValue.toString());
|
||||
}
|
||||
|
||||
const materialDashboardTableMetaData = tableMetaData?.supplementalTableMetaData?.get("materialDashboard");
|
||||
const materialDashboardInstanceMetaData = metaData?.supplementalInstanceMetaData?.get("materialDashboard");
|
||||
const showRecordSidebar = materialDashboardTableMetaData?.showRecordSidebar !== false;
|
||||
const recordViewActionsPlacement = materialDashboardInstanceMetaData?.recordViewActionsPlacement ?? materialDashboardTableMetaData?.recordViewActionsPlacement ?? "IN_IDENTITY_SECTION";
|
||||
const showRecordViewActionsInlineWithPageTitle = recordViewActionsPlacement === "INLINE_WITH_PAGE_TITLE";
|
||||
|
||||
const renderRecordViewActions = (boxProps: any = {}) =>
|
||||
{
|
||||
return (
|
||||
<>
|
||||
<Box display="flex" {...boxProps}>
|
||||
<GotoRecordButton metaData={metaData} tableMetaData={tableMetaData} />
|
||||
{renderShareButton()}
|
||||
{metaData && tableMetaData && <RecordViewAdditionalMenus tableMetaData={tableMetaData} record={record} actions={recordViewMenuActions} />}
|
||||
{metaData && tableMetaData && <QActionsMenuButton isOpen={actionsMenuAnchorElement} onClickHandler={openActionsMenu} />}
|
||||
</Box>
|
||||
<Menu
|
||||
anchorEl={actionsMenuAnchorElement}
|
||||
anchorOrigin={{vertical: "bottom", horizontal: "right",}}
|
||||
transformOrigin={{vertical: "top", horizontal: "right",}}
|
||||
open={Boolean(actionsMenuAnchorElement)}
|
||||
onClose={closeActionsMenu}
|
||||
keepMounted
|
||||
data-qqq-id="record-view-actions-menu"
|
||||
>
|
||||
{actionMenu?.items.map((item, index) => (<RecordViewMenuItem key={index} tableMetaData={tableMetaData} menuItem={item} record={record} actions={recordViewMenuActions} closeMenu={closeActionsMenu} itemsShown={itemsShownInActionsMenu} />))}
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const pageHeaderRecordViewActions = useMemo(() =>
|
||||
{
|
||||
if (!showRecordViewActionsInlineWithPageTitle)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box display="flex" pb="1rem" pr="0.5rem">
|
||||
{renderRecordViewActions()}
|
||||
</Box>
|
||||
);
|
||||
}, [showRecordViewActionsInlineWithPageTitle, metaData, tableMetaData, record, actionsMenuAnchorElement, actionMenu, itemsShownInActionsMenu]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setPageHeaderRightContent?.(pageHeaderRecordViewActions);
|
||||
|
||||
return () => setPageHeaderRightContent?.(null);
|
||||
}, [setPageHeaderRightContent, pageHeaderRecordViewActions]);
|
||||
|
||||
return (
|
||||
<BaseLayout>
|
||||
<Box className="recordView">
|
||||
@@ -1134,7 +1186,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
<Box pb={3}>
|
||||
{
|
||||
successMessage ?
|
||||
<Alert color="success" sx={{mb: 3}} onClose={() =>
|
||||
<Alert color="success" sx={{mb: 3}} onClose={() =>
|
||||
{
|
||||
setSuccessMessage(null);
|
||||
}}>
|
||||
@@ -1144,7 +1196,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
}
|
||||
{
|
||||
warningMessage ?
|
||||
<Alert color="warning" sx={{mb: 3}} icon={<Icon>warning</Icon>} onClose={() =>
|
||||
<Alert color="warning" sx={{mb: 3}} icon={<Icon>warning</Icon>} onClose={() =>
|
||||
{
|
||||
setWarningMessage(null);
|
||||
}}>
|
||||
@@ -1154,7 +1206,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
}
|
||||
{
|
||||
errorMessage ?
|
||||
<Alert color="error" sx={{mb: 3}} onClose={() =>
|
||||
<Alert color="error" sx={{mb: 3}} onClose={() =>
|
||||
{
|
||||
setErrorMessage(null);
|
||||
}}>
|
||||
@@ -1164,10 +1216,10 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
}
|
||||
|
||||
<Grid container spacing={3} flexWrap={{md: "nowrap"}}>
|
||||
<Grid item xs={12} lg={3} className="recordSidebar">
|
||||
{showRecordSidebar && <Grid item xs={12} lg={3} className="recordSidebar">
|
||||
<QRecordSidebar tableSections={tableSections} />
|
||||
</Grid>
|
||||
<Grid item xs={12} lg={9} className="recordWithSidebar">
|
||||
</Grid>}
|
||||
<Grid item xs={12} lg={showRecordSidebar ? 9 : 12} className={showRecordSidebar ? "recordWithSidebar" : ""}>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} mb={3}>
|
||||
@@ -1184,23 +1236,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
<Typography variant="h5" mb="0.5rem">
|
||||
{tableMetaData && record ? `Viewing ${tableMetaData?.label}: ${record?.recordLabel || ""}` : ""}
|
||||
</Typography>
|
||||
<Box display="flex" ml="auto">
|
||||
<GotoRecordButton metaData={metaData} tableMetaData={tableMetaData} />
|
||||
{renderShareButton()}
|
||||
{metaData && tableMetaData && <RecordViewAdditionalMenus tableMetaData={tableMetaData} record={record} actions={recordViewMenuActions} />}
|
||||
{metaData && tableMetaData && <QActionsMenuButton isOpen={actionsMenuAnchorElement} onClickHandler={openActionsMenu} />}
|
||||
</Box>
|
||||
<Menu
|
||||
anchorEl={actionsMenuAnchorElement}
|
||||
anchorOrigin={{vertical: "bottom", horizontal: "right",}}
|
||||
transformOrigin={{vertical: "top", horizontal: "right",}}
|
||||
open={Boolean(actionsMenuAnchorElement)}
|
||||
onClose={closeActionsMenu}
|
||||
keepMounted
|
||||
data-qqq-id="record-view-actions-menu"
|
||||
>
|
||||
{actionMenu?.items.map((item, index) => (<RecordViewMenuItem key={index} tableMetaData={tableMetaData} menuItem={item} record={record} actions={recordViewMenuActions} closeMenu={closeActionsMenu} itemsShown={itemsShownInActionsMenu} />))}
|
||||
</Menu>
|
||||
{!showRecordViewActionsInlineWithPageTitle && renderRecordViewActions({ml: "auto"})}
|
||||
</Box>
|
||||
</Box>
|
||||
{t1Section && getSectionHelp(t1Section)}
|
||||
@@ -1209,7 +1245,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container spacing={3} pb={4}>
|
||||
{nonT1TableSections.length > 0 ? nonT1TableSections.map((section: QTableSection) =>
|
||||
{nonT1TableSections.length > 0 ? nonT1TableSections.map((section: QTableSection) =>
|
||||
{
|
||||
///////////////////////////////////////////////
|
||||
// render all sections after the T1 section. //
|
||||
@@ -1222,7 +1258,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
if (section.widgetName)
|
||||
{
|
||||
const widgetMetaData = metaData.widgets.get(section.widgetName);
|
||||
if(section.collapsible)
|
||||
if (section.collapsible)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// if the section has collapsible meta-data, then put that meta-data in the //
|
||||
@@ -1265,14 +1301,14 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
</Typography>
|
||||
{
|
||||
section.collapsible?.isCollapsible &&
|
||||
<Box p="0.75rem 0.25rem 0">
|
||||
<IconButton onClick={() => toggleCollapsibleSectionOpenState(section.name)}>
|
||||
<Icon fontSize="large">{open ? "expand_less" : "expand_more"}</Icon>
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Box p="0.75rem 0.25rem 0">
|
||||
<IconButton onClick={() => toggleCollapsibleSectionOpenState(section.name)}>
|
||||
<Icon fontSize="large">{open ? "expand_less" : "expand_more"}</Icon>
|
||||
</IconButton>
|
||||
</Box>
|
||||
}
|
||||
</Box>
|
||||
<Box style={{display: open ? "block" : "none"}}>
|
||||
<Box style={{display: open ? "block" : "none"}}>
|
||||
{getSectionHelp(section)}
|
||||
<Box p={3} pt={0} flexDirection="column">
|
||||
{sectionFieldElements.get(section.name)}
|
||||
|
||||
+54
-3
@@ -40,10 +40,62 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for MaterialDashboardTableMetaData
|
||||
** Unit test for MaterialDashboardTableMetaData
|
||||
*******************************************************************************/
|
||||
class MaterialDashboardTableMetaDataTest extends BaseTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testShowRecordSidebarDefaultsTrue()
|
||||
{
|
||||
MaterialDashboardTableMetaData tableMetaData = new MaterialDashboardTableMetaData();
|
||||
assertThat(tableMetaData.getShowRecordSidebar()).isTrue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testShowRecordSidebarCloned()
|
||||
{
|
||||
MaterialDashboardTableMetaData tableMetaData = new MaterialDashboardTableMetaData().withShowRecordSidebar(false);
|
||||
MaterialDashboardTableMetaData clone = (MaterialDashboardTableMetaData) tableMetaData.clone();
|
||||
|
||||
assertThat(clone.getShowRecordSidebar()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordViewActionsPlacementDefaultsToInIdentitySection()
|
||||
{
|
||||
MaterialDashboardTableMetaData tableMetaData = new MaterialDashboardTableMetaData();
|
||||
assertThat(tableMetaData.getRecordViewActionsPlacement()).isEqualTo(MaterialDashboardTableMetaData.RecordViewActionsPlacement.IN_IDENTITY_SECTION);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordViewActionsPlacementCloned()
|
||||
{
|
||||
MaterialDashboardTableMetaData tableMetaData = new MaterialDashboardTableMetaData()
|
||||
.withRecordViewActionsPlacement(MaterialDashboardTableMetaData.RecordViewActionsPlacement.INLINE_WITH_PAGE_TITLE);
|
||||
MaterialDashboardTableMetaData clone = (MaterialDashboardTableMetaData) tableMetaData.clone();
|
||||
|
||||
assertThat(clone.getRecordViewActionsPlacement()).isEqualTo(MaterialDashboardTableMetaData.RecordViewActionsPlacement.INLINE_WITH_PAGE_TITLE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
@@ -119,7 +171,6 @@ class MaterialDashboardTableMetaDataTest extends BaseTest
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Run a little setup code on a qInstance; then validate it, and assert that it
|
||||
** failed validation with reasons that match the supplied vararg-reasons (but allow
|
||||
@@ -209,4 +260,4 @@ class MaterialDashboardTableMetaDataTest extends BaseTest
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// todo - end of methods copied from QInstanceValidatorTest... //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user