/* * 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 . */ 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, isBulkEdit?: boolean } 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, isBulkEdit}: BulkLoadMappingFieldsProps): JSX.Element { const [, forceUpdate] = useReducer((x) => x + 1, 0); const [forceHierarchyAutoCompleteRerender, setForceHierarchyAutoCompleteRerender] = 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); setForceHierarchyAutoCompleteRerender(forceHierarchyAutoCompleteRerender + 1); }, [bulkLoadMapping, bulkLoadMapping.layout]); /////////////////////////////////////////////// // 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) { 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(); setForceHierarchyAutoCompleteRerender(forceHierarchyAutoCompleteRerender + 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(); } } 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 ( <> {isBulkEdit ?
Key Fields
:
Required Fields
} { bulkLoadMapping.requiredFields.length == 0 && ( isBulkEdit ? Select table key fields to continue. : There are no required fields in this table. ) } {bulkLoadMapping.requiredFields.map((bulkLoadField) => ( ))} {isBulkEdit ?
Fields To Update
:
Additional Fields
} {bulkLoadMapping.additionalFields.map((bulkLoadField) => ( removeField(bulkLoadField)} forceParentUpdate={forceParentUpdate} isBulkEdit={isBulkEdit} /> ))} add Add Fields keyboard_arrow_down} isModeSelectOne keepOpenAfterSelectOne handleSelectedOption={handleAddField} forceRerender={forceHierarchyAutoCompleteRerender} disabledStates={addFieldsDisableStates} tooltips={tooltips} />
); }