/* * 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 {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {Checkbox, FormControlLabel, Radio, Tooltip} from "@mui/material"; import Autocomplete from "@mui/material/Autocomplete"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import Icon from "@mui/material/Icon"; import RadioGroup from "@mui/material/RadioGroup"; import TextField from "@mui/material/TextField"; import {useFormikContext} from "formik"; import colors from "qqq/assets/theme/base/colors"; import QDynamicFormField from "qqq/components/forms/DynamicFormField"; import DynamicFormUtils from "qqq/components/forms/DynamicFormUtils"; import {BulkLoadField, FileDescription} from "qqq/models/processes/BulkLoadModels"; import Client from "qqq/utils/qqq/Client"; import React, {useEffect, useState} from "react"; interface BulkLoadMappingFieldProps { bulkLoadField: BulkLoadField, isRequired: boolean, removeFieldCallback?: () => void, fileDescription: FileDescription, forceParentUpdate?: () => void, } const xIconButtonSX = { border: `1px solid ${colors.grayLines.main} !important`, borderRadius: "0.5rem", textTransform: "none", fontSize: "1rem", fontWeight: "400", width: "30px", minWidth: "30px", height: "2rem", minHeight: "2rem", paddingLeft: 0, paddingRight: 0, marginRight: "0.5rem", marginTop: "0.5rem", color: colors.error.main, "&:hover": {color: colors.error.main}, "&:focus": {color: colors.error.main}, "&:focus:not(:hover)": {color: colors.error.main}, }; const qController = Client.getInstance(); /*************************************************************************** ** row for a single field on the bulk load mapping screen. ***************************************************************************/ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, removeFieldCallback, fileDescription, forceParentUpdate}: BulkLoadMappingFieldProps): JSX.Element { const columnNames = fileDescription.getColumnNames(); const [valueType, setValueType] = useState(bulkLoadField.valueType); const [selectedColumn, setSelectedColumn] = useState({label: columnNames[bulkLoadField.columnIndex], value: bulkLoadField.columnIndex}); const [selectedColumnInputValue, setSelectedColumnInputValue] = useState(columnNames[bulkLoadField.columnIndex]); const [doingInitialLoadOfPossibleValue, setDoingInitialLoadOfPossibleValue] = useState(false); const [everDidInitialLoadOfPossibleValue, setEverDidInitialLoadOfPossibleValue] = useState(false); const [possibleValueInitialDisplayValue, setPossibleValueInitialDisplayValue] = useState(null as string); const fieldMetaData = new QFieldMetaData(bulkLoadField.field); const dynamicField = DynamicFormUtils.getDynamicField(fieldMetaData); const dynamicFieldInObject: any = {}; dynamicFieldInObject[fieldMetaData["name"]] = dynamicField; DynamicFormUtils.addPossibleValueProps(dynamicFieldInObject, [fieldMetaData], bulkLoadField.tableStructure.tableName, null, null); ///////////////////////////////////////////////////////////////////////////////////// // deal with dynamically loading the initial default value for a possible value... // ///////////////////////////////////////////////////////////////////////////////////// let actuallyDoingInitialLoadOfPossibleValue = doingInitialLoadOfPossibleValue; if (dynamicField.possibleValueProps && bulkLoadField.defaultValue && !doingInitialLoadOfPossibleValue && !everDidInitialLoadOfPossibleValue) { actuallyDoingInitialLoadOfPossibleValue = true; setDoingInitialLoadOfPossibleValue(true); setEverDidInitialLoadOfPossibleValue(true); (async () => { try { const possibleValues = await qController.possibleValues(bulkLoadField.tableStructure.tableName, null, fieldMetaData.name, null, [bulkLoadField.defaultValue], undefined, null, "filter"); if (possibleValues && possibleValues.length > 0) { setPossibleValueInitialDisplayValue(possibleValues[0].label); } else { setPossibleValueInitialDisplayValue(null); } } catch (e) { console.log(`Error loading possible value: ${e}`); } actuallyDoingInitialLoadOfPossibleValue = false; setDoingInitialLoadOfPossibleValue(false); })(); } if (dynamicField.possibleValueProps && possibleValueInitialDisplayValue) { dynamicField.possibleValueProps.initialDisplayValue = possibleValueInitialDisplayValue; } ////////////////////////////////////////////////////// // build array of options for the columns drop down // // don't allow duplicates // ////////////////////////////////////////////////////// const columnOptions: { value: number, label: string }[] = []; const usedLabels: { [label: string]: boolean } = {}; for (let i = 0; i < columnNames.length; i++) { const label = columnNames[i]; if (!usedLabels[label]) { columnOptions.push({label: label, value: i}); usedLabels[label] = true; } } ////////////////////////////////////////////////////////////////////// // try to pick up changes in the hasHeaderRow toggle from way above // ////////////////////////////////////////////////////////////////////// if (bulkLoadField.columnIndex != null && bulkLoadField.columnIndex != undefined && selectedColumn.label && columnNames[bulkLoadField.columnIndex] != selectedColumn.label) { setSelectedColumn({label: columnNames[bulkLoadField.columnIndex], value: bulkLoadField.columnIndex}); setSelectedColumnInputValue(columnNames[bulkLoadField.columnIndex]); } const mainFontSize = "0.875rem"; const smallerFontSize = "0.75rem"; ///////////////////////////////////////////////////////////////////////////////////////////// // some field types get their value from formik. // // so for a pre-populated value, do an on-load useEffect, that'll set the value in formik. // ///////////////////////////////////////////////////////////////////////////////////////////// const {setFieldValue} = useFormikContext(); useEffect(() => { if (valueType == "defaultValue") { setFieldValue(`${bulkLoadField.field.name}.defaultValue`, bulkLoadField.defaultValue); } }, []); /*************************************************************************** ** ***************************************************************************/ function columnChanged(event: any, newValue: any, reason: string) { setSelectedColumn(newValue); setSelectedColumnInputValue(newValue == null ? "" : newValue.label); bulkLoadField.columnIndex = newValue == null ? null : newValue.value; if (fileDescription.hasHeaderRow) { bulkLoadField.headerName = newValue == null ? null : newValue.label; } bulkLoadField.error = null; bulkLoadField.warning = null; forceParentUpdate && forceParentUpdate(); } /*************************************************************************** ** ***************************************************************************/ function defaultValueChanged(newValue: any) { setFieldValue(`${bulkLoadField.field.name}.defaultValue`, newValue); bulkLoadField.defaultValue = newValue; bulkLoadField.error = null; bulkLoadField.warning = null; forceParentUpdate && forceParentUpdate(); } /*************************************************************************** ** ***************************************************************************/ function valueTypeChanged(isColumn: boolean) { const newValueType = isColumn ? "column" : "defaultValue"; bulkLoadField.valueType = newValueType; setValueType(newValueType); bulkLoadField.error = null; bulkLoadField.warning = null; forceParentUpdate && forceParentUpdate(); } /*************************************************************************** ** ***************************************************************************/ function mapValuesChanged(value: boolean) { bulkLoadField.doValueMapping = value; forceParentUpdate && forceParentUpdate(); } /*************************************************************************** ** ***************************************************************************/ function changeSelectedColumnInputValue(e: React.ChangeEvent) { setSelectedColumnInputValue(e.target.value); } return ( { (!isRequired) && } {bulkLoadField.getQualifiedLabel()} valueTypeChanged(checked)} />} label={"File column"} sx={{minWidth: "140px", whiteSpace: "nowrap"}} /> { valueType == "column" && ( changeSelectedColumnInputValue(e)} fullWidth variant="outlined" autoComplete="off" type="search" InputProps={{...params.InputProps}} sx={{"& .MuiOutlinedInput-root": {borderRadius: "0.75rem"}}} />)} fullWidth options={columnOptions} multiple={false} defaultValue={selectedColumn} value={selectedColumn} inputValue={selectedColumnInputValue} onChange={columnChanged} getOptionLabel={(option) => typeof (option) == "string" ? option : (option?.label ?? "")} isOptionEqualToValue={(option, value) => option == null && value == null || option.value == value.value} renderOption={(props, option, state) => (
  • {option?.label ?? ""}
  • )} sx={{"& .MuiOutlinedInput-root": {padding: "0"}}} />
    }
    valueTypeChanged(!checked)} />} label={"Default value"} sx={{minWidth: "140px", whiteSpace: "nowrap"}} /> { valueType == "defaultValue" && actuallyDoingInitialLoadOfPossibleValue && Loading... } { valueType == "defaultValue" && !actuallyDoingInitialLoadOfPossibleValue && }
    { bulkLoadField.warning && {bulkLoadField.warning} } { bulkLoadField.error && {bulkLoadField.error} }
    { valueType == "column" && <> mapValuesChanged(checked)} />} label={"Map values"} sx={{minWidth: "140px", whiteSpace: "nowrap"}} /> Preview Values: {(fileDescription.getPreviewValues(selectedColumn?.value) ?? [""]).join(", ")} }
    ); }