mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 21:00:45 +00:00
CE-1955 Add warning about duplicate column headers, and un-selection of dupes if switching from no-header-row mode to header-row mode
This commit is contained in:
@ -131,11 +131,18 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
// build array of options for the columns drop down //
|
// build array of options for the columns drop down //
|
||||||
|
// don't allow duplicates //
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
const columnOptions: { value: number, label: string }[] = [];
|
const columnOptions: { value: number, label: string }[] = [];
|
||||||
|
const usedLabels: {[label: string]: boolean} = {};
|
||||||
for (let i = 0; i < columnNames.length; i++)
|
for (let i = 0; i < columnNames.length; i++)
|
||||||
{
|
{
|
||||||
columnOptions.push({label: columnNames[i], value: i});
|
const label = columnNames[i];
|
||||||
|
if(!usedLabels[label])
|
||||||
|
{
|
||||||
|
columnOptions.push({label: label, value: i});
|
||||||
|
usedLabels[label] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
@ -180,6 +187,7 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
|||||||
}
|
}
|
||||||
|
|
||||||
bulkLoadField.error = null;
|
bulkLoadField.error = null;
|
||||||
|
bulkLoadField.warning = null;
|
||||||
forceParentUpdate && forceParentUpdate();
|
forceParentUpdate && forceParentUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,6 +200,7 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
|||||||
setFieldValue(`${bulkLoadField.field.name}.defaultValue`, newValue);
|
setFieldValue(`${bulkLoadField.field.name}.defaultValue`, newValue);
|
||||||
bulkLoadField.defaultValue = newValue;
|
bulkLoadField.defaultValue = newValue;
|
||||||
bulkLoadField.error = null;
|
bulkLoadField.error = null;
|
||||||
|
bulkLoadField.warning = null;
|
||||||
forceParentUpdate && forceParentUpdate();
|
forceParentUpdate && forceParentUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,6 +214,7 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
|||||||
bulkLoadField.valueType = newValueType;
|
bulkLoadField.valueType = newValueType;
|
||||||
setValueType(newValueType);
|
setValueType(newValueType);
|
||||||
bulkLoadField.error = null;
|
bulkLoadField.error = null;
|
||||||
|
bulkLoadField.warning = null;
|
||||||
forceParentUpdate && forceParentUpdate();
|
forceParentUpdate && forceParentUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,6 +297,12 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
|||||||
}
|
}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
{
|
||||||
|
bulkLoadField.warning &&
|
||||||
|
<Box fontSize={smallerFontSize} color={colors.warning.main} ml="145px" className="bulkLoadFieldError">
|
||||||
|
{bulkLoadField.warning}
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
{
|
{
|
||||||
bulkLoadField.error &&
|
bulkLoadField.error &&
|
||||||
<Box fontSize={smallerFontSize} color={colors.error.main} ml="145px" className="bulkLoadFieldError">
|
<Box fontSize={smallerFontSize} color={colors.error.main} ml="145px" className="bulkLoadFieldError">
|
||||||
|
@ -298,6 +298,9 @@ function BulkLoadMappingHeader({fileDescription, fileName, bulkLoadMapping, fiel
|
|||||||
{
|
{
|
||||||
bulkLoadMapping.hasHeaderRow = newValue;
|
bulkLoadMapping.hasHeaderRow = newValue;
|
||||||
fileDescription.hasHeaderRow = newValue;
|
fileDescription.hasHeaderRow = newValue;
|
||||||
|
|
||||||
|
bulkLoadMapping.handleChangeToHasHeaderRow(newValue, fileDescription);
|
||||||
|
|
||||||
fieldErrors.hasHeaderRow = null;
|
fieldErrors.hasHeaderRow = null;
|
||||||
forceParentUpdate();
|
forceParentUpdate();
|
||||||
}
|
}
|
||||||
@ -470,19 +473,29 @@ function BulkLoadMappingFilePreview({fileDescription, bulkLoadMapping}: BulkLoad
|
|||||||
{
|
{
|
||||||
const fields = bulkLoadMapping.getFieldsForColumnIndex(index);
|
const fields = bulkLoadMapping.getFieldsForColumnIndex(index);
|
||||||
const count = fields.length;
|
const count = fields.length;
|
||||||
|
|
||||||
|
let dupeWarning = <></>
|
||||||
|
if(fileDescription.hasHeaderRow && fileDescription.duplicateHeaderIndexes[index])
|
||||||
|
{
|
||||||
|
dupeWarning = <Tooltip title="This column header is a duplicate. Only the first occurrance of it will be used." placement="top" enterDelay={500}>
|
||||||
|
<Icon color="warning" sx={{p: "0.125rem", mr: "0.25rem"}}>warning</Icon>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
|
||||||
return (<td key={letter} style={{textAlign: "center", color: getHeaderColor(count), cursor: getCursor(count)}}>
|
return (<td key={letter} style={{textAlign: "center", color: getHeaderColor(count), cursor: getCursor(count)}}>
|
||||||
<>
|
<>
|
||||||
{
|
{
|
||||||
count > 0 &&
|
count > 0 &&
|
||||||
<Tooltip title={getColumnTooltip(fields)} placement="top" enterDelay={500}>
|
<Tooltip title={getColumnTooltip(fields)} placement="top" enterDelay={500}>
|
||||||
<Box>
|
<Box>
|
||||||
|
{dupeWarning}
|
||||||
{letter}
|
{letter}
|
||||||
<Badge badgeContent={count} variant={"standard"} color="secondary" sx={{marginTop: ".75rem"}}><Icon></Icon></Badge>
|
<Badge badgeContent={count} variant={"standard"} color="secondary" sx={{marginTop: ".75rem"}}><Icon></Icon></Badge>
|
||||||
</Box>
|
</Box>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
count == 0 && <Box>{letter}</Box>
|
count == 0 && <Box>{dupeWarning}{letter}</Box>
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
</td>);
|
</td>);
|
||||||
|
@ -43,6 +43,7 @@ export class BulkLoadField
|
|||||||
wideLayoutIndexPath: number[] = [];
|
wideLayoutIndexPath: number[] = [];
|
||||||
|
|
||||||
error: string = null;
|
error: string = null;
|
||||||
|
warning: string = null;
|
||||||
|
|
||||||
key: string;
|
key: string;
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ export class BulkLoadField
|
|||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
**
|
**
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
constructor(field: QFieldMetaData, tableStructure: BulkLoadTableStructure, valueType: ValueType = "column", columnIndex?: number, headerName?: string, defaultValue?: any, doValueMapping?: boolean, wideLayoutIndexPath: number[] = [])
|
constructor(field: QFieldMetaData, tableStructure: BulkLoadTableStructure, valueType: ValueType = "column", columnIndex?: number, headerName?: string, defaultValue?: any, doValueMapping?: boolean, wideLayoutIndexPath: number[] = [], error: string = null, warning: string = null)
|
||||||
{
|
{
|
||||||
this.field = field;
|
this.field = field;
|
||||||
this.tableStructure = tableStructure;
|
this.tableStructure = tableStructure;
|
||||||
@ -60,6 +61,8 @@ export class BulkLoadField
|
|||||||
this.defaultValue = defaultValue;
|
this.defaultValue = defaultValue;
|
||||||
this.doValueMapping = doValueMapping;
|
this.doValueMapping = doValueMapping;
|
||||||
this.wideLayoutIndexPath = wideLayoutIndexPath;
|
this.wideLayoutIndexPath = wideLayoutIndexPath;
|
||||||
|
this.error = error;
|
||||||
|
this.warning = warning;
|
||||||
this.key = new Date().getTime().toString();
|
this.key = new Date().getTime().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +72,7 @@ export class BulkLoadField
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
public static clone(source: BulkLoadField): BulkLoadField
|
public static clone(source: BulkLoadField): BulkLoadField
|
||||||
{
|
{
|
||||||
return (new BulkLoadField(source.field, source.tableStructure, source.valueType, source.columnIndex, source.headerName, source.defaultValue, source.doValueMapping, source.wideLayoutIndexPath));
|
return (new BulkLoadField(source.field, source.tableStructure, source.valueType, source.columnIndex, source.headerName, source.defaultValue, source.doValueMapping, source.wideLayoutIndexPath, source.error, source.warning));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -431,7 +434,7 @@ export class BulkLoadMapping
|
|||||||
{
|
{
|
||||||
if (existingField.getQualifiedName() == bulkLoadField.getQualifiedName())
|
if (existingField.getQualifiedName() == bulkLoadField.getQualifiedName())
|
||||||
{
|
{
|
||||||
const thisIndex = existingField.wideLayoutIndexPath[0]
|
const thisIndex = existingField.wideLayoutIndexPath[0];
|
||||||
if (thisIndex != null && thisIndex != undefined && thisIndex > maxIndex)
|
if (thisIndex != null && thisIndex != undefined && thisIndex > maxIndex)
|
||||||
{
|
{
|
||||||
maxIndex = thisIndex;
|
maxIndex = thisIndex;
|
||||||
@ -506,7 +509,7 @@ export class BulkLoadMapping
|
|||||||
namesWhereOneWideLayoutIndexHasBeenFound[name] = true;
|
namesWhereOneWideLayoutIndexHasBeenFound[name] = true;
|
||||||
const newField = BulkLoadField.clone(existingField);
|
const newField = BulkLoadField.clone(existingField);
|
||||||
newField.wideLayoutIndexPath = [];
|
newField.wideLayoutIndexPath = [];
|
||||||
newAdditionalFields.push(newField)
|
newAdditionalFields.push(newField);
|
||||||
anyChanges = true;
|
anyChanges = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,7 +518,7 @@ export class BulkLoadMapping
|
|||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
// else, non-wide-path fields, just get added as-is //
|
// else, non-wide-path fields, just get added as-is //
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
newAdditionalFields.push(existingField)
|
newAdditionalFields.push(existingField);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -531,7 +534,7 @@ export class BulkLoadMapping
|
|||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
// fields from main table come over as-is //
|
// fields from main table come over as-is //
|
||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
newAdditionalFields.push(existingField)
|
newAdditionalFields.push(existingField);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -540,7 +543,7 @@ export class BulkLoadMapping
|
|||||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
const newField = BulkLoadField.clone(existingField);
|
const newField = BulkLoadField.clone(existingField);
|
||||||
newField.wideLayoutIndexPath = [0];
|
newField.wideLayoutIndexPath = [0];
|
||||||
newAdditionalFields.push(newField)
|
newAdditionalFields.push(newField);
|
||||||
anyChanges = true;
|
anyChanges = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -564,7 +567,7 @@ export class BulkLoadMapping
|
|||||||
|
|
||||||
for (let field of [...this.requiredFields, ...this.additionalFields])
|
for (let field of [...this.requiredFields, ...this.additionalFields])
|
||||||
{
|
{
|
||||||
if(field.valueType == "column" && field.columnIndex == i)
|
if (field.valueType == "column" && field.columnIndex == i)
|
||||||
{
|
{
|
||||||
rs.push(field);
|
rs.push(field);
|
||||||
}
|
}
|
||||||
@ -572,6 +575,68 @@ export class BulkLoadMapping
|
|||||||
|
|
||||||
return (rs);
|
return (rs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public handleChangeToHasHeaderRow(newValue: any, fileDescription: FileDescription)
|
||||||
|
{
|
||||||
|
const newRequiredFields: BulkLoadField[] = [];
|
||||||
|
let anyChangesToRequiredFields = false;
|
||||||
|
|
||||||
|
const newAdditionalFields: BulkLoadField[] = [];
|
||||||
|
let anyChangesToAdditionalFields = false;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if we're switching to have header-rows enabled, then make sure that no columns w/ duplicated headers are selected //
|
||||||
|
// strategy to do this: build new lists of both required & additional fields - and track if we had to change any //
|
||||||
|
// column indexes (set to null) - add a warning to them, and only replace the arrays if there were changes. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if (newValue)
|
||||||
|
{
|
||||||
|
for (let field of this.requiredFields)
|
||||||
|
{
|
||||||
|
if (field.valueType == "column" && fileDescription.duplicateHeaderIndexes[field.columnIndex])
|
||||||
|
{
|
||||||
|
const newField = BulkLoadField.clone(field);
|
||||||
|
newField.columnIndex = null;
|
||||||
|
newField.warning = "This field was assigned to a column with a duplicated header"
|
||||||
|
newRequiredFields.push(newField);
|
||||||
|
anyChangesToRequiredFields = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newRequiredFields.push(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let field of this.additionalFields)
|
||||||
|
{
|
||||||
|
if (field.valueType == "column" && fileDescription.duplicateHeaderIndexes[field.columnIndex])
|
||||||
|
{
|
||||||
|
const newField = BulkLoadField.clone(field);
|
||||||
|
newField.columnIndex = null;
|
||||||
|
newField.warning = "This field was assigned to a column with a duplicated header"
|
||||||
|
newAdditionalFields.push(newField);
|
||||||
|
anyChangesToAdditionalFields = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newAdditionalFields.push(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anyChangesToRequiredFields)
|
||||||
|
{
|
||||||
|
this.requiredFields = newRequiredFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anyChangesToAdditionalFields)
|
||||||
|
{
|
||||||
|
this.additionalFields = newAdditionalFields;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -584,6 +649,8 @@ export class FileDescription
|
|||||||
headerLetters: string[];
|
headerLetters: string[];
|
||||||
bodyValuesPreview: string[][];
|
bodyValuesPreview: string[][];
|
||||||
|
|
||||||
|
duplicateHeaderIndexes: boolean[];
|
||||||
|
|
||||||
// todo - just get this from the profile always - it's not part of the file per-se
|
// todo - just get this from the profile always - it's not part of the file per-se
|
||||||
hasHeaderRow: boolean = true;
|
hasHeaderRow: boolean = true;
|
||||||
|
|
||||||
@ -595,6 +662,18 @@ export class FileDescription
|
|||||||
this.headerValues = headerValues;
|
this.headerValues = headerValues;
|
||||||
this.headerLetters = headerLetters;
|
this.headerLetters = headerLetters;
|
||||||
this.bodyValuesPreview = bodyValuesPreview;
|
this.bodyValuesPreview = bodyValuesPreview;
|
||||||
|
|
||||||
|
this.duplicateHeaderIndexes = [];
|
||||||
|
const usedLabels: { [label: string]: boolean } = {};
|
||||||
|
for (let i = 0; i < headerValues.length; i++)
|
||||||
|
{
|
||||||
|
const label = headerValues[i];
|
||||||
|
if (usedLabels[label])
|
||||||
|
{
|
||||||
|
this.duplicateHeaderIndexes[i] = true;
|
||||||
|
}
|
||||||
|
usedLabels[label] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -635,7 +714,7 @@ export class FileDescription
|
|||||||
|
|
||||||
function getTypedValue(value: any): string
|
function getTypedValue(value: any): string
|
||||||
{
|
{
|
||||||
if(value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -694,13 +773,13 @@ export class FileDescription
|
|||||||
|
|
||||||
if (!this.hasHeaderRow)
|
if (!this.hasHeaderRow)
|
||||||
{
|
{
|
||||||
const typedValue = getTypedValue(this.headerValues[columnIndex])
|
const typedValue = getTypedValue(this.headerValues[columnIndex]);
|
||||||
valueArray.push(typedValue == null ? "" : `${typedValue}`);
|
valueArray.push(typedValue == null ? "" : `${typedValue}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let value of this.bodyValuesPreview[columnIndex])
|
for (let value of this.bodyValuesPreview[columnIndex])
|
||||||
{
|
{
|
||||||
const typedValue = getTypedValue(value)
|
const typedValue = getTypedValue(value);
|
||||||
valueArray.push(typedValue == null ? "" : `${typedValue}`);
|
valueArray.push(typedValue == null ? "" : `${typedValue}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user