Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDE-79 Pairing suggestions in template creation are expecting an header that does not exist #57

Merged
merged 3 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 106 additions & 67 deletions lib/components/steps/template/TemplateStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ import { PlusIcon, PairIcon } from '../../../icons/index.tsx';
import { vars } from '../../../theme/variables.ts';
const { gray100, gray500, gray600 } = vars


function TemplateStep({ onCloseModal }: { onCloseModal: () => void }) {
const [visibleRows, setVisibleRows] = React.useState<Option[]>([{
id: '',
label: '',
group: '',
content: []
}]);
const { headerIndexes, collections, datasetMapping } = useDataContext();
const { headerIndexes, collections, datasetMapping, datasetMappingHeader } = useDataContext();
const { getUnmappedVariableNames, searchCustomDictionaryFields, updateDatasetMappingRowTemplate, onClose } = useServicesContext();
const collectionKeys = Object.keys(collections);
const defaultCollection = collectionKeys.length > 0 ? collectionKeys[0] : '';
Expand All @@ -38,6 +39,8 @@ function TemplateStep({ onCloseModal }: { onCloseModal: () => void }) {
const [selectableCollections, setSelectableCollections] = useState<SelectableCollection[]>([]);
const [selectedOptionsMap, setSelectedOptionsMap] = useState<{ [id: string]: Option }>({});
const [createdCustomDictionaryFields, setCreatedCustomDictionaryFields] = useState<{ [id: string]: Option }>({});
const [visiblePairingSuggestions, setVisiblePairingSuggestions] = useState<boolean[]>([false]);
const [acceptedSuggestions, setAcceptedSuggestions] = useState<string[]>([]);

useEffect(() => {
const initialSelectedCollections = Object.keys(collections).map(key => ({
Expand All @@ -49,6 +52,32 @@ function TemplateStep({ onCloseModal }: { onCloseModal: () => void }) {
setSelectableCollections([...initialSelectedCollections, getCustomDictionaryFieldSelectableCollection()]);
}, [collections, defaultCollection]);

useEffect(() => {
const updateState = async () => {
setSelectedOptionsMap(prevSelectedOptionsMap => {
const updatedOptionsMap = Object.keys(prevSelectedOptionsMap).reduce((acc, key) => {
const label = prevSelectedOptionsMap[key].label;
const found = Object.values(datasetMapping).some(arr => arr.includes(label));
if (found) {
acc[key] = prevSelectedOptionsMap[key];
}
return acc;
}, {} as { [id: string]: Option });
return updatedOptionsMap;
});

setVisibleRows(prevVisibleRows => {
const labelsToRemove = Object.values(datasetMapping).reduce((acc, val) => acc.concat(val), []);
const updatedVisibleRows = prevVisibleRows.length > 1
? prevVisibleRows.filter(row => labelsToRemove.includes(row.label))
: prevVisibleRows;
return updatedVisibleRows;
});
};

updateState();
}, [datasetMapping, datasetMappingHeader, headerIndexes]);

const handleCollectionSelect = (selectedCollection: SelectableCollection) => {
setSelectableCollections(prevCollections =>
prevCollections.map(collection => {
Expand Down Expand Up @@ -87,12 +116,18 @@ function TemplateStep({ onCloseModal }: { onCloseModal: () => void }) {
);

const beforeHandleSelection = async (option: Option, newIsSelectedState: boolean, rowIndex: number) => {
const variableName = getAbbreviationFromOption(option, headerIndexes)
if (!option) {
console.error("No option provided");
return;
}
const variableName = getAbbreviationFromOption(option, headerIndexes);
setVisibleRows(prevState => {
const newArray = [...prevState];
newArray[rowIndex] = option;
return newArray;
});
setVisiblePairingSuggestions(prevState => prevState.map((_, index) => index === rowIndex));
setAcceptedSuggestions(prevState => prevState.includes(option.id) ? prevState.filter(id => id !== option.id) : prevState);
handleSelection(variableName || option.label, option, newIsSelectedState, rowIndex);
};

Expand Down Expand Up @@ -120,10 +155,10 @@ function TemplateStep({ onCloseModal }: { onCloseModal: () => void }) {
}
}
const mappedIds = Object.keys(selectedOptionsMap).map(item => item.toLowerCase().replace(':', '_'));
const filteredSuggestions = aggregatedPairingSuggestions.filter(suggestion => !mappedIds.includes(suggestion.id));
const filteredSuggestions = aggregatedPairingSuggestions.filter(suggestion => !(mappedIds.includes(suggestion.id) || suggestion.id === option.id));
updateAvailableSuggestions(variableName, filteredSuggestions);
} else if (option && !newIsSelectedState) {
updateAvailableSuggestions(variableName, []);
// updateAvailableSuggestions(variableName, []);
updateDatasetMappingRowTemplate(variableName, [], rowIndex);

} else {
Expand All @@ -146,16 +181,20 @@ function TemplateStep({ onCloseModal }: { onCloseModal: () => void }) {
};

const handlePairingSuggestion = (variableName: string, suggestion: Option, selectedColumn: string | null) => {
markSuggestionAsProcessed(variableName, suggestion.id);
if (selectedColumn !== null) {
const emptyObjectIndex = visibleRows.findIndex(row => row.id === "" && row.label === "");
const newIndex = replaceOrAddSuggestion(suggestion, emptyObjectIndex);

setAcceptedSuggestions((prevAcceptedSuggestion) => [...prevAcceptedSuggestion, suggestion.id]);
setVisiblePairingSuggestions(prevState => {
return [...prevState, false];
});
setSelectedOptionsMap(prevOptionsMap => ({
...prevOptionsMap,
[suggestion.id]: suggestion,
}));
updateDatasetMappingRowTemplate(selectedColumn || suggestion.label, suggestion.content, newIndex);
} else {
markSuggestionAsProcessed(variableName, suggestion.id);
}
};

Expand All @@ -182,17 +221,17 @@ function TemplateStep({ onCloseModal }: { onCloseModal: () => void }) {
content: []
}];
});
setVisiblePairingSuggestions(prevState => {
return [...prevState.map(() => false), false];
});
};

// Check if there is any element with an empty id and label in the visibleRows array
const hasEmptyElement = visibleRows.some(row => row.id === '' && row.label === '');

// Determine the index of the first visible row with pairing suggestions
const firstRowWithSuggestions = visibleRows.find(row => hasPairingSuggestions(row.label));

const searchText = "Search in " + (selectableCollections.length === 1 ? `${selectableCollections[0].name} collection` : 'multiple collections');
const visibleRowIds = new Set(visibleRows.map(row => String(row.id)));

const hasOneEmptyItem = (
visibleRows.reduce((count, row) => count + (row.id === '' && row.label === '' ? 1 : 0), 0) === 1 ||
Object.keys(selectedOptionsMap).length === 0
);
const hasNoItems = (visibleRows.length===1 && Object.keys(selectedOptionsMap).length === 0);
return (
<Box display="flex" flexDirection="column" justifyContent="space-between" height={1}>
<ModalHeightWrapper height="15rem">
Expand Down Expand Up @@ -224,78 +263,78 @@ function TemplateStep({ onCloseModal }: { onCloseModal: () => void }) {
variableName={row.label}
onCustomDictionaryFieldCreation={(option, newIsSelectedState) => onCustomDictionaryFieldCreation(row.label, option, newIsSelectedState, rowIndex)}
/>
{visiblePairingSuggestions[rowIndex] && hasPairingSuggestions(row.label) && selectedOptionsMap[row.id] && (
<Box
display="flex"
sx={{
boxSizing: 'border-box',
columnGap: '1.5rem',
flexWrap: 'wrap',
padding: '1.5rem 0',
borderBottom: '0.0625rem solid #ECEDEE',
}}
>
<Accordion>
<AccordionSummary>
<PairIcon />
<Typography sx={{
fontSize: '0.75rem',
color: '#4F5359',
fontWeight: 500,
lineHeight: '150%'
}}>Pairing suggestions</Typography>
<PairingTooltip />
</AccordionSummary>
<AccordionDetails>
<Box pl='2.5625rem'>
{getPairingSuggestions(row.label).filter((suggestion) => !acceptedSuggestions.includes(suggestion.id)).map((suggestion) => {
const headerOptions = getUnmappedVariableNames().map((label, index) => ({
label,
index
}));
const rowContent = optionDetailsToCdeDetails(suggestion.content);
const abbreviation = getAbbreviationFromOption(suggestion, headerIndexes);
const title = getTitleFromOption(suggestion, headerIndexes);

return (
<PairingSuggestion
key={suggestion.id}
onChange={(selectedColumn) => handlePairingSuggestion(row.label, suggestion, selectedColumn)}
headerOptions={headerOptions}
label={abbreviation}
title={title}
rowContent={rowContent}
/>
);
})}
</Box>
</AccordionDetails>
</Accordion>
</Box>
)}
</Fragment>
))
}

{!hasEmptyElement && firstRowWithSuggestions && (
<Box
display="flex"
sx={{
boxSizing: 'border-box',
columnGap: '1.5rem',
flexWrap: 'wrap',
padding: '1.5rem 0',
borderBottom: '0.0625rem solid #ECEDEE',
}}
>
<Accordion>
<AccordionSummary>
<PairIcon />
<Typography sx={{
fontSize: '0.75rem',
color: '#4F5359',
fontWeight: 500,
lineHeight: '150%'
}}>Pairing suggestions</Typography>
<PairingTooltip />
</AccordionSummary>
<AccordionDetails>
<Box pl='2.5625rem'>
{getPairingSuggestions(firstRowWithSuggestions.label).filter(suggestion => !visibleRowIds.has(String(suggestion.id))).map((suggestion) => {
const headerOptions = getUnmappedVariableNames().map((label, index) => ({
label,
index
}));
const rowContent = optionDetailsToCdeDetails(suggestion.content);
const abbreviation = getAbbreviationFromOption(suggestion, headerIndexes);
const title = getTitleFromOption(suggestion, headerIndexes);

return (
<PairingSuggestion
key={suggestion.id}
onChange={(selectedColumn) => handlePairingSuggestion(firstRowWithSuggestions.label, suggestion, selectedColumn)}
headerOptions={headerOptions}
label={abbreviation}
title={title}
rowContent={rowContent}
/>
);
})}
</Box>
</AccordionDetails>
</Accordion>
</Box>
)}
<Box width={1} justifyContent="start">
<Button
variant='text'
startIcon={<PlusIcon />}
sx={{ '&:hover': { backgroundColor: 'transparent', color: gray500 } }}
onClick={addAnotherField}
disabled={hasOneEmptyItem}
>
Remove pairing suggestions and add another field
Add another field
</Button>
</Box>
</Stack>
</Box>
</ModalHeightWrapper>
<Box px={3} py={2} display="flex" justifyContent="end" gap={1} sx={{ borderTop: '1px solid #ECEDEE' }}>
<Button variant='text' onClick={onCloseModal}>Cancel</Button>
<Button variant='contained' onClick={onClose}>Create template</Button>
<Button variant='contained' onClick={onClose} disabled={hasNoItems}>Create template</Button>
</Box>
</Box>
);
}

export default TemplateStep;
export default TemplateStep;
6 changes: 3 additions & 3 deletions lib/icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ export const TutorialCloseIcon = () => (
</svg>
)

export const PlusIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M17.5 10C17.5 10.1658 17.4342 10.3247 17.3169 10.4419C17.1997 10.5592 17.0408 10.625 16.875 10.625H10.625V16.875C10.625 17.0408 10.5592 17.1997 10.4419 17.3169C10.3247 17.4342 10.1658 17.5 10 17.5C9.83424 17.5 9.67527 17.4342 9.55806 17.3169C9.44085 17.1997 9.375 17.0408 9.375 16.875V10.625H3.125C2.95924 10.625 2.80027 10.5592 2.68306 10.4419C2.56585 10.3247 2.5 10.1658 2.5 10C2.5 9.83424 2.56585 9.67527 2.68306 9.55806C2.80027 9.44085 2.95924 9.375 3.125 9.375H9.375V3.125C9.375 2.95924 9.44085 2.80027 9.55806 2.68306C9.67527 2.56585 9.83424 2.5 10 2.5C10.1658 2.5 10.3247 2.56585 10.4419 2.68306C10.5592 2.80027 10.625 2.95924 10.625 3.125V9.375H16.875C17.0408 9.375 17.1997 9.44085 17.3169 9.55806C17.4342 9.67527 17.5 9.83424 17.5 10Z" fill="#676C74"/>
export const PlusIcon: React.FC<SVGProps<SVGSVGElement>> = (props) => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
<path d="M17.5 10C17.5 10.1658 17.4342 10.3247 17.3169 10.4419C17.1997 10.5592 17.0408 10.625 16.875 10.625H10.625V16.875C10.625 17.0408 10.5592 17.1997 10.4419 17.3169C10.3247 17.4342 10.1658 17.5 10 17.5C9.83424 17.5 9.67527 17.4342 9.55806 17.3169C9.44085 17.1997 9.375 17.0408 9.375 16.875V10.625H3.125C2.95924 10.625 2.80027 10.5592 2.68306 10.4419C2.56585 10.3247 2.5 10.1658 2.5 10C2.5 9.83424 2.56585 9.67527 2.68306 9.55806C2.80027 9.44085 2.95924 9.375 3.125 9.375H9.375V3.125C9.375 2.95924 9.44085 2.80027 9.55806 2.68306C9.67527 2.56585 9.83424 2.5 10 2.5C10.1658 2.5 10.3247 2.56585 10.4419 2.68306C10.5592 2.80027 10.625 2.95924 10.625 3.125V9.375H16.875C17.0408 9.375 17.1997 9.44085 17.3169 9.55806C17.4342 9.67527 17.5 9.83424 17.5 10Z"/>
</svg>
)

Expand Down
Loading