mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-18 05:10:45 +00:00
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:
@ -574,6 +574,7 @@ export default function App()
|
|||||||
const [tableMetaData, setTableMetaData] = useState(null);
|
const [tableMetaData, setTableMetaData] = useState(null);
|
||||||
const [tableProcesses, setTableProcesses] = useState(null);
|
const [tableProcesses, setTableProcesses] = useState(null);
|
||||||
const [dotMenuOpen, setDotMenuOpen] = useState(false);
|
const [dotMenuOpen, setDotMenuOpen] = useState(false);
|
||||||
|
const [keyboardHelpOpen, setKeyboardHelpOpen] = useState(false);
|
||||||
return (
|
return (
|
||||||
|
|
||||||
appRoutes && (
|
appRoutes && (
|
||||||
@ -583,11 +584,13 @@ export default function App()
|
|||||||
tableMetaData: tableMetaData,
|
tableMetaData: tableMetaData,
|
||||||
tableProcesses: tableProcesses,
|
tableProcesses: tableProcesses,
|
||||||
dotMenuOpen: dotMenuOpen,
|
dotMenuOpen: dotMenuOpen,
|
||||||
|
keyboardHelpOpen: keyboardHelpOpen,
|
||||||
setPageHeader: (header: string | JSX.Element) => setPageHeader(header),
|
setPageHeader: (header: string | JSX.Element) => setPageHeader(header),
|
||||||
setAccentColor: (accentColor: string) => setAccentColor(accentColor),
|
setAccentColor: (accentColor: string) => setAccentColor(accentColor),
|
||||||
setTableMetaData: (tableMetaData: QTableMetaData) => setTableMetaData(tableMetaData),
|
setTableMetaData: (tableMetaData: QTableMetaData) => setTableMetaData(tableMetaData),
|
||||||
setTableProcesses: (tableProcesses: QProcessMetaData[]) => setTableProcesses(tableProcesses),
|
setTableProcesses: (tableProcesses: QProcessMetaData[]) => setTableProcesses(tableProcesses),
|
||||||
setDotMenuOpen: (dotMenuOpent: boolean) => setDotMenuOpen(dotMenuOpent),
|
setDotMenuOpen: (dotMenuOpent: boolean) => setDotMenuOpen(dotMenuOpent),
|
||||||
|
setKeyboardHelpOpen: (keyboardHelpOpen: boolean) => setKeyboardHelpOpen(keyboardHelpOpen),
|
||||||
pathToLabelMap: pathToLabelMap,
|
pathToLabelMap: pathToLabelMap,
|
||||||
branding: branding
|
branding: branding
|
||||||
}}>
|
}}>
|
||||||
|
@ -67,9 +67,8 @@ const CommandMenu = ({metaData}: Props) =>
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const pathParts = location.pathname.replace(/\/+$/, "").split("/");
|
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();
|
const classes = useStyles();
|
||||||
|
|
||||||
function evalueKeyPress(e: KeyboardEvent)
|
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 item xs={6} className={classes.item}><span className={classes.keyboardKey}>?</span>Open Keyboard Shortcuts Help</Grid>
|
||||||
</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>
|
<Typography variant="h6" pt={3}>Record View</Typography>
|
||||||
<Grid container columnSpacing={5} rowSpacing={1}>
|
<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}>n</span>Create a New Record</Grid>
|
||||||
|
@ -37,6 +37,9 @@ interface QContext
|
|||||||
dotMenuOpen: boolean;
|
dotMenuOpen: boolean;
|
||||||
setDotMenuOpen?: (dotMenuOpen: boolean) => void;
|
setDotMenuOpen?: (dotMenuOpen: boolean) => void;
|
||||||
|
|
||||||
|
keyboardHelpOpen: boolean;
|
||||||
|
setKeyboardHelpOpen?: (keyboardHelpOpen: boolean) => void;
|
||||||
|
|
||||||
tableMetaData?: QTableMetaData;
|
tableMetaData?: QTableMetaData;
|
||||||
setTableMetaData?: (tableMetaData: QTableMetaData) => void;
|
setTableMetaData?: (tableMetaData: QTableMetaData) => void;
|
||||||
|
|
||||||
@ -54,6 +57,7 @@ const defaultState = {
|
|||||||
pageHeader: "",
|
pageHeader: "",
|
||||||
accentColor: "#0062FF",
|
accentColor: "#0062FF",
|
||||||
dotMenuOpen: false,
|
dotMenuOpen: false,
|
||||||
|
keyboardHelpOpen: false,
|
||||||
pathToLabelMap: {},
|
pathToLabelMap: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ function QDynamicFormField({
|
|||||||
/>
|
/>
|
||||||
<Box mt={0.75}>
|
<Box mt={0.75}>
|
||||||
<MDTypography component="div" variant="caption" color="error" fontWeight="regular">
|
<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>
|
</MDTypography>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
|
@ -104,7 +104,18 @@ class DynamicFormUtils
|
|||||||
{
|
{
|
||||||
if (field.isRequired)
|
if (field.isRequired)
|
||||||
{
|
{
|
||||||
return (Yup.string().required(`${field.label} is required.`));
|
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);
|
return (null);
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,9 @@ import Autocomplete from "@mui/material/Autocomplete";
|
|||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Switch from "@mui/material/Switch";
|
import Switch from "@mui/material/Switch";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import {useFormikContext} from "formik";
|
import {ErrorMessage, useFormikContext} from "formik";
|
||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
|
import MDTypography from "qqq/components/legacy/MDTypography";
|
||||||
import Client from "qqq/utils/qqq/Client";
|
import Client from "qqq/utils/qqq/Client";
|
||||||
|
|
||||||
interface Props
|
interface Props
|
||||||
@ -246,78 +247,88 @@ function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabe
|
|||||||
// console.log(`default value: ${JSON.stringify(defaultValue)}`);
|
// console.log(`default value: ${JSON.stringify(defaultValue)}`);
|
||||||
|
|
||||||
const autocomplete = (
|
const autocomplete = (
|
||||||
<Autocomplete
|
<Box>
|
||||||
id={overrideId ?? fieldName}
|
<Autocomplete
|
||||||
sx={{background: isDisabled ? "#f0f2f5!important" : "initial"}}
|
id={overrideId ?? fieldName}
|
||||||
open={open}
|
sx={{background: isDisabled ? "#f0f2f5!important" : "initial"}}
|
||||||
fullWidth
|
open={open}
|
||||||
onOpen={() =>
|
fullWidth
|
||||||
{
|
onOpen={() =>
|
||||||
setOpen(true);
|
|
||||||
// console.log("setting open...");
|
|
||||||
if(options.length == 0)
|
|
||||||
{
|
{
|
||||||
// console.log("no options yet, so setting search term to ''...");
|
setOpen(true);
|
||||||
setSearchTerm("");
|
// console.log("setting open...");
|
||||||
}
|
if(options.length == 0)
|
||||||
}}
|
{
|
||||||
onClose={() =>
|
// console.log("no options yet, so setting search term to ''...");
|
||||||
{
|
setSearchTerm("");
|
||||||
setOpen(false);
|
}
|
||||||
}}
|
}}
|
||||||
isOptionEqualToValue={(option, value) => option.id === value.id}
|
onClose={() =>
|
||||||
getOptionLabel={(option) =>
|
{
|
||||||
{
|
setOpen(false);
|
||||||
// @ts-ignore
|
}}
|
||||||
if(option && option.length)
|
isOptionEqualToValue={(option, value) => option.id === value.id}
|
||||||
|
getOptionLabel={(option) =>
|
||||||
{
|
{
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
option = option[0];
|
if(option && option.length)
|
||||||
}
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
option = option[0];
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
return option.label
|
||||||
|
}}
|
||||||
|
options={options}
|
||||||
|
loading={loading}
|
||||||
|
onInputChange={inputChanged}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
defaultValue={defaultValue}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return option.label
|
onChange={handleChanged}
|
||||||
}}
|
noOptionsText={"No matches found"}
|
||||||
options={options}
|
onKeyPress={e =>
|
||||||
loading={loading}
|
|
||||||
onInputChange={inputChanged}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
defaultValue={defaultValue}
|
|
||||||
// @ts-ignore
|
|
||||||
onChange={handleChanged}
|
|
||||||
noOptionsText={"No matches found"}
|
|
||||||
onKeyPress={e =>
|
|
||||||
{
|
|
||||||
if (e.key === "Enter")
|
|
||||||
{
|
{
|
||||||
e.preventDefault();
|
if (e.key === "Enter")
|
||||||
}
|
{
|
||||||
}}
|
e.preventDefault();
|
||||||
renderOption={renderOption}
|
}
|
||||||
filterOptions={filterOptions}
|
}}
|
||||||
disabled={isDisabled}
|
renderOption={renderOption}
|
||||||
multiple={isMultiple}
|
filterOptions={filterOptions}
|
||||||
disableCloseOnSelect={isMultiple}
|
disabled={isDisabled}
|
||||||
limitTags={5}
|
multiple={isMultiple}
|
||||||
slotProps={{popper: {className: "DynamicSelectPopper"}}}
|
disableCloseOnSelect={isMultiple}
|
||||||
renderInput={(params) => (
|
limitTags={5}
|
||||||
<TextField
|
slotProps={{popper: {className: "DynamicSelectPopper"}}}
|
||||||
{...params}
|
renderInput={(params) => (
|
||||||
label={fieldLabel}
|
<TextField
|
||||||
variant="standard"
|
{...params}
|
||||||
autoComplete="off"
|
label={fieldLabel}
|
||||||
type="search"
|
variant="standard"
|
||||||
InputProps={{
|
autoComplete="off"
|
||||||
...params.InputProps,
|
type="search"
|
||||||
endAdornment: (
|
InputProps={{
|
||||||
<React.Fragment>
|
...params.InputProps,
|
||||||
{loading ? <CircularProgress color="inherit" size={20} /> : null}
|
endAdornment: (
|
||||||
{params.InputProps.endAdornment}
|
<React.Fragment>
|
||||||
</React.Fragment>
|
{loading ? <CircularProgress color="inherit" size={20} /> : null}
|
||||||
),
|
{params.InputProps.endAdornment}
|
||||||
}}
|
</React.Fragment>
|
||||||
/>
|
),
|
||||||
)}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
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>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ import Box from "@mui/material/Box";
|
|||||||
import Card from "@mui/material/Card";
|
import Card from "@mui/material/Card";
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
import {Form, Formik} from "formik";
|
import {Form, Formik, useFormikContext} from "formik";
|
||||||
import React, {useContext, useReducer, useState} from "react";
|
import React, {useContext, useEffect, useReducer, useState} from "react";
|
||||||
import {useLocation, useNavigate, useParams} from "react-router-dom";
|
import {useLocation, useNavigate, useParams} from "react-router-dom";
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import QContext from "QContext";
|
import QContext from "QContext";
|
||||||
@ -77,7 +77,7 @@ function EntityForm(props: Props): JSX.Element
|
|||||||
|
|
||||||
const [formTitle, setFormTitle] = useState("");
|
const [formTitle, setFormTitle] = useState("");
|
||||||
const [validations, setValidations] = 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 [formFields, setFormFields] = useState(null as Map<string, any>);
|
||||||
const [t1sectionName, setT1SectionName] = useState(null as string);
|
const [t1sectionName, setT1SectionName] = useState(null as string);
|
||||||
const [nonT1Sections, setNonT1Sections] = useState([] as QTableSection[]);
|
const [nonT1Sections, setNonT1Sections] = useState([] as QTableSection[]);
|
||||||
@ -233,27 +233,25 @@ function EntityForm(props: Props): JSX.Element
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// if default values were supplied for a new record, then populate initialValues, for formik. //
|
// if default values were supplied for a new record, then populate initialValues, for formik. //
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
if(defaultValues)
|
for (let i = 0; i < fieldArray.length; i++)
|
||||||
{
|
{
|
||||||
for (let i = 0; i < fieldArray.length; i++)
|
const fieldMetaData = fieldArray[i];
|
||||||
|
const fieldName = fieldMetaData.name;
|
||||||
|
const defaultValue = (defaultValues && defaultValues[fieldName]) ? defaultValues[fieldName] : fieldMetaData.defaultValue;
|
||||||
|
if (defaultValue)
|
||||||
{
|
{
|
||||||
const fieldMetaData = fieldArray[i];
|
initialValues[fieldName] = defaultValue;
|
||||||
const fieldName = fieldMetaData.name;
|
|
||||||
if (defaultValues[fieldName])
|
|
||||||
{
|
|
||||||
initialValues[fieldName] = defaultValues[fieldName];
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// we need to set the initialDisplayValue for possible value fields with a default value //
|
// we need to set the initialDisplayValue for possible value fields with a default value //
|
||||||
// so, look them up here now if needed //
|
// so, look them up here now if needed //
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
if (fieldMetaData.possibleValueSourceName)
|
if (fieldMetaData.possibleValueSourceName)
|
||||||
|
{
|
||||||
|
const results: QPossibleValue[] = await qController.possibleValues(tableName, null, fieldName, null, [initialValues[fieldName]]);
|
||||||
|
if (results && results.length > 0)
|
||||||
{
|
{
|
||||||
const results: QPossibleValue[] = await qController.possibleValues(tableName, null, fieldName, null, [initialValues[fieldName]]);
|
defaultDisplayValues.set(fieldName, results[0].label);
|
||||||
if (results && results.length > 0)
|
|
||||||
{
|
|
||||||
defaultDisplayValues.set(fieldName, results[0].label);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -598,6 +596,7 @@ function EntityForm(props: Props): JSX.Element
|
|||||||
isSubmitting,
|
isSubmitting,
|
||||||
}) => (
|
}) => (
|
||||||
<Form id={formId} autoComplete="off">
|
<Form id={formId} autoComplete="off">
|
||||||
|
<ScrollToFirstError />
|
||||||
|
|
||||||
<Box pb={3} pt={0}>
|
<Box pb={3} pt={0}>
|
||||||
<Card id={`${t1sectionName}`} sx={{overflow: "visible", pb: 2, scrollMarginTop: "100px"}} elevation={cardElevation}>
|
<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;
|
export default EntityForm;
|
||||||
|
@ -31,7 +31,7 @@ import TextField from "@mui/material/TextField";
|
|||||||
import {GridColDef, GridSlotsComponentsProps, useGridApiContext, useGridSelector} from "@mui/x-data-grid-pro";
|
import {GridColDef, GridSlotsComponentsProps, useGridApiContext, useGridSelector} from "@mui/x-data-grid-pro";
|
||||||
import {GridColumnsPanelProps} from "@mui/x-data-grid/components/panel/GridColumnsPanel";
|
import {GridColumnsPanelProps} from "@mui/x-data-grid/components/panel/GridColumnsPanel";
|
||||||
import {gridColumnDefinitionsSelector, gridColumnVisibilityModelSelector} from "@mui/x-data-grid/hooks/features/columns/gridColumnsSelector";
|
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"
|
declare module "@mui/x-data-grid"
|
||||||
{
|
{
|
||||||
@ -55,6 +55,9 @@ export const CustomColumnsPanel = forwardRef<any, GridColumnsPanelProps>(
|
|||||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||||
const someRef = createRef();
|
const someRef = createRef();
|
||||||
|
|
||||||
|
const textRef = useRef(null);
|
||||||
|
const [didInitialFocus, setDidInitialFocus] = useState(false)
|
||||||
|
|
||||||
const [openGroups, setOpenGroups] = useState(props.initialOpenedGroups || {});
|
const [openGroups, setOpenGroups] = useState(props.initialOpenedGroups || {});
|
||||||
const openGroupsBecauseOfFilter = {} as { [name: string]: boolean };
|
const openGroupsBecauseOfFilter = {} as { [name: string]: boolean };
|
||||||
const [lastScrollTop, setLastScrollTop] = useState(0);
|
const [lastScrollTop, setLastScrollTop] = useState(0);
|
||||||
@ -68,6 +71,15 @@ export const CustomColumnsPanel = forwardRef<any, GridColumnsPanelProps>(
|
|||||||
|
|
||||||
console.log(`Open groups: ${JSON.stringify(openGroups)}`);
|
console.log(`Open groups: ${JSON.stringify(openGroups)}`);
|
||||||
|
|
||||||
|
if(!didInitialFocus)
|
||||||
|
{
|
||||||
|
if(textRef.current)
|
||||||
|
{
|
||||||
|
textRef.current.select();
|
||||||
|
setDidInitialFocus(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (props.tableMetaData.exposedJoins)
|
if (props.tableMetaData.exposedJoins)
|
||||||
{
|
{
|
||||||
for (let i = 0; i < props.tableMetaData.exposedJoins.length; i++)
|
for (let i = 0; i < props.tableMetaData.exposedJoins.length; i++)
|
||||||
@ -360,7 +372,7 @@ export const CustomColumnsPanel = forwardRef<any, GridColumnsPanelProps>(
|
|||||||
return (
|
return (
|
||||||
<Box className="custom-columns-panel" style={{width: "350px", height: "450px"}}>
|
<Box className="custom-columns-panel" style={{width: "350px", height: "450px"}}>
|
||||||
<Box height="55px" padding="5px" display="flex">
|
<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}
|
value={filterText}
|
||||||
onChange={(event) => filterTextChanged(event)}
|
onChange={(event) => filterTextChanged(event)}
|
||||||
></TextField>
|
></TextField>
|
||||||
|
@ -56,12 +56,114 @@ export interface OperatorOption
|
|||||||
{
|
{
|
||||||
label: string;
|
label: string;
|
||||||
value: QCriteriaOperator;
|
value: QCriteriaOperator;
|
||||||
implicitValues?: [any];
|
implicitValues?: any[];
|
||||||
valueMode: ValueMode;
|
valueMode: ValueMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getDefaultCriteriaValue = () => [""];
|
export const getDefaultCriteriaValue = () => [""];
|
||||||
|
|
||||||
|
export const getOperatorOptions = (tableMetaData: QTableMetaData, fieldName: string): OperatorOption[] =>
|
||||||
|
{
|
||||||
|
const [field, fieldTable] = FilterUtils.getField(tableMetaData, fieldName);
|
||||||
|
let operatorOptions = [];
|
||||||
|
if (field && fieldTable)
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// setup array of options for operator Autocomplete //
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
if (field.possibleValueSourceName)
|
||||||
|
{
|
||||||
|
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.PVS_SINGLE});
|
||||||
|
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.PVS_SINGLE});
|
||||||
|
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is any of", value: QCriteriaOperator.IN, valueMode: ValueMode.PVS_MULTI});
|
||||||
|
operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN, valueMode: ValueMode.PVS_MULTI});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (field.type)
|
||||||
|
{
|
||||||
|
case QFieldType.DECIMAL:
|
||||||
|
case QFieldType.INTEGER:
|
||||||
|
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "greater than", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "greater than or equals", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "less than", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "less than or equals", value: QCriteriaOperator.LESS_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN, valueMode: ValueMode.DOUBLE});
|
||||||
|
operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN, valueMode: ValueMode.DOUBLE});
|
||||||
|
operatorOptions.push({label: "is any of", value: QCriteriaOperator.IN, valueMode: ValueMode.MULTI});
|
||||||
|
operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN, valueMode: ValueMode.MULTI});
|
||||||
|
break;
|
||||||
|
case QFieldType.DATE:
|
||||||
|
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE});
|
||||||
|
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE_DATE});
|
||||||
|
operatorOptions.push({label: "is after", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE_DATE});
|
||||||
|
operatorOptions.push({label: "is on or after", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE});
|
||||||
|
operatorOptions.push({label: "is before", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE_DATE});
|
||||||
|
operatorOptions.push({label: "is on or before", value: QCriteriaOperator.LESS_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE});
|
||||||
|
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN, valueMode: ValueMode.DOUBLE_DATE});
|
||||||
|
operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN, valueMode: ValueMode.DOUBLE_DATE});
|
||||||
|
//? operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN});
|
||||||
|
//? operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN});
|
||||||
|
//? operatorOptions.push({label: "is any of", value: QCriteriaOperator.IN});
|
||||||
|
//? operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN});
|
||||||
|
break;
|
||||||
|
case QFieldType.DATE_TIME:
|
||||||
|
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||||
|
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||||
|
operatorOptions.push({label: "is after", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||||
|
operatorOptions.push({label: "is at or after", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||||
|
operatorOptions.push({label: "is before", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||||
|
operatorOptions.push({label: "is at or before", value: QCriteriaOperator.LESS_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
|
||||||
|
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN, valueMode: ValueMode.DOUBLE_DATE_TIME});
|
||||||
|
operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN, valueMode: ValueMode.DOUBLE_DATE_TIME});
|
||||||
|
//? operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN});
|
||||||
|
//? operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN});
|
||||||
|
break;
|
||||||
|
case QFieldType.BOOLEAN:
|
||||||
|
operatorOptions.push({label: "equals yes", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [true]});
|
||||||
|
operatorOptions.push({label: "equals no", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [false]});
|
||||||
|
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
/*
|
||||||
|
? is yes or empty (is not no)
|
||||||
|
? is no or empty (is not yes)
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
case QFieldType.BLOB:
|
||||||
|
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "contains ", value: QCriteriaOperator.CONTAINS, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "does not contain", value: QCriteriaOperator.NOT_CONTAINS, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "starts with", value: QCriteriaOperator.STARTS_WITH, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "does not start with", value: QCriteriaOperator.NOT_STARTS_WITH, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "ends with", value: QCriteriaOperator.ENDS_WITH, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "does not end with", value: QCriteriaOperator.NOT_ENDS_WITH, valueMode: ValueMode.SINGLE});
|
||||||
|
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
||||||
|
operatorOptions.push({label: "is any of", value: QCriteriaOperator.IN, valueMode: ValueMode.MULTI});
|
||||||
|
operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN, valueMode: ValueMode.MULTI});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (operatorOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
interface FilterCriteriaRowProps
|
interface FilterCriteriaRowProps
|
||||||
{
|
{
|
||||||
id: number;
|
id: number;
|
||||||
@ -120,105 +222,6 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
let operatorOptions: OperatorOption[] = [];
|
let operatorOptions: OperatorOption[] = [];
|
||||||
|
|
||||||
function setOperatorOptions(fieldName: string)
|
|
||||||
{
|
|
||||||
const [field, fieldTable] = FilterUtils.getField(tableMetaData, fieldName);
|
|
||||||
operatorOptions = [];
|
|
||||||
if (field && fieldTable)
|
|
||||||
{
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
// setup array of options for operator Autocomplete //
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
if (field.possibleValueSourceName)
|
|
||||||
{
|
|
||||||
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.PVS_SINGLE});
|
|
||||||
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.PVS_SINGLE});
|
|
||||||
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is any of", value: QCriteriaOperator.IN, valueMode: ValueMode.PVS_MULTI});
|
|
||||||
operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN, valueMode: ValueMode.PVS_MULTI});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (field.type)
|
|
||||||
{
|
|
||||||
case QFieldType.DECIMAL:
|
|
||||||
case QFieldType.INTEGER:
|
|
||||||
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "greater than", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "greater than or equals", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "less than", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "less than or equals", value: QCriteriaOperator.LESS_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN, valueMode: ValueMode.DOUBLE});
|
|
||||||
operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN, valueMode: ValueMode.DOUBLE});
|
|
||||||
operatorOptions.push({label: "is any of", value: QCriteriaOperator.IN, valueMode: ValueMode.MULTI});
|
|
||||||
operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN, valueMode: ValueMode.MULTI});
|
|
||||||
break;
|
|
||||||
case QFieldType.DATE:
|
|
||||||
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE});
|
|
||||||
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE_DATE});
|
|
||||||
operatorOptions.push({label: "is after", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE_DATE});
|
|
||||||
operatorOptions.push({label: "is on or after", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE});
|
|
||||||
operatorOptions.push({label: "is before", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE_DATE});
|
|
||||||
operatorOptions.push({label: "is on or before", value: QCriteriaOperator.LESS_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE});
|
|
||||||
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN, valueMode: ValueMode.DOUBLE_DATE});
|
|
||||||
operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN, valueMode: ValueMode.DOUBLE_DATE});
|
|
||||||
//? operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN});
|
|
||||||
//? operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN});
|
|
||||||
//? operatorOptions.push({label: "is any of", value: QCriteriaOperator.IN});
|
|
||||||
//? operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN});
|
|
||||||
break;
|
|
||||||
case QFieldType.DATE_TIME:
|
|
||||||
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
|
|
||||||
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE_DATE_TIME});
|
|
||||||
operatorOptions.push({label: "is after", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE_DATE_TIME});
|
|
||||||
operatorOptions.push({label: "is at or after", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
|
|
||||||
operatorOptions.push({label: "is before", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE_DATE_TIME});
|
|
||||||
operatorOptions.push({label: "is at or before", value: QCriteriaOperator.LESS_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME});
|
|
||||||
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN, valueMode: ValueMode.DOUBLE_DATE_TIME});
|
|
||||||
operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN, valueMode: ValueMode.DOUBLE_DATE_TIME});
|
|
||||||
//? operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN});
|
|
||||||
//? operatorOptions.push({label: "is not between", value: QCriteriaOperator.NOT_BETWEEN});
|
|
||||||
break;
|
|
||||||
case QFieldType.BOOLEAN:
|
|
||||||
operatorOptions.push({label: "equals yes", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [true]});
|
|
||||||
operatorOptions.push({label: "equals no", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.NONE, implicitValues: [false]});
|
|
||||||
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
/*
|
|
||||||
? is yes or empty (is not no)
|
|
||||||
? is no or empty (is not yes)
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
case QFieldType.BLOB:
|
|
||||||
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "contains ", value: QCriteriaOperator.CONTAINS, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "does not contain", value: QCriteriaOperator.NOT_CONTAINS, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "starts with", value: QCriteriaOperator.STARTS_WITH, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "does not start with", value: QCriteriaOperator.NOT_STARTS_WITH, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "ends with", value: QCriteriaOperator.ENDS_WITH, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "does not end with", value: QCriteriaOperator.NOT_ENDS_WITH, valueMode: ValueMode.SINGLE});
|
|
||||||
operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE});
|
|
||||||
operatorOptions.push({label: "is any of", value: QCriteriaOperator.IN, valueMode: ValueMode.MULTI});
|
|
||||||
operatorOptions.push({label: "is none of", value: QCriteriaOperator.NOT_IN, valueMode: ValueMode.MULTI});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
// make currently selected values appear in the Autocompletes //
|
// 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};
|
defaultFieldValue = {field: field, table: fieldTable, fieldName: criteria.fieldName};
|
||||||
}
|
}
|
||||||
|
|
||||||
setOperatorOptions(criteria.fieldName);
|
operatorOptions = getOperatorOptions(tableMetaData, criteria.fieldName);
|
||||||
|
|
||||||
|
|
||||||
let newOperatorSelectedValue = operatorOptions.filter(option =>
|
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 //
|
// update the operator options, and the operator on this criteria //
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
setOperatorOptions(criteria.fieldName);
|
operatorOptions = getOperatorOptions(tableMetaData, criteria.fieldName);
|
||||||
if (operatorOptions.length)
|
if (operatorOptions.length)
|
||||||
{
|
{
|
||||||
if (isFieldTypeDifferent(oldFieldName, criteria.fieldName))
|
if (isFieldTypeDifferent(oldFieldName, criteria.fieldName))
|
||||||
@ -473,7 +475,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(isNotSet(criteria.values[0]))
|
if(!criteria.values || isNotSet(criteria.values[0]))
|
||||||
{
|
{
|
||||||
criteriaIsValid = false;
|
criteriaIsValid = false;
|
||||||
criteriaStatusTooltip = "You must enter a value to complete the definition of this condition.";
|
criteriaStatusTooltip = "You must enter a value to complete the definition of this condition.";
|
||||||
|
@ -51,7 +51,7 @@ import MenuItem from "@mui/material/MenuItem";
|
|||||||
import Modal from "@mui/material/Modal";
|
import Modal from "@mui/material/Modal";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
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 {GridRowModel} from "@mui/x-data-grid/models/gridRows";
|
||||||
import FormData from "form-data";
|
import FormData from "form-data";
|
||||||
import React, {forwardRef, useContext, useEffect, useReducer, useRef, useState} from "react";
|
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 [queryErrors, setQueryErrors] = useState({} as any);
|
||||||
const [receivedQueryErrorTimestamp, setReceivedQueryErrorTimestamp] = useState(new Date());
|
const [receivedQueryErrorTimestamp, setReceivedQueryErrorTimestamp] = useState(new Date());
|
||||||
|
|
||||||
const {setPageHeader} = useContext(QContext);
|
const {setPageHeader, dotMenuOpen, keyboardHelpOpen} = useContext(QContext);
|
||||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||||
|
|
||||||
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
||||||
const closeActionsMenu = () => setActionsMenu(null);
|
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. //
|
// 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>
|
<Card>
|
||||||
<Box height="100%">
|
<Box height="100%">
|
||||||
<DataGridPro
|
<DataGridPro
|
||||||
|
apiRef={gridApiRef}
|
||||||
components={{
|
components={{
|
||||||
Toolbar: CustomToolbar,
|
Toolbar: CustomToolbar,
|
||||||
Pagination: CustomPagination,
|
Pagination: CustomPagination,
|
||||||
|
@ -116,7 +116,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element
|
|||||||
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
||||||
const closeActionsMenu = () => setActionsMenu(null);
|
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))
|
if (localStorage.getItem(tableVariantLocalStorageKey))
|
||||||
{
|
{
|
||||||
@ -138,7 +138,9 @@ function RecordView({table, launchProcess}: Props): JSX.Element
|
|||||||
setShowAudit(false);
|
setShowAudit(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Toggle the menu when ⌘K is pressed
|
///////////////////////
|
||||||
|
// Keyboard handling //
|
||||||
|
///////////////////////
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(tableMetaData == null)
|
if(tableMetaData == null)
|
||||||
@ -155,7 +157,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element
|
|||||||
const type = (e.target as any).type;
|
const type = (e.target as any).type;
|
||||||
const validType = (type !== "text" && type !== "textarea" && type !== "input" && type !== "search");
|
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)
|
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)
|
document.removeEventListener("keydown", down)
|
||||||
}
|
}
|
||||||
}, [dotMenuOpen, showEditChildForm, showAudit, metaData])
|
}, [dotMenuOpen, keyboardHelpOpen, showEditChildForm, showAudit, metaData])
|
||||||
|
|
||||||
const gotoCreate = () =>
|
const gotoCreate = () =>
|
||||||
{
|
{
|
||||||
|
@ -433,6 +433,13 @@ input[type="search"]::-webkit-search-results-decoration { display: none; }
|
|||||||
line-height: 1.75;
|
line-height: 1.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filterCriteriaRowColumnPopper .MuiAutocomplete-groupLabel
|
||||||
|
{
|
||||||
|
line-height: 1.75;
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
/* taller list box */
|
/* taller list box */
|
||||||
.filterCriteriaRowColumnPopper .MuiAutocomplete-listbox
|
.filterCriteriaRowColumnPopper .MuiAutocomplete-listbox
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user