Merge pull request #26 from Kingsrook/feature/CE-551-change-logic-for-fed-ex

Feature/ce 551 change logic for fed ex
This commit is contained in:
2023-07-26 08:43:22 -05:00
committed by GitHub
12 changed files with 355 additions and 204 deletions

View File

@ -574,6 +574,7 @@ export default function App()
const [tableMetaData, setTableMetaData] = useState(null);
const [tableProcesses, setTableProcesses] = useState(null);
const [dotMenuOpen, setDotMenuOpen] = useState(false);
const [keyboardHelpOpen, setKeyboardHelpOpen] = useState(false);
return (
appRoutes && (
@ -583,11 +584,13 @@ export default function App()
tableMetaData: tableMetaData,
tableProcesses: tableProcesses,
dotMenuOpen: dotMenuOpen,
keyboardHelpOpen: keyboardHelpOpen,
setPageHeader: (header: string | JSX.Element) => setPageHeader(header),
setAccentColor: (accentColor: string) => setAccentColor(accentColor),
setTableMetaData: (tableMetaData: QTableMetaData) => setTableMetaData(tableMetaData),
setTableProcesses: (tableProcesses: QProcessMetaData[]) => setTableProcesses(tableProcesses),
setDotMenuOpen: (dotMenuOpent: boolean) => setDotMenuOpen(dotMenuOpent),
setKeyboardHelpOpen: (keyboardHelpOpen: boolean) => setKeyboardHelpOpen(keyboardHelpOpen),
pathToLabelMap: pathToLabelMap,
branding: branding
}}>

View File

@ -67,9 +67,8 @@ const CommandMenu = ({metaData}: Props) =>
const navigate = useNavigate();
const pathParts = location.pathname.replace(/\/+$/, "").split("/");
const {accentColor, tableMetaData, dotMenuOpen, setDotMenuOpen, setTableMetaData, tableProcesses} = useContext(QContext);
const {accentColor, tableMetaData, dotMenuOpen, setDotMenuOpen, keyboardHelpOpen, setKeyboardHelpOpen, setTableMetaData, tableProcesses} = useContext(QContext);
const [keyboardHelpOpen, setKeyboardHelpOpen] = useState(false)
const classes = useStyles();
function evalueKeyPress(e: KeyboardEvent)
@ -351,6 +350,14 @@ const CommandMenu = ({metaData}: Props) =>
<Grid item xs={6} className={classes.item}><span className={classes.keyboardKey}>?</span>Open Keyboard Shortcuts Help</Grid>
</Grid>
<Typography variant="h6" pt={3}>Table Query</Typography>
<Grid container columnSpacing={5} rowSpacing={1}>
<Grid item xs={6} className={classes.item}><span className={classes.keyboardKey}>n</span>Create a New Record</Grid>
<Grid item xs={6} className={classes.item}><span className={classes.keyboardKey}>r</span>Refresh the Query</Grid>
<Grid item xs={6} className={classes.item}><span className={classes.keyboardKey}>c</span>Open the Columns Panel</Grid>
<Grid item xs={6} className={classes.item}><span className={classes.keyboardKey}>f</span>Open the Filter Panel</Grid>
</Grid>
<Typography variant="h6" pt={3}>Record View</Typography>
<Grid container columnSpacing={5} rowSpacing={1}>
<Grid item xs={6} className={classes.item}><span className={classes.keyboardKey}>n</span>Create a New Record</Grid>

View File

@ -37,6 +37,9 @@ interface QContext
dotMenuOpen: boolean;
setDotMenuOpen?: (dotMenuOpen: boolean) => void;
keyboardHelpOpen: boolean;
setKeyboardHelpOpen?: (keyboardHelpOpen: boolean) => void;
tableMetaData?: QTableMetaData;
setTableMetaData?: (tableMetaData: QTableMetaData) => void;
@ -54,6 +57,7 @@ const defaultState = {
pageHeader: "",
accentColor: "#0062FF",
dotMenuOpen: false,
keyboardHelpOpen: false,
pathToLabelMap: {},
};

View File

@ -135,7 +135,7 @@ function QDynamicFormField({
/>
<Box mt={0.75}>
<MDTypography component="div" variant="caption" color="error" fontWeight="regular">
{!isDisabled && <div className="fieldErrorMessage"><ErrorMessage name={name} /></div>}
{!isDisabled && <div className="fieldErrorMessage"><ErrorMessage name={name} render={msg => <span data-field-error="true">{msg}</span>} /></div>}
</MDTypography>
</Box>
</>

View File

@ -103,9 +103,20 @@ class DynamicFormUtils
public static getValidationForField(field: QFieldMetaData)
{
if (field.isRequired)
{
if(field.possibleValueSourceName)
{
////////////////////////////////////////////////////////////////////////////////////////////
// the "nullable(true)" here doesn't mean that you're allowed to set the field to null... //
// rather, it's more like "null is how empty will be treated" or some-such... //
////////////////////////////////////////////////////////////////////////////////////////////
return (Yup.string().required(`${field.label} is required.`).nullable(true));
}
else
{
return (Yup.string().required(`${field.label} is required.`));
}
}
return (null);
}

View File

@ -27,8 +27,9 @@ import Autocomplete from "@mui/material/Autocomplete";
import Box from "@mui/material/Box";
import Switch from "@mui/material/Switch";
import TextField from "@mui/material/TextField";
import {useFormikContext} from "formik";
import {ErrorMessage, useFormikContext} from "formik";
import React, {useEffect, useState} from "react";
import MDTypography from "qqq/components/legacy/MDTypography";
import Client from "qqq/utils/qqq/Client";
interface Props
@ -246,6 +247,7 @@ function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabe
// console.log(`default value: ${JSON.stringify(defaultValue)}`);
const autocomplete = (
<Box>
<Autocomplete
id={overrideId ?? fieldName}
sx={{background: isDisabled ? "#f0f2f5!important" : "initial"}}
@ -318,6 +320,15 @@ function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabe
/>
)}
/>
{
inForm &&
<Box mt={0.75}>
<MDTypography component="div" variant="caption" color="error" fontWeight="regular">
{!isDisabled && <div className="fieldErrorMessage"><ErrorMessage name={fieldName} render={msg => <span data-field-error="true">{msg}</span>} /></div>}
</MDTypography>
</Box>
}
</Box>
);

View File

@ -32,8 +32,8 @@ import Box from "@mui/material/Box";
import Card from "@mui/material/Card";
import Grid from "@mui/material/Grid";
import Icon from "@mui/material/Icon";
import {Form, Formik} from "formik";
import React, {useContext, useReducer, useState} from "react";
import {Form, Formik, useFormikContext} from "formik";
import React, {useContext, useEffect, useReducer, useState} from "react";
import {useLocation, useNavigate, useParams} from "react-router-dom";
import * as Yup from "yup";
import QContext from "QContext";
@ -77,7 +77,7 @@ function EntityForm(props: Props): JSX.Element
const [formTitle, setFormTitle] = useState("");
const [validations, setValidations] = useState({});
const [initialValues, setInitialValues] = useState({} as { [key: string]: string });
const [initialValues, setInitialValues] = useState({} as { [key: string]: any });
const [formFields, setFormFields] = useState(null as Map<string, any>);
const [t1sectionName, setT1SectionName] = useState(null as string);
const [nonT1Sections, setNonT1Sections] = useState([] as QTableSection[]);
@ -233,15 +233,14 @@ function EntityForm(props: Props): JSX.Element
////////////////////////////////////////////////////////////////////////////////////////////////
// if default values were supplied for a new record, then populate initialValues, for formik. //
////////////////////////////////////////////////////////////////////////////////////////////////
if(defaultValues)
{
for (let i = 0; i < fieldArray.length; i++)
{
const fieldMetaData = fieldArray[i];
const fieldName = fieldMetaData.name;
if (defaultValues[fieldName])
const defaultValue = (defaultValues && defaultValues[fieldName]) ? defaultValues[fieldName] : fieldMetaData.defaultValue;
if (defaultValue)
{
initialValues[fieldName] = defaultValues[fieldName];
initialValues[fieldName] = defaultValue;
///////////////////////////////////////////////////////////////////////////////////////////
// we need to set the initialDisplayValue for possible value fields with a default value //
@ -258,7 +257,6 @@ function EntityForm(props: Props): JSX.Element
}
}
}
}
//////////////////////////////////////
// check capabilities & permissions //
@ -598,6 +596,7 @@ function EntityForm(props: Props): JSX.Element
isSubmitting,
}) => (
<Form id={formId} autoComplete="off">
<ScrollToFirstError />
<Box pb={3} pt={0}>
<Card id={`${t1sectionName}`} sx={{overflow: "visible", pb: 2, scrollMarginTop: "100px"}} elevation={cardElevation}>
@ -672,4 +671,43 @@ function EntityForm(props: Props): JSX.Element
}
}
function ScrollToFirstError(): JSX.Element
{
const {submitCount, isValid} = useFormikContext()
useEffect(() =>
{
/////////////////////////////////////////////////////////////////////////////
// Wrap the code in setTimeout to make sure it runs after the DOM has been //
// updated and has the error message elements. //
/////////////////////////////////////////////////////////////////////////////
setTimeout(() =>
{
////////////////////////////////////////
// Only run on submit or if not valid //
////////////////////////////////////////
if (submitCount === 0 || isValid)
{
return;
}
//////////////////////////////////
// Find the first error message //
//////////////////////////////////
const errorMessageSelector = "[data-field-error]";
const firstErrorMessage = document.querySelector(errorMessageSelector);
if (!firstErrorMessage)
{
console.warn(`Form failed validation but no error field was found with selector: ${errorMessageSelector}`);
return;
}
firstErrorMessage.scrollIntoView({block: "center"});
}, 100)
}, [submitCount, isValid])
return null;
}
export default EntityForm;

View File

@ -31,7 +31,7 @@ import TextField from "@mui/material/TextField";
import {GridColDef, GridSlotsComponentsProps, useGridApiContext, useGridSelector} from "@mui/x-data-grid-pro";
import {GridColumnsPanelProps} from "@mui/x-data-grid/components/panel/GridColumnsPanel";
import {gridColumnDefinitionsSelector, gridColumnVisibilityModelSelector} from "@mui/x-data-grid/hooks/features/columns/gridColumnsSelector";
import React, {createRef, forwardRef, useEffect, useReducer, useState} from "react";
import React, {createRef, forwardRef, useEffect, useReducer, useRef, useState} from "react";
declare module "@mui/x-data-grid"
{
@ -55,6 +55,9 @@ export const CustomColumnsPanel = forwardRef<any, GridColumnsPanelProps>(
const [, forceUpdate] = useReducer((x) => x + 1, 0);
const someRef = createRef();
const textRef = useRef(null);
const [didInitialFocus, setDidInitialFocus] = useState(false)
const [openGroups, setOpenGroups] = useState(props.initialOpenedGroups || {});
const openGroupsBecauseOfFilter = {} as { [name: string]: boolean };
const [lastScrollTop, setLastScrollTop] = useState(0);
@ -68,6 +71,15 @@ export const CustomColumnsPanel = forwardRef<any, GridColumnsPanelProps>(
console.log(`Open groups: ${JSON.stringify(openGroups)}`);
if(!didInitialFocus)
{
if(textRef.current)
{
textRef.current.select();
setDidInitialFocus(true);
}
}
if (props.tableMetaData.exposedJoins)
{
for (let i = 0; i < props.tableMetaData.exposedJoins.length; i++)
@ -360,7 +372,7 @@ export const CustomColumnsPanel = forwardRef<any, GridColumnsPanelProps>(
return (
<Box className="custom-columns-panel" style={{width: "350px", height: "450px"}}>
<Box height="55px" padding="5px" display="flex">
<TextField id="findColumn" label="Find column" placeholder="Column title" variant="standard" fullWidth={true}
<TextField inputRef={textRef} id="findColumn" label="Find column" placeholder="Column title" variant="standard" fullWidth={true}
value={filterText}
onChange={(event) => filterTextChanged(event)}
></TextField>

View File

@ -56,74 +56,16 @@ export interface OperatorOption
{
label: string;
value: QCriteriaOperator;
implicitValues?: [any];
implicitValues?: any[];
valueMode: ValueMode;
}
export const getDefaultCriteriaValue = () => [""];
interface FilterCriteriaRowProps
{
id: number;
index: number;
tableMetaData: QTableMetaData;
metaData: QInstance;
criteria: QFilterCriteria;
booleanOperator: "AND" | "OR" | null;
updateCriteria: (newCriteria: QFilterCriteria, needDebounce: boolean) => void;
removeCriteria: () => void;
updateBooleanOperator: (newValue: string) => void;
}
FilterCriteriaRow.defaultProps = {};
function makeFieldOptionsForTable(tableMetaData: QTableMetaData, fieldOptions: any[], isJoinTable: boolean)
{
const sortedFields = [...tableMetaData.fields.values()].sort((a, b) => a.label.localeCompare(b.label));
for (let i = 0; i < sortedFields.length; i++)
{
const fieldName = isJoinTable ? `${tableMetaData.name}.${sortedFields[i].name}` : sortedFields[i].name;
fieldOptions.push({field: sortedFields[i], table: tableMetaData, fieldName: fieldName});
}
}
export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator}: FilterCriteriaRowProps): JSX.Element
{
// console.log(`FilterCriteriaRow: criteria: ${JSON.stringify(criteria)}`);
const [operatorSelectedValue, setOperatorSelectedValue] = useState(null as OperatorOption);
const [operatorInputValue, setOperatorInputValue] = useState("");
///////////////////////////////////////////////////////////////
// set up the array of options for the fields Autocomplete //
// also, a groupBy function, in case there are exposed joins //
///////////////////////////////////////////////////////////////
const fieldOptions: any[] = [];
makeFieldOptionsForTable(tableMetaData, fieldOptions, false);
let fieldsGroupBy = null;
if (tableMetaData.exposedJoins && tableMetaData.exposedJoins.length > 0)
{
for (let i = 0; i < tableMetaData.exposedJoins.length; i++)
{
const exposedJoin = tableMetaData.exposedJoins[i];
if (metaData.tables.has(exposedJoin.joinTable.name))
{
fieldsGroupBy = (option: any) => `${option.table.label} fields`;
makeFieldOptionsForTable(exposedJoin.joinTable, fieldOptions, true);
}
}
}
////////////////////////////////////////////////////////////
// set up array of options for operator dropdown //
// only call the function to do it if we have a field set //
////////////////////////////////////////////////////////////
let operatorOptions: OperatorOption[] = [];
function setOperatorOptions(fieldName: string)
export const getOperatorOptions = (tableMetaData: QTableMetaData, fieldName: string): OperatorOption[] =>
{
const [field, fieldTable] = FilterUtils.getField(tableMetaData, fieldName);
operatorOptions = [];
let operatorOptions = [];
if (field && fieldTable)
{
//////////////////////////////////////////////////////
@ -217,8 +159,69 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
}
}
}
return (operatorOptions);
}
interface FilterCriteriaRowProps
{
id: number;
index: number;
tableMetaData: QTableMetaData;
metaData: QInstance;
criteria: QFilterCriteria;
booleanOperator: "AND" | "OR" | null;
updateCriteria: (newCriteria: QFilterCriteria, needDebounce: boolean) => void;
removeCriteria: () => void;
updateBooleanOperator: (newValue: string) => void;
}
FilterCriteriaRow.defaultProps = {};
function makeFieldOptionsForTable(tableMetaData: QTableMetaData, fieldOptions: any[], isJoinTable: boolean)
{
const sortedFields = [...tableMetaData.fields.values()].sort((a, b) => a.label.localeCompare(b.label));
for (let i = 0; i < sortedFields.length; i++)
{
const fieldName = isJoinTable ? `${tableMetaData.name}.${sortedFields[i].name}` : sortedFields[i].name;
fieldOptions.push({field: sortedFields[i], table: tableMetaData, fieldName: fieldName});
}
}
export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator}: FilterCriteriaRowProps): JSX.Element
{
// console.log(`FilterCriteriaRow: criteria: ${JSON.stringify(criteria)}`);
const [operatorSelectedValue, setOperatorSelectedValue] = useState(null as OperatorOption);
const [operatorInputValue, setOperatorInputValue] = useState("");
///////////////////////////////////////////////////////////////
// set up the array of options for the fields Autocomplete //
// also, a groupBy function, in case there are exposed joins //
///////////////////////////////////////////////////////////////
const fieldOptions: any[] = [];
makeFieldOptionsForTable(tableMetaData, fieldOptions, false);
let fieldsGroupBy = null;
if (tableMetaData.exposedJoins && tableMetaData.exposedJoins.length > 0)
{
for (let i = 0; i < tableMetaData.exposedJoins.length; i++)
{
const exposedJoin = tableMetaData.exposedJoins[i];
if (metaData.tables.has(exposedJoin.joinTable.name))
{
fieldsGroupBy = (option: any) => `${option.table.label} fields`;
makeFieldOptionsForTable(exposedJoin.joinTable, fieldOptions, true);
}
}
}
////////////////////////////////////////////////////////////
// set up array of options for operator dropdown //
// only call the function to do it if we have a field set //
////////////////////////////////////////////////////////////
let operatorOptions: OperatorOption[] = [];
////////////////////////////////////////////////////////////////
// make currently selected values appear in the Autocompletes //
////////////////////////////////////////////////////////////////
@ -240,8 +243,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
defaultFieldValue = {field: field, table: fieldTable, fieldName: criteria.fieldName};
}
setOperatorOptions(criteria.fieldName);
operatorOptions = getOperatorOptions(tableMetaData, criteria.fieldName);
let newOperatorSelectedValue = operatorOptions.filter(option =>
{
@ -294,7 +296,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
////////////////////////////////////////////////////////////////////
// update the operator options, and the operator on this criteria //
////////////////////////////////////////////////////////////////////
setOperatorOptions(criteria.fieldName);
operatorOptions = getOperatorOptions(tableMetaData, criteria.fieldName);
if (operatorOptions.length)
{
if (isFieldTypeDifferent(oldFieldName, criteria.fieldName))
@ -473,7 +475,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
}
else
{
if(isNotSet(criteria.values[0]))
if(!criteria.values || isNotSet(criteria.values[0]))
{
criteriaIsValid = false;
criteriaStatusTooltip = "You must enter a value to complete the definition of this condition.";

View File

@ -51,7 +51,7 @@ import MenuItem from "@mui/material/MenuItem";
import Modal from "@mui/material/Modal";
import TextField from "@mui/material/TextField";
import Tooltip from "@mui/material/Tooltip";
import {DataGridPro, GridCallbackDetails, GridColDef, GridColumnMenuContainer, GridColumnMenuProps, GridColumnOrderChangeParams, GridColumnPinningMenuItems, GridColumnsMenuItem, GridColumnVisibilityModel, GridDensity, GridEventListener, GridExportMenuItemProps, GridFilterMenuItem, GridFilterModel, GridPinnedColumns, gridPreferencePanelStateSelector, GridRowId, GridRowParams, GridRowsProp, GridSelectionModel, GridSortItem, GridSortModel, GridState, GridToolbarColumnsButton, GridToolbarContainer, GridToolbarDensitySelector, GridToolbarExportContainer, GridToolbarFilterButton, HideGridColMenuItem, MuiEvent, SortGridMenuItems, useGridApiContext, useGridApiEventHandler, useGridSelector} from "@mui/x-data-grid-pro";
import {DataGridPro, GridCallbackDetails, GridColDef, GridColumnMenuContainer, GridColumnMenuProps, GridColumnOrderChangeParams, GridColumnPinningMenuItems, GridColumnsMenuItem, GridColumnVisibilityModel, GridDensity, GridEventListener, GridExportMenuItemProps, GridFilterMenuItem, GridFilterModel, GridPinnedColumns, gridPreferencePanelStateSelector, GridRowId, GridRowParams, GridRowsProp, GridSelectionModel, GridSortItem, GridSortModel, GridState, GridToolbarColumnsButton, GridToolbarContainer, GridToolbarDensitySelector, GridToolbarExportContainer, GridToolbarFilterButton, HideGridColMenuItem, MuiEvent, SortGridMenuItems, useGridApiContext, useGridApiEventHandler, useGridSelector, useGridApiRef, GridPreferencePanelsValue} from "@mui/x-data-grid-pro";
import {GridRowModel} from "@mui/x-data-grid/models/gridRows";
import FormData from "form-data";
import React, {forwardRef, useContext, useEffect, useReducer, useRef, useState} from "react";
@ -252,12 +252,65 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
const [queryErrors, setQueryErrors] = useState({} as any);
const [receivedQueryErrorTimestamp, setReceivedQueryErrorTimestamp] = useState(new Date());
const {setPageHeader} = useContext(QContext);
const {setPageHeader, dotMenuOpen, keyboardHelpOpen} = useContext(QContext);
const [, forceUpdate] = useReducer((x) => x + 1, 0);
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
const closeActionsMenu = () => setActionsMenu(null);
const gridApiRef = useGridApiRef();
///////////////////////
// Keyboard handling //
///////////////////////
useEffect(() =>
{
if(tableMetaData == null)
{
(async() =>
{
const tableMetaData = await qController.loadTableMetaData(tableName);
setTableMetaData(tableMetaData);
})();
}
const down = (e: KeyboardEvent) =>
{
const type = (e.target as any).type;
const validType = (type !== "text" && type !== "textarea" && type !== "input" && type !== "search");
if(validType && !dotMenuOpen && !keyboardHelpOpen && !activeModalProcess)
{
if (! e.metaKey && e.key === "n" && table.capabilities.has(Capability.TABLE_INSERT) && table.insertPermission)
{
e.preventDefault()
navigate(`${metaData?.getTablePathByName(tableName)}/create`);
}
else if (! e.metaKey && e.key === "r")
{
e.preventDefault()
updateTable();
}
else if (! e.metaKey && e.key === "c")
{
e.preventDefault()
gridApiRef.current.showPreferences(GridPreferencePanelsValue.columns)
}
else if (! e.metaKey && e.key === "f")
{
e.preventDefault()
gridApiRef.current.showFilterPanel()
}
}
}
document.addEventListener("keydown", down)
return () =>
{
document.removeEventListener("keydown", down)
}
}, [dotMenuOpen, keyboardHelpOpen, metaData, activeModalProcess])
/////////////////////////////////////////////////////////////////////////////////////////
// monitor location changes - if our url looks like a process, then open that process. //
/////////////////////////////////////////////////////////////////////////////////////////
@ -1934,6 +1987,7 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element
<Card>
<Box height="100%">
<DataGridPro
apiRef={gridApiRef}
components={{
Toolbar: CustomToolbar,
Pagination: CustomPagination,

View File

@ -116,7 +116,7 @@ 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);
const {accentColor, setPageHeader, tableMetaData, setTableMetaData, tableProcesses, setTableProcesses, dotMenuOpen, keyboardHelpOpen} = useContext(QContext);
if (localStorage.getItem(tableVariantLocalStorageKey))
{
@ -138,7 +138,9 @@ function RecordView({table, launchProcess}: Props): JSX.Element
setShowAudit(false);
};
// Toggle the menu when ⌘K is pressed
///////////////////////
// Keyboard handling //
///////////////////////
useEffect(() =>
{
if(tableMetaData == null)
@ -155,7 +157,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element
const type = (e.target as any).type;
const validType = (type !== "text" && type !== "textarea" && type !== "input" && type !== "search");
if(validType && !dotMenuOpen && !showAudit && !showEditChildForm)
if(validType && !dotMenuOpen && !keyboardHelpOpen && !showAudit && !showEditChildForm)
{
if (! e.metaKey && e.key === "n" && table.capabilities.has(Capability.TABLE_INSERT) && table.insertPermission)
{
@ -190,7 +192,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element
{
document.removeEventListener("keydown", down)
}
}, [dotMenuOpen, showEditChildForm, showAudit, metaData])
}, [dotMenuOpen, keyboardHelpOpen, showEditChildForm, showAudit, metaData])
const gotoCreate = () =>
{

View File

@ -433,6 +433,13 @@ input[type="search"]::-webkit-search-results-decoration { display: none; }
line-height: 1.75;
}
.filterCriteriaRowColumnPopper .MuiAutocomplete-groupLabel
{
line-height: 1.75;
padding-top: 8px;
padding-bottom: 8px;
}
/* taller list box */
.filterCriteriaRowColumnPopper .MuiAutocomplete-listbox
{