Merge branch 'integration/sprint-28' into feature/CTLE-214-dot-menu

This commit is contained in:
Tim Chamberlain
2023-06-22 10:17:11 -05:00
26 changed files with 2469 additions and 272 deletions

View File

@ -118,12 +118,12 @@ function ChipTextField({...props})
return (
<div id="chip-text-field-container" style={{flexWrap: "wrap", display:"flex"}}>
<TextField
sx={{width: "100%"}}
sx={{width: "99%"}}
disabled={disabled}
label={label}
InputProps={{
startAdornment:
<div>
<div style={{overflowY: "auto", overflowX: "hidden", margin: "-10px", width: "calc(100% + 20px)", padding: "10px", marginBottom: "-20px", height: "calc(100% + 10px)"}}>
{
chips.map((item, i) => (
<Chip

View File

@ -38,6 +38,7 @@ interface Props
tableName?: string;
processName?: string;
fieldName: string;
overrideId?: string;
fieldLabel: string;
inForm: boolean;
initialValue?: any;
@ -70,29 +71,34 @@ DynamicSelect.defaultProps = {
const qController = Client.getInstance();
function DynamicSelect({tableName, processName, fieldName, fieldLabel, inForm, initialValue, initialDisplayValue, initialValues, onChange, isEditable, isMultiple, bulkEditMode, bulkEditSwitchChangeHandler, otherValues}: Props)
function DynamicSelect({tableName, processName, fieldName, overrideId, fieldLabel, inForm, initialValue, initialDisplayValue, initialValues, onChange, isEditable, isMultiple, bulkEditMode, bulkEditSwitchChangeHandler, otherValues}: Props)
{
const [ open, setOpen ] = useState(false);
const [ options, setOptions ] = useState<readonly QPossibleValue[]>([]);
const [ searchTerm, setSearchTerm ] = useState(null);
const [ firstRender, setFirstRender ] = useState(true);
const [open, setOpen] = useState(false);
const [options, setOptions] = useState<readonly QPossibleValue[]>([]);
const [searchTerm, setSearchTerm] = useState(null);
const [firstRender, setFirstRender] = useState(true);
////////////////////////////////////////////////////////////////////////////////////////////////
// default value - needs to be an array (from initialValues (array) prop) for multiple mode - //
// else non-multiple, assume we took in an initialValue (id) and initialDisplayValue (label), //
// and build a little object that looks like a possibleValue out of those //
////////////////////////////////////////////////////////////////////////////////////////////////
const [defaultValue, _] = isMultiple ? useState(initialValues ?? undefined)
let [defaultValue, _] = isMultiple ? useState(initialValues ?? undefined)
: useState(initialValue && initialDisplayValue ? [{id: initialValue, label: initialDisplayValue}] : null);
if (isMultiple && defaultValue === null)
{
defaultValue = [];
}
// const loading = open && options.length === 0;
const [loading, setLoading] = useState(false);
const [ switchChecked, setSwitchChecked ] = useState(false);
const [ isDisabled, setIsDisabled ] = useState(!isEditable || bulkEditMode);
const [switchChecked, setSwitchChecked] = useState(false);
const [isDisabled, setIsDisabled] = useState(!isEditable || bulkEditMode);
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData);
let setFieldValueRef: (field: string, value: any, shouldValidate?: boolean) => void = null;
if(inForm)
if (inForm)
{
const {setFieldValue} = useFormikContext();
setFieldValueRef = setFieldValue;
@ -239,9 +245,11 @@ function DynamicSelect({tableName, processName, fieldName, fieldLabel, inForm, i
bulkEditSwitchChangeHandler(fieldName, newSwitchValue);
};
// console.log(`default value: ${JSON.stringify(defaultValue)}`);
const autocomplete = (
<Autocomplete
id={fieldName}
id={overrideId ?? fieldName}
sx={{background: isDisabled ? "#f0f2f5!important" : "initial"}}
open={open}
fullWidth
@ -291,6 +299,8 @@ function DynamicSelect({tableName, processName, fieldName, fieldLabel, inForm, i
disabled={isDisabled}
multiple={isMultiple}
disableCloseOnSelect={isMultiple}
limitTags={5}
slotProps={{popper: {className: "DynamicSelectPopper"}}}
renderInput={(params) => (
<TextField
{...params}

View File

@ -54,6 +54,7 @@ interface Props
closeModalHandler?: (event: object, reason: string) => void;
defaultValues: { [key: string]: string };
disabledFields: { [key: string]: boolean } | string[];
isDuplicate?: boolean;
}
EntityForm.defaultProps = {
@ -63,6 +64,7 @@ EntityForm.defaultProps = {
closeModalHandler: null,
defaultValues: {},
disabledFields: {},
isDuplicate: false
};
function EntityForm(props: Props): JSX.Element
@ -133,6 +135,15 @@ function EntityForm(props: Props): JSX.Element
for (let i = 0; i < formFields.length; i++)
{
formData.formFields[formFields[i].name] = formFields[i];
if (formFields[i].possibleValueProps)
{
formFields[i].possibleValueProps.otherValues = formFields[i].possibleValueProps.otherValues ?? new Map<string, any>();
Object.keys(formFields).forEach((otherKey) =>
{
formFields[i].possibleValueProps.otherValues.set(otherKey, values[otherKey]);
});
}
}
if (!Object.keys(formFields).length)
@ -164,24 +175,30 @@ function EntityForm(props: Props): JSX.Element
fieldArray.push(fieldMetaData);
});
/////////////////////////////////////////////////////////////////////////////////
// if doing an edit, fetch the record and pre-populate the form values from it //
/////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
// if doing an edit or duplicate, fetch the record and pre-populate the form values from it //
//////////////////////////////////////////////////////////////////////////////////////////////
let record: QRecord = null;
let defaultDisplayValues = new Map<string, string>();
if (props.id !== null)
{
record = await qController.get(tableName, props.id);
setRecord(record);
setFormTitle(`Edit ${tableMetaData?.label}: ${record?.recordLabel}`);
const titleVerb = props.isDuplicate ? "Duplicate" : "Edit";
setFormTitle(`${titleVerb} ${tableMetaData?.label}: ${record?.recordLabel}`);
if (!props.isModal)
{
setPageHeader(`Edit ${tableMetaData?.label}: ${record?.recordLabel}`);
setPageHeader(`${titleVerb} ${tableMetaData?.label}: ${record?.recordLabel}`);
}
tableMetaData.fields.forEach((fieldMetaData, key) =>
{
if (props.isDuplicate && fieldMetaData.name == tableMetaData.primaryKeyField)
{
return;
}
initialValues[key] = record.values.get(key);
});
@ -206,15 +223,6 @@ function EntityForm(props: Props): JSX.Element
setPageHeader(`Creating New ${tableMetaData?.label}`);
}
if (!tableMetaData.capabilities.has(Capability.TABLE_INSERT))
{
setNotAllowedError("Records may not be created in this table");
}
else if (!tableMetaData.insertPermission)
{
setNotAllowedError(`You do not have permission to create ${tableMetaData.label} records`);
}
////////////////////////////////////////////////////////////////////////////////////////////////
// if default values were supplied for a new record, then populate initialValues, for formik. //
////////////////////////////////////////////////////////////////////////////////////////////////
@ -245,6 +253,32 @@ function EntityForm(props: Props): JSX.Element
}
}
//////////////////////////////////////
// check capabilities & permissions //
//////////////////////////////////////
if (props.isDuplicate || !props.id)
{
if (!tableMetaData.capabilities.has(Capability.TABLE_INSERT))
{
setNotAllowedError("Records may not be created in this table");
}
else if (!tableMetaData.insertPermission)
{
setNotAllowedError(`You do not have permission to create ${tableMetaData.label} records`);
}
}
else
{
if (!tableMetaData.capabilities.has(Capability.TABLE_UPDATE))
{
setNotAllowedError("Records may not be edited in this table");
}
else if (!tableMetaData.editPermission)
{
setNotAllowedError(`You do not have permission to edit ${tableMetaData.label} records`);
}
}
/////////////////////////////////////////////////////////////////////
// make sure all initialValues are properly formatted for the form //
/////////////////////////////////////////////////////////////////////
@ -307,11 +341,11 @@ function EntityForm(props: Props): JSX.Element
const fieldName = section.fieldNames[j];
const field = tableMetaData.fields.get(fieldName);
////////////////////////////////////////////////////////////////////////////////////////////
// if id !== null - means we're on the edit screen -- show all fields on the edit screen. //
// || (or) we're on the insert screen in which case, only show editable fields. //
////////////////////////////////////////////////////////////////////////////////////////////
if (props.id !== null || field.isEditable)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if id !== null (and we're not duplicating) - means we're on the edit screen -- show all fields on the edit screen. //
// || (or) we're on the insert screen in which case, only show editable fields. //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if ((props.id !== null && !props.isDuplicate) || field.isEditable)
{
sectionDynamicFormFields.push(dynamicFormFields[fieldName]);
}
@ -359,7 +393,12 @@ function EntityForm(props: Props): JSX.Element
// but if the user used the anchors on the page, this doesn't effectively cancel... //
// what we have here pushed a new history entry (I think?), so could be better //
///////////////////////////////////////////////////////////////////////////////////////
if (props.id !== null)
if (props.id !== null && props.isDuplicate)
{
const path = `${location.pathname.replace(/\/duplicate$/, "")}`;
navigate(path, {replace: true});
}
else if (props.id !== null)
{
const path = `${location.pathname.replace(/\/edit$/, "")}`;
navigate(path, {replace: true});
@ -419,8 +458,9 @@ function EntityForm(props: Props): JSX.Element
}
}
if (props.id !== null)
if (props.id !== null && !props.isDuplicate)
{
// todo - audit that it's a dupe
await qController
.update(tableName, props.id, values)
.then((record) =>
@ -464,7 +504,9 @@ function EntityForm(props: Props): JSX.Element
}
else
{
const path = location.pathname.replace(/create$/, record.values.get(tableMetaData.primaryKeyField));
const path = props.isDuplicate ?
location.pathname.replace(new RegExp(`/${props.id}/duplicate$`), "/" + record.values.get(tableMetaData.primaryKeyField))
: location.pathname.replace(/create$/, record.values.get(tableMetaData.primaryKeyField));
navigate(path, {state: {createSuccess: true}});
}
})
@ -472,8 +514,9 @@ function EntityForm(props: Props): JSX.Element
{
if(error.message.toLowerCase().startsWith("warning"))
{
const path = location.pathname.replace(/create$/, record.values.get(tableMetaData.primaryKeyField));
navigate(path);
const path = props.isDuplicate ?
location.pathname.replace(new RegExp(`/${props.id}/duplicate$`), "/" + record.values.get(tableMetaData.primaryKeyField))
: location.pathname.replace(/create$/, record.values.get(tableMetaData.primaryKeyField));
navigate(path, {state: {createSuccess: true, warning: error.message}});
}
else