mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-20 06:10:43 +00:00
CE-1955 - Initial checkin of qfmd support for bulk-load
This commit is contained in:
322
src/qqq/components/processes/BulkLoadFileMappingFields.tsx
Normal file
322
src/qqq/components/processes/BulkLoadFileMappingFields.tsx
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import Box from "@mui/material/Box";
|
||||
import Icon from "@mui/material/Icon";
|
||||
import colors from "qqq/assets/theme/base/colors";
|
||||
import QHierarchyAutoComplete, {Group, Option} from "qqq/components/misc/QHierarchyAutoComplete";
|
||||
import BulkLoadFileMappingField from "qqq/components/processes/BulkLoadFileMappingField";
|
||||
import {BulkLoadField, BulkLoadMapping, FileDescription} from "qqq/models/processes/BulkLoadModels";
|
||||
import React, {useEffect, useReducer, useState} from "react";
|
||||
|
||||
interface BulkLoadMappingFieldsProps
|
||||
{
|
||||
bulkLoadMapping: BulkLoadMapping,
|
||||
fileDescription: FileDescription,
|
||||
forceParentUpdate?: () => void,
|
||||
}
|
||||
|
||||
|
||||
const ADD_SINGLE_FIELD_TOOLTIP = "Click to add this field to your mapping.";
|
||||
const ADD_MANY_FIELD_TOOLTIP = "Click to add this field to your mapping as many times as you need.";
|
||||
const ALREADY_ADDED_FIELD_TOOLTIP = "This field has already been added to your mapping.";
|
||||
|
||||
/***************************************************************************
|
||||
** The section of the bulk load mapping screen with all the fields.
|
||||
***************************************************************************/
|
||||
export default function BulkLoadFileMappingFields({bulkLoadMapping, fileDescription, forceParentUpdate}: BulkLoadMappingFieldsProps): JSX.Element
|
||||
{
|
||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||
|
||||
const [forceRerender, setForceRerender] = useState(0);
|
||||
|
||||
////////////////////////////////////////////
|
||||
// build list of fields that can be added //
|
||||
////////////////////////////////////////////
|
||||
const [addFieldsGroup, setAddFieldsGroup] = useState({
|
||||
label: bulkLoadMapping.tablesByPath[""]?.label,
|
||||
value: "mainTable",
|
||||
options: [],
|
||||
subGroups: []
|
||||
} as Group);
|
||||
// const [addFieldsToggleStates, setAddFieldsToggleStates] = useState({} as { [name: string]: boolean });
|
||||
const [addFieldsDisableStates, setAddFieldsDisableStates] = useState({} as { [name: string]: boolean });
|
||||
const [tooltips, setTooltips] = useState({} as { [name: string]: string });
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const newDisableStates: { [name: string]: boolean } = {};
|
||||
const newTooltips: { [name: string]: string } = {};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// do the unused fields array first, as we've got some use-case where i think a field from //
|
||||
// suggested mappings (or profiles?) are in this list, even though they shouldn't be? //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
for (let field of bulkLoadMapping.unusedFields)
|
||||
{
|
||||
const qualifiedName = field.getQualifiedName();
|
||||
newTooltips[qualifiedName] = field.isMany() ? ADD_MANY_FIELD_TOOLTIP : ADD_SINGLE_FIELD_TOOLTIP;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// then do all the required & additional fields //
|
||||
//////////////////////////////////////////////////
|
||||
for (let field of [...(bulkLoadMapping.requiredFields ?? []), ...(bulkLoadMapping.additionalFields ?? [])])
|
||||
{
|
||||
const qualifiedName = field.getQualifiedName();
|
||||
|
||||
if (bulkLoadMapping.layout == "WIDE" && field.isMany())
|
||||
{
|
||||
newDisableStates[qualifiedName] = false;
|
||||
newTooltips[qualifiedName] = ADD_MANY_FIELD_TOOLTIP;
|
||||
}
|
||||
else
|
||||
{
|
||||
newDisableStates[qualifiedName] = true;
|
||||
newTooltips[qualifiedName] = ALREADY_ADDED_FIELD_TOOLTIP;
|
||||
}
|
||||
}
|
||||
|
||||
setAddFieldsDisableStates(newDisableStates);
|
||||
setTooltips(newTooltips);
|
||||
|
||||
}, [bulkLoadMapping]);
|
||||
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// initialize this structure on first render //
|
||||
///////////////////////////////////////////////
|
||||
if (addFieldsGroup.options.length == 0)
|
||||
{
|
||||
for (let qualifiedFieldName in bulkLoadMapping.fieldsByTablePrefix[""])
|
||||
{
|
||||
const bulkLoadField = bulkLoadMapping.fieldsByTablePrefix[""][qualifiedFieldName];
|
||||
const field = bulkLoadField.field;
|
||||
addFieldsGroup.options.push({label: field.label, value: field.name, bulkLoadField: bulkLoadField});
|
||||
}
|
||||
|
||||
for (let prefix in bulkLoadMapping.fieldsByTablePrefix)
|
||||
{
|
||||
if (prefix == "")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const associationOptions: Option[] = [];
|
||||
const tableStructure = bulkLoadMapping.tablesByPath[prefix];
|
||||
addFieldsGroup.subGroups.push({label: tableStructure.label, value: tableStructure.associationPath, options: associationOptions});
|
||||
|
||||
for (let qualifiedFieldName in bulkLoadMapping.fieldsByTablePrefix[prefix])
|
||||
{
|
||||
const bulkLoadField = bulkLoadMapping.fieldsByTablePrefix[prefix][qualifiedFieldName];
|
||||
const field = bulkLoadField.field;
|
||||
associationOptions.push({label: field.label, value: field.name, bulkLoadField: bulkLoadField});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
function removeField(bulkLoadField: BulkLoadField)
|
||||
{
|
||||
// addFieldsToggleStates[bulkLoadField.getQualifiedName()] = false;
|
||||
// setAddFieldsToggleStates(Object.assign({}, addFieldsToggleStates));
|
||||
|
||||
addFieldsDisableStates[bulkLoadField.getQualifiedName()] = false;
|
||||
setAddFieldsDisableStates(Object.assign({}, addFieldsDisableStates));
|
||||
|
||||
if (bulkLoadMapping.layout == "WIDE" && bulkLoadField.isMany())
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ok, you can add more - so don't disable and don't change the tooltip //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
else
|
||||
{
|
||||
tooltips[bulkLoadField.getQualifiedName()] = ADD_SINGLE_FIELD_TOOLTIP;
|
||||
}
|
||||
|
||||
bulkLoadMapping.removeField(bulkLoadField);
|
||||
forceUpdate();
|
||||
forceParentUpdate();
|
||||
setForceRerender(forceRerender + 1);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
function handleToggleField(option: Option, group: Group, newValue: boolean)
|
||||
{
|
||||
const fieldKey = group.value == "mainTable" ? option.value : group.value + "." + option.value;
|
||||
|
||||
// addFieldsToggleStates[fieldKey] = newValue;
|
||||
// setAddFieldsToggleStates(Object.assign({}, addFieldsToggleStates));
|
||||
|
||||
addFieldsDisableStates[fieldKey] = newValue;
|
||||
setAddFieldsDisableStates(Object.assign({}, addFieldsDisableStates));
|
||||
|
||||
const bulkLoadField = bulkLoadMapping.fields[fieldKey];
|
||||
if (bulkLoadField)
|
||||
{
|
||||
if (newValue)
|
||||
{
|
||||
bulkLoadMapping.addField(bulkLoadField);
|
||||
}
|
||||
else
|
||||
{
|
||||
bulkLoadMapping.removeField(bulkLoadField);
|
||||
}
|
||||
|
||||
forceUpdate();
|
||||
forceParentUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
function handleAddField(option: Option, group: Group)
|
||||
{
|
||||
const fieldKey = group.value == "mainTable" ? option.value : group.value + "." + option.value;
|
||||
|
||||
const bulkLoadField = bulkLoadMapping.fields[fieldKey];
|
||||
if (bulkLoadField)
|
||||
{
|
||||
bulkLoadMapping.addField(bulkLoadField);
|
||||
|
||||
// addFieldsDisableStates[fieldKey] = true;
|
||||
// setAddFieldsDisableStates(Object.assign({}, addFieldsDisableStates));
|
||||
|
||||
if (bulkLoadMapping.layout == "WIDE" && bulkLoadField.isMany())
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ok, you can add more - so don't disable and don't change the tooltip //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
else
|
||||
{
|
||||
addFieldsDisableStates[fieldKey] = true;
|
||||
setAddFieldsDisableStates(Object.assign({}, addFieldsDisableStates));
|
||||
|
||||
tooltips[fieldKey] = ALREADY_ADDED_FIELD_TOOLTIP;
|
||||
}
|
||||
|
||||
forceUpdate();
|
||||
forceParentUpdate();
|
||||
|
||||
document.getElementById("addFieldsButton")?.scrollIntoView();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
function copyWideField(bulkLoadField: BulkLoadField)
|
||||
{
|
||||
bulkLoadMapping.addField(bulkLoadField);
|
||||
forceUpdate();
|
||||
//? //? forceParentUpdate();
|
||||
//? setForceRerender(forceRerender + 1);
|
||||
}
|
||||
|
||||
|
||||
let buttonBackground = "none";
|
||||
let buttonBorder = colors.grayLines.main;
|
||||
let buttonColor = colors.gray.main;
|
||||
|
||||
const addFieldMenuButtonStyles = {
|
||||
borderRadius: "0.75rem",
|
||||
border: `1px solid ${buttonBorder}`,
|
||||
color: buttonColor,
|
||||
textTransform: "none",
|
||||
fontWeight: 500,
|
||||
fontSize: "0.875rem",
|
||||
p: "0.5rem",
|
||||
backgroundColor: buttonBackground,
|
||||
"&:focus:not(:hover)": {
|
||||
color: buttonColor,
|
||||
backgroundColor: buttonBackground,
|
||||
},
|
||||
"&:hover": {
|
||||
color: buttonColor,
|
||||
backgroundColor: buttonBackground,
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<h5>Required Fields</h5>
|
||||
<Box pl={"1rem"}>
|
||||
{
|
||||
bulkLoadMapping.requiredFields.length == 0 &&
|
||||
<i style={{fontSize: "0.875rem"}}>There are no required fields in this table.</i>
|
||||
}
|
||||
{bulkLoadMapping.requiredFields.map((bulkLoadField) => (
|
||||
<BulkLoadFileMappingField
|
||||
fileDescription={fileDescription}
|
||||
key={bulkLoadField.getKey()}
|
||||
bulkLoadField={bulkLoadField}
|
||||
isRequired={true}
|
||||
forceParentUpdate={forceParentUpdate}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
<Box mt="1rem">
|
||||
<h5>Additional Fields</h5>
|
||||
<Box pl={"1rem"}>
|
||||
{bulkLoadMapping.additionalFields.map((bulkLoadField) => (
|
||||
<BulkLoadFileMappingField
|
||||
fileDescription={fileDescription}
|
||||
key={bulkLoadField.getKey()}
|
||||
bulkLoadField={bulkLoadField}
|
||||
isRequired={false}
|
||||
removeFieldCallback={() => removeField(bulkLoadField)}
|
||||
forceParentUpdate={forceParentUpdate}
|
||||
/>
|
||||
))}
|
||||
|
||||
<Box display="flex" pt="1rem" pl="12.5rem">
|
||||
<QHierarchyAutoComplete
|
||||
idPrefix="addFieldAutocomplete"
|
||||
defaultGroup={addFieldsGroup}
|
||||
menuDirection="up"
|
||||
buttonProps={{id: "addFieldsButton", sx: addFieldMenuButtonStyles}}
|
||||
buttonChildren={<><Icon sx={{mr: "0.5rem"}}>add</Icon> Add Fields <Icon sx={{ml: "0.5rem"}}>keyboard_arrow_down</Icon></>}
|
||||
isModeSelectOne
|
||||
keepOpenAfterSelectOne
|
||||
handleSelectedOption={handleAddField}
|
||||
forceRerender={forceRerender}
|
||||
disabledStates={addFieldsDisableStates}
|
||||
tooltips={tooltips}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user