mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 21:00:45 +00:00
CE-1955 Bulk load bugs & usability improvements
This commit is contained in:
@ -21,9 +21,10 @@
|
|||||||
|
|
||||||
|
|
||||||
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
||||||
import {Checkbox, FormControlLabel, Radio} from "@mui/material";
|
import {Checkbox, FormControlLabel, Radio, Tooltip} from "@mui/material";
|
||||||
import Autocomplete from "@mui/material/Autocomplete";
|
import Autocomplete from "@mui/material/Autocomplete";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
import Icon from "@mui/material/Icon";
|
import Icon from "@mui/material/Icon";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import RadioGroup from "@mui/material/RadioGroup";
|
import RadioGroup from "@mui/material/RadioGroup";
|
||||||
@ -45,6 +46,27 @@ interface BulkLoadMappingFieldProps
|
|||||||
forceParentUpdate?: () => void,
|
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();
|
const qController = Client.getInstance();
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
@ -212,7 +234,9 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
|||||||
|
|
||||||
<Box display="flex" alignItems="flex-start">
|
<Box display="flex" alignItems="flex-start">
|
||||||
{
|
{
|
||||||
(!isRequired) && <IconButton onClick={() => removeFieldCallback()} sx={{pt: "0.75rem"}}><Icon fontSize="small">remove_circle</Icon></IconButton>
|
(!isRequired) && <Tooltip placement="bottom" title="Remove this field from your mapping.">
|
||||||
|
<Button sx={xIconButtonSX} onClick={() => removeFieldCallback()}><Icon>clear</Icon></Button>
|
||||||
|
</Tooltip>
|
||||||
}
|
}
|
||||||
<Box pt="0.625rem">
|
<Box pt="0.625rem">
|
||||||
{bulkLoadField.getQualifiedLabel()}
|
{bulkLoadField.getQualifiedLabel()}
|
||||||
@ -265,7 +289,7 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
|||||||
</Box>
|
</Box>
|
||||||
{
|
{
|
||||||
bulkLoadField.error &&
|
bulkLoadField.error &&
|
||||||
<Box fontSize={smallerFontSize} color={colors.error.main} ml="145px">
|
<Box fontSize={smallerFontSize} color={colors.error.main} ml="145px" className="bulkLoadFieldError">
|
||||||
{bulkLoadField.error}
|
{bulkLoadField.error}
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ export default function BulkLoadFileMappingFields({bulkLoadMapping, fileDescript
|
|||||||
{
|
{
|
||||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||||
|
|
||||||
const [forceRerender, setForceRerender] = useState(0);
|
const [forceHierarchyAutoCompleteRerender, setForceHierarchyAutoCompleteRerender] = useState(0);
|
||||||
|
|
||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
// build list of fields that can be added //
|
// build list of fields that can be added //
|
||||||
@ -98,8 +98,9 @@ export default function BulkLoadFileMappingFields({bulkLoadMapping, fileDescript
|
|||||||
|
|
||||||
setAddFieldsDisableStates(newDisableStates);
|
setAddFieldsDisableStates(newDisableStates);
|
||||||
setTooltips(newTooltips);
|
setTooltips(newTooltips);
|
||||||
|
setForceHierarchyAutoCompleteRerender(forceHierarchyAutoCompleteRerender + 1);
|
||||||
|
|
||||||
}, [bulkLoadMapping]);
|
}, [bulkLoadMapping, bulkLoadMapping.layout]);
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////
|
///////////////////////////////////////////////
|
||||||
@ -140,9 +141,6 @@ export default function BulkLoadFileMappingFields({bulkLoadMapping, fileDescript
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
function removeField(bulkLoadField: BulkLoadField)
|
function removeField(bulkLoadField: BulkLoadField)
|
||||||
{
|
{
|
||||||
// addFieldsToggleStates[bulkLoadField.getQualifiedName()] = false;
|
|
||||||
// setAddFieldsToggleStates(Object.assign({}, addFieldsToggleStates));
|
|
||||||
|
|
||||||
addFieldsDisableStates[bulkLoadField.getQualifiedName()] = false;
|
addFieldsDisableStates[bulkLoadField.getQualifiedName()] = false;
|
||||||
setAddFieldsDisableStates(Object.assign({}, addFieldsDisableStates));
|
setAddFieldsDisableStates(Object.assign({}, addFieldsDisableStates));
|
||||||
|
|
||||||
@ -160,7 +158,7 @@ export default function BulkLoadFileMappingFields({bulkLoadMapping, fileDescript
|
|||||||
bulkLoadMapping.removeField(bulkLoadField);
|
bulkLoadMapping.removeField(bulkLoadField);
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
forceParentUpdate();
|
forceParentUpdate();
|
||||||
setForceRerender(forceRerender + 1);
|
setForceHierarchyAutoCompleteRerender(forceHierarchyAutoCompleteRerender + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
@ -297,7 +295,7 @@ export default function BulkLoadFileMappingFields({bulkLoadMapping, fileDescript
|
|||||||
isModeSelectOne
|
isModeSelectOne
|
||||||
keepOpenAfterSelectOne
|
keepOpenAfterSelectOne
|
||||||
handleSelectedOption={handleAddField}
|
handleSelectedOption={handleAddField}
|
||||||
forceRerender={forceRerender}
|
forceRerender={forceHierarchyAutoCompleteRerender}
|
||||||
disabledStates={addFieldsDisableStates}
|
disabledStates={addFieldsDisableStates}
|
||||||
tooltips={tooltips}
|
tooltips={tooltips}
|
||||||
/>
|
/>
|
||||||
|
@ -26,10 +26,12 @@ import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstan
|
|||||||
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
|
||||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||||
|
import {Badge, Icon} from "@mui/material";
|
||||||
import Autocomplete from "@mui/material/Autocomplete";
|
import Autocomplete from "@mui/material/Autocomplete";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
|
import Tooltip from "@mui/material/Tooltip/Tooltip";
|
||||||
import {useFormikContext} from "formik";
|
import {useFormikContext} from "formik";
|
||||||
import {DynamicFormFieldLabel} from "qqq/components/forms/DynamicForm";
|
import {DynamicFormFieldLabel} from "qqq/components/forms/DynamicForm";
|
||||||
import QDynamicFormField from "qqq/components/forms/DynamicFormField";
|
import QDynamicFormField from "qqq/components/forms/DynamicFormField";
|
||||||
@ -126,6 +128,14 @@ const BulkLoadFileMappingForm = forwardRef(({processValues, tableMetaData, metaD
|
|||||||
}
|
}
|
||||||
setFieldErrors(fieldErrors);
|
setFieldErrors(fieldErrors);
|
||||||
|
|
||||||
|
if(haveProfileErrors)
|
||||||
|
{
|
||||||
|
setTimeout(() =>
|
||||||
|
{
|
||||||
|
document.querySelector(".bulkLoadFieldError")?.scrollIntoView({behavior: "smooth", block: "center", inline: "center"});
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
|
||||||
return {maySubmit: !haveProfileErrors && !haveLocalErrors, values};
|
return {maySubmit: !haveProfileErrors && !haveLocalErrors, values};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -224,7 +234,11 @@ const BulkLoadFileMappingForm = forwardRef(({processValues, tableMetaData, metaD
|
|||||||
<BulkLoadFileMappingFields
|
<BulkLoadFileMappingFields
|
||||||
bulkLoadMapping={bulkLoadMapping}
|
bulkLoadMapping={bulkLoadMapping}
|
||||||
fileDescription={fileDescription}
|
fileDescription={fileDescription}
|
||||||
forceParentUpdate={() => forceUpdate()}
|
forceParentUpdate={() =>
|
||||||
|
{
|
||||||
|
setRerenderHeader(rerenderHeader + 1);
|
||||||
|
forceUpdate();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -293,7 +307,7 @@ function BulkLoadMappingHeader({fileDescription, fileName, bulkLoadMapping, fiel
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
function layoutChanged(event: any, newValue: any)
|
function layoutChanged(event: any, newValue: any)
|
||||||
{
|
{
|
||||||
bulkLoadMapping.layout = newValue ? newValue.id : null;
|
bulkLoadMapping.switchLayout(newValue ? newValue.id : null);
|
||||||
fieldErrors.layout = null;
|
fieldErrors.layout = null;
|
||||||
forceParentUpdate();
|
forceParentUpdate();
|
||||||
}
|
}
|
||||||
@ -322,7 +336,7 @@ function BulkLoadMappingHeader({fileDescription, fileName, bulkLoadMapping, fiel
|
|||||||
<h5>File Details</h5>
|
<h5>File Details</h5>
|
||||||
<Box ml="1rem">
|
<Box ml="1rem">
|
||||||
<ProcessViewForm fields={viewFields} values={viewValues} columns={2} />
|
<ProcessViewForm fields={viewFields} values={viewValues} columns={2} />
|
||||||
<BulkLoadMappingFilePreview fileDescription={fileDescription} />
|
<BulkLoadMappingFilePreview fileDescription={fileDescription} bulkLoadMapping={bulkLoadMapping} />
|
||||||
<Grid container pt="1rem">
|
<Grid container pt="1rem">
|
||||||
<Grid item xs={12} md={6}>
|
<Grid item xs={12} md={6}>
|
||||||
<DynamicFormFieldLabel name={hasHeaderRowFormField.name} label={`${hasHeaderRowFormField.label} *`} />
|
<DynamicFormFieldLabel name={hasHeaderRowFormField.name} label={`${hasHeaderRowFormField.label} *`} />
|
||||||
@ -347,6 +361,7 @@ function BulkLoadMappingHeader({fileDescription, fileName, bulkLoadMapping, fiel
|
|||||||
getOptionLabel={(option) => typeof (option) == "string" ? option : (option?.label ?? "")}
|
getOptionLabel={(option) => typeof (option) == "string" ? option : (option?.label ?? "")}
|
||||||
isOptionEqualToValue={(option, value) => option == null && value == null || option.id == value.id}
|
isOptionEqualToValue={(option, value) => option == null && value == null || option.id == value.id}
|
||||||
renderOption={(props, option, state) => (<li {...props}>{option?.label ?? ""}</li>)}
|
renderOption={(props, option, state) => (<li {...props}>{option?.label ?? ""}</li>)}
|
||||||
|
disableClearable
|
||||||
sx={{"& .MuiOutlinedInput-root": {padding: "0"}}}
|
sx={{"& .MuiOutlinedInput-root": {padding: "0"}}}
|
||||||
/>
|
/>
|
||||||
{
|
{
|
||||||
@ -366,13 +381,14 @@ function BulkLoadMappingHeader({fileDescription, fileName, bulkLoadMapping, fiel
|
|||||||
|
|
||||||
interface BulkLoadMappingFilePreviewProps
|
interface BulkLoadMappingFilePreviewProps
|
||||||
{
|
{
|
||||||
fileDescription: FileDescription;
|
fileDescription: FileDescription,
|
||||||
|
bulkLoadMapping?: BulkLoadMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
** private subcomponent - the file-preview section of the bulk load file mapping screen.
|
** private subcomponent - the file-preview section of the bulk load file mapping screen.
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
function BulkLoadMappingFilePreview({fileDescription}: BulkLoadMappingFilePreviewProps): JSX.Element
|
function BulkLoadMappingFilePreview({fileDescription, bulkLoadMapping}: BulkLoadMappingFilePreviewProps): JSX.Element
|
||||||
{
|
{
|
||||||
const rows: number[] = [];
|
const rows: number[] = [];
|
||||||
for (let i = 0; i < fileDescription.bodyValuesPreview[0].length; i++)
|
for (let i = 0; i < fileDescription.bodyValuesPreview[0].length; i++)
|
||||||
@ -380,25 +396,135 @@ function BulkLoadMappingFilePreview({fileDescription}: BulkLoadMappingFilePrevie
|
|||||||
rows.push(i);
|
rows.push(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
function getValue(i: number, j: number)
|
||||||
|
{
|
||||||
|
const value = fileDescription.bodyValuesPreview[j][i];
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// this was useful at one point in time when we had an object coming back for xlsx files with many different data types //
|
||||||
|
// we'd see a .string attribute, which would have the value we'd want to show. not using it now, but keep in case //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// @ts-ignore
|
||||||
|
if (value && value.string)
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
return (value.string);
|
||||||
|
}
|
||||||
|
return `${value}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
function getHeaderColor(count: number): string
|
||||||
|
{
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
return "blue";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "black";
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
function getCursor(count: number): string
|
||||||
|
{
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
return "pointer";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
function getColumnTooltip(fields: BulkLoadField[])
|
||||||
|
{
|
||||||
|
return (<Box>
|
||||||
|
This column is mapped to the field{fields.length == 1 ? "" : "s"}:
|
||||||
|
<ul style={{marginLeft: "1rem"}}>
|
||||||
|
{fields.map((field, i) => <li key={i}>{field.getQualifiedLabel()}</li>)}
|
||||||
|
</ul>
|
||||||
|
</Box>);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{"& table, & td": {border: "1px solid black", borderCollapse: "collapse", padding: "0 0.25rem", fontSize: "0.875rem", whiteSpace: "nowrap"}}}>
|
<Box sx={{"& table, & td": {border: "1px solid black", borderCollapse: "collapse", padding: "0 0.25rem", fontSize: "0.875rem", whiteSpace: "nowrap"}}}>
|
||||||
<Box sx={{width: "100%", overflow: "auto"}}>
|
<Box sx={{width: "100%", overflow: "auto"}}>
|
||||||
<table cellSpacing="0" width="100%">
|
<table cellSpacing="0" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr style={{backgroundColor: "#d3d3d3"}}>
|
<tr style={{backgroundColor: "#d3d3d3", height: "1.75rem"}}>
|
||||||
<td></td>
|
<td></td>
|
||||||
{fileDescription.headerLetters.map((letter) => <td key={letter} style={{textAlign: "center"}}>{letter}</td>)}
|
{fileDescription.headerLetters.map((letter, index) =>
|
||||||
|
{
|
||||||
|
const fields = bulkLoadMapping.getFieldsForColumnIndex(index);
|
||||||
|
const count = fields.length;
|
||||||
|
return (<td key={letter} style={{textAlign: "center", color: getHeaderColor(count), cursor: getCursor(count)}}>
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
count > 0 &&
|
||||||
|
<Tooltip title={getColumnTooltip(fields)} placement="top" enterDelay={500}>
|
||||||
|
<Box>
|
||||||
|
{letter}
|
||||||
|
<Badge badgeContent={count} variant={"standard"} color="secondary" sx={{marginTop: ".75rem"}}><Icon></Icon></Badge>
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
count == 0 && <Box>{letter}</Box>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
</td>);
|
||||||
|
})}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style={{backgroundColor: "#d3d3d3", textAlign: "center"}}>1</td>
|
<td style={{backgroundColor: "#d3d3d3", textAlign: "center"}}>1</td>
|
||||||
{fileDescription.headerValues.map((value) => <td key={value} style={{backgroundColor: fileDescription.hasHeaderRow ? "#ebebeb" : ""}}>{value}</td>)}
|
|
||||||
|
{fileDescription.headerValues.map((value, index) =>
|
||||||
|
{
|
||||||
|
const fields = bulkLoadMapping.getFieldsForColumnIndex(index);
|
||||||
|
const count = fields.length;
|
||||||
|
const tdStyle = {color: getHeaderColor(count), cursor: getCursor(count), backgroundColor: ""};
|
||||||
|
|
||||||
|
if(fileDescription.hasHeaderRow)
|
||||||
|
{
|
||||||
|
tdStyle.backgroundColor = "#ebebeb";
|
||||||
|
|
||||||
|
if(count > 0)
|
||||||
|
{
|
||||||
|
return <td key={value} style={tdStyle}>
|
||||||
|
<Tooltip title={getColumnTooltip(fields)} placement="top" enterDelay={500}><Box>{value}</Box></Tooltip>
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return <td key={value} style={tdStyle}>{value}</td>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return <td key={value} style={tdStyle}>{value}</td>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)}
|
||||||
</tr>
|
</tr>
|
||||||
{rows.map((i) => (
|
{rows.map((i) => (
|
||||||
<tr key={i}>
|
<tr key={i}>
|
||||||
<td style={{backgroundColor: "#d3d3d3", textAlign: "center"}}>{i + 2}</td>
|
<td style={{backgroundColor: "#d3d3d3", textAlign: "center"}}>{i + 2}</td>
|
||||||
{fileDescription.headerLetters.map((letter, j) => <td key={j}>{fileDescription.bodyValuesPreview[j][i]}</td>)}
|
{fileDescription.headerLetters.map((letter, j) => <td key={j}>{getValue(i, j)}</td>)}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
||||||
|
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
|
||||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||||
|
|
||||||
export type ValueType = "defaultValue" | "column";
|
export type ValueType = "defaultValue" | "column";
|
||||||
@ -422,17 +423,22 @@ export class BulkLoadMapping
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
index = 0;
|
///////////////////////////////////////////////
|
||||||
///////////////////////////////////////////////////////////
|
// find the max index for this field already //
|
||||||
// count how many copies of this field there are already //
|
///////////////////////////////////////////////
|
||||||
///////////////////////////////////////////////////////////
|
let maxIndex = -1;
|
||||||
for (let existingField of [...this.requiredFields, ...this.additionalFields])
|
for (let existingField of [...this.requiredFields, ...this.additionalFields])
|
||||||
{
|
{
|
||||||
if (existingField.getQualifiedName() == bulkLoadField.getQualifiedName())
|
if (existingField.getQualifiedName() == bulkLoadField.getQualifiedName())
|
||||||
{
|
{
|
||||||
index++;
|
const thisIndex = existingField.wideLayoutIndexPath[0]
|
||||||
|
if (thisIndex != null && thisIndex != undefined && thisIndex > maxIndex)
|
||||||
|
{
|
||||||
|
maxIndex = thisIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
index = maxIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cloneField = BulkLoadField.clone(bulkLoadField);
|
const cloneField = BulkLoadField.clone(bulkLoadField);
|
||||||
@ -455,7 +461,7 @@ export class BulkLoadMapping
|
|||||||
const newAdditionalFields: BulkLoadField[] = [];
|
const newAdditionalFields: BulkLoadField[] = [];
|
||||||
for (let bulkLoadField of this.additionalFields)
|
for (let bulkLoadField of this.additionalFields)
|
||||||
{
|
{
|
||||||
if (bulkLoadField.getQualifiedName() != toRemove.getQualifiedName())
|
if (bulkLoadField.getQualifiedNameWithWideSuffix() != toRemove.getQualifiedNameWithWideSuffix())
|
||||||
{
|
{
|
||||||
newAdditionalFields.push(bulkLoadField);
|
newAdditionalFields.push(bulkLoadField);
|
||||||
}
|
}
|
||||||
@ -463,6 +469,107 @@ export class BulkLoadMapping
|
|||||||
|
|
||||||
this.additionalFields = newAdditionalFields;
|
this.additionalFields = newAdditionalFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public switchLayout(newLayout: string): void
|
||||||
|
{
|
||||||
|
const newAdditionalFields: BulkLoadField[] = [];
|
||||||
|
let anyChanges = false;
|
||||||
|
|
||||||
|
if ("WIDE" != newLayout)
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if going to a layout other than WIDE, make sure there aren't any fields with a wideLayoutIndexPath //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const namesWhereOneWideLayoutIndexHasBeenFound: { [name: string]: boolean } = {};
|
||||||
|
for (let existingField of this.additionalFields)
|
||||||
|
{
|
||||||
|
if (existingField.wideLayoutIndexPath.length > 0)
|
||||||
|
{
|
||||||
|
const name = existingField.getQualifiedName();
|
||||||
|
if (namesWhereOneWideLayoutIndexHasBeenFound[name])
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// in this case, we're on like the 2nd or 3rd instance of, say, Line Item: SKU - so - just discard it. //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
anyChanges = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// else, this is the 1st instance of, say, Line Item: SKU - so mark that we've found it - and keep this field //
|
||||||
|
// (that is, put it in the new array), but with no index path //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
namesWhereOneWideLayoutIndexHasBeenFound[name] = true;
|
||||||
|
const newField = BulkLoadField.clone(existingField);
|
||||||
|
newField.wideLayoutIndexPath = [];
|
||||||
|
newAdditionalFields.push(newField)
|
||||||
|
anyChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// else, non-wide-path fields, just get added as-is //
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
newAdditionalFields.push(existingField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if going to WIDE layout, then any field from a child table needs a wide-layout-index-path //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
for (let existingField of this.additionalFields)
|
||||||
|
{
|
||||||
|
if (existingField.tableStructure.isMain)
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// fields from main table come over as-is //
|
||||||
|
////////////////////////////////////////////
|
||||||
|
newAdditionalFields.push(existingField)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// fields from child tables get a wideLayoutIndexPath (and we're assuming just 1 for each) //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
const newField = BulkLoadField.clone(existingField);
|
||||||
|
newField.wideLayoutIndexPath = [0];
|
||||||
|
newAdditionalFields.push(newField)
|
||||||
|
anyChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anyChanges)
|
||||||
|
{
|
||||||
|
this.additionalFields = newAdditionalFields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public getFieldsForColumnIndex(i: number): BulkLoadField[]
|
||||||
|
{
|
||||||
|
const rs: BulkLoadField[] = [];
|
||||||
|
|
||||||
|
for (let field of [...this.requiredFields, ...this.additionalFields])
|
||||||
|
{
|
||||||
|
if(field.valueType == "column" && field.columnIndex == i)
|
||||||
|
{
|
||||||
|
rs.push(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (rs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -517,21 +624,85 @@ export class FileDescription
|
|||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
**
|
**
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
public getPreviewValues(columnIndex: number): string[]
|
public getPreviewValues(columnIndex: number, fieldType?: QFieldType): string[]
|
||||||
{
|
{
|
||||||
if (columnIndex == undefined)
|
if (columnIndex == undefined)
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasHeaderRow)
|
function getTypedValue(value: any): string
|
||||||
{
|
{
|
||||||
return (this.bodyValuesPreview[columnIndex]);
|
if(value == null)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// this was useful at one point in time when we had an object coming back for xlsx files with many different data types //
|
||||||
|
// we'd see a .string attribute, which would have the value we'd want to show. not using it now, but keep in case //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if (value && value.string)
|
||||||
|
{
|
||||||
|
switch (fieldType)
|
||||||
|
{
|
||||||
|
case QFieldType.BOOLEAN:
|
||||||
|
{
|
||||||
|
return value.bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QFieldType.STRING:
|
||||||
|
case QFieldType.TEXT:
|
||||||
|
case QFieldType.HTML:
|
||||||
|
case QFieldType.PASSWORD:
|
||||||
|
{
|
||||||
|
return value.string;
|
||||||
|
}
|
||||||
|
|
||||||
|
case QFieldType.INTEGER:
|
||||||
|
case QFieldType.LONG:
|
||||||
|
{
|
||||||
|
return value.integer;
|
||||||
|
}
|
||||||
|
case QFieldType.DECIMAL:
|
||||||
|
{
|
||||||
|
return value.decimal;
|
||||||
|
}
|
||||||
|
case QFieldType.DATE:
|
||||||
|
{
|
||||||
|
return value.date;
|
||||||
|
}
|
||||||
|
case QFieldType.TIME:
|
||||||
|
{
|
||||||
|
return value.time;
|
||||||
|
}
|
||||||
|
case QFieldType.DATE_TIME:
|
||||||
|
{
|
||||||
|
return value.dateTime;
|
||||||
|
}
|
||||||
|
case QFieldType.BLOB:
|
||||||
|
return ""; // !!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (`${value}`);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
const valueArray: string[] = [];
|
||||||
|
|
||||||
|
if (!this.hasHeaderRow)
|
||||||
{
|
{
|
||||||
return ([this.headerValues[columnIndex], ...this.bodyValuesPreview[columnIndex]]);
|
const typedValue = getTypedValue(this.headerValues[columnIndex])
|
||||||
|
valueArray.push(typedValue == null ? "" : `${typedValue}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let value of this.bodyValuesPreview[columnIndex])
|
||||||
|
{
|
||||||
|
const typedValue = getTypedValue(value)
|
||||||
|
valueArray.push(typedValue == null ? "" : `${typedValue}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (valueArray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user