From 9ea044121be3864a9fc1cad17550f05663960585 Mon Sep 17 00:00:00 2001 From: Katie Campbell Downie Date: Wed, 8 Jan 2025 16:40:41 -0600 Subject: [PATCH] use DibbsValueSet instead of VsGrouping --- .../app/query/components/CustomizeQuery.tsx | 109 ++++++++---------- .../CustomizeQueryAccordionBody.tsx | 22 ++-- .../CustomizeQueryAccordionHeader.tsx | 38 +++--- .../customizeQuery/CustomizeQueryNav.tsx | 16 +-- .../src/app/queryBuilding/utils.ts | 6 + .../src/app/utils/valueSetTranslation.ts | 30 ++--- 6 files changed, 103 insertions(+), 118 deletions(-) diff --git a/query-connector/src/app/query/components/CustomizeQuery.tsx b/query-connector/src/app/query/components/CustomizeQuery.tsx index cbd722237..c8ac299f9 100644 --- a/query-connector/src/app/query/components/CustomizeQuery.tsx +++ b/query-connector/src/app/query/components/CustomizeQuery.tsx @@ -18,10 +18,7 @@ import Accordion from "../designSystem/Accordion"; import CustomizeQueryNav from "./customizeQuery/CustomizeQueryNav"; import Backlink from "./backLink/Backlink"; import { RETURN_LABEL } from "./stepIndicator/StepIndicator"; -import { - VsGrouping, - generateValueSetGroupingsByDibbsConceptType, -} from "@/app/utils/valueSetTranslation"; +import { generateValueSetGroupingsByDibbsConceptType } from "@/app/utils/valueSetTranslation"; interface CustomizeQueryProps { useCaseQueryResponse: UseCaseQueryResponse; @@ -52,7 +49,7 @@ const CustomizeQuery: React.FC = ({ const [valueSetOptions, setValueSetOptions] = useState<{ [dibbsConceptType in DibbsConceptType]: { - [vsNameAuthorSystem: string]: VsGrouping; + [vsNameAuthorSystem: string]: DibbsValueSet; }; }>({ labs: {}, @@ -89,75 +86,71 @@ const CustomizeQuery: React.FC = ({ // Handles the toggle of the 'include' state for individual concepts in // the accordion - const toggleInclude = ( - groupIndex: string, - valueSetIndex: number, - conceptIndex: number, - ) => { - const updatedGroups = valueSetOptions[activeTab]; - const updatedValueSets = [...updatedGroups[groupIndex].items]; // Clone the current group items - const updatedConceptsInIndexedValueSet = [ - ...updatedValueSets[valueSetIndex].concepts, - ]; - updatedConceptsInIndexedValueSet[conceptIndex] = { - ...updatedConceptsInIndexedValueSet[conceptIndex], - include: !updatedConceptsInIndexedValueSet[conceptIndex].include, // Toggle the include for the clicked concept + const toggleInclude = (vsNameAuthorSystem: string, conceptIndex: number) => { + const updatedNameToVsMap = valueSetOptions[activeTab]; + let updatedValueSet = updatedNameToVsMap[vsNameAuthorSystem]; + const updatedConcepts = updatedValueSet.concepts; + updatedConcepts[conceptIndex] = { + ...updatedConcepts[conceptIndex], + include: !updatedConcepts[conceptIndex].include, // Toggle the include for the clicked concept }; - updatedValueSets[valueSetIndex] = { - ...updatedValueSets[valueSetIndex], - concepts: updatedConceptsInIndexedValueSet, // Update the concepts in the accessed value set + updatedValueSet = { + ...updatedValueSet, + concepts: updatedConcepts, // Update the concepts in the accessed value set }; - updatedGroups[groupIndex] = { - ...updatedGroups[groupIndex], - items: updatedValueSets, // Update the whole group's items + updatedNameToVsMap[vsNameAuthorSystem] = { + ...updatedNameToVsMap[vsNameAuthorSystem], + ...updatedValueSet, // Update the entire VS }; setValueSetOptions((prevState) => ({ ...prevState, - [activeTab]: updatedGroups, // Update the state with the new group + [activeTab]: updatedNameToVsMap, // Update the state with the modified VS })); }; // Allows all items to be selected within all accordion sections of the active tab - const handleSelectAllChange = (groupIndex: string, checked: boolean) => { - const updatedGroups = valueSetOptions[activeTab]; - + const handleSelectAllChange = ( + vsNameAuthorSystem: string, + checked: boolean, + ) => { + const updatedNameToVsMap = valueSetOptions[activeTab]; // a single group of lab/med/etc. // Update only the group at the specified index - updatedGroups[groupIndex].items = updatedGroups[groupIndex].items.map( - (item) => ({ - ...item, - includeValueSet: checked, // Set all items in this group to checked or unchecked - concepts: item.concepts.map((ic) => { - return { ...ic, include: checked }; - }), - }), + updatedNameToVsMap[vsNameAuthorSystem].includeValueSet = checked; + const updatedConcepts = updatedNameToVsMap[vsNameAuthorSystem].concepts.map( + (concept) => { + return { ...concept, include: checked }; + }, ); + updatedNameToVsMap[vsNameAuthorSystem].concepts = updatedConcepts; setValueSetOptions((prevState) => ({ ...prevState, - [activeTab]: updatedGroups, // Update the state for the current tab + [activeTab]: updatedNameToVsMap, // Update the state for the current tab })); }; // Allows all items to be selected within the entire active tab const handleSelectAllForTab = (checked: boolean) => { const activeItems = valueSetOptions[activeTab] ?? {}; - const updatedGroups = Object.values(activeItems).map((group) => ({ - ...group, - items: group.items.map((item) => ({ - ...item, + const updatedValueSets = Object.values(activeItems).map((vs) => { + const updatedValueSetData = { includeValueSet: checked, // Set all items in this group to checked or unchecked - concepts: item.concepts.map((ic) => { - return { ...ic, include: checked }; + concepts: vs.concepts.map((concept) => { + return { ...concept, include: checked }; }), - })), - })); + }; + return { + ...vs, + ...updatedValueSetData, + }; + }); setValueSetOptions((prevState) => ({ ...prevState, - [activeTab]: updatedGroups, // Update the state for the current tab + [activeTab]: updatedValueSets, // Update the state for the current tab })); }; @@ -165,8 +158,8 @@ const CustomizeQuery: React.FC = ({ // by the entire query branch of the app const handleApplyChanges = () => { const selectedItems = Object.keys(valueSetOptions).reduce((acc, key) => { - const items = valueSetOptions[key as DibbsConceptType] ?? {}; - acc = acc.concat(Object.values(items).flatMap((dict) => dict.items)); + const nameToVs = valueSetOptions[key as DibbsConceptType] ?? {}; + acc = acc.concat(Object.values(nameToVs)); return acc; }, [] as DibbsValueSet[]); setQueryValuesets(selectedItems); @@ -202,8 +195,8 @@ const CustomizeQuery: React.FC = ({ handleSelectAllForTab={handleSelectAllForTab} valueSetOptions={valueSetOptions} /> - {valueSetOptionsToDisplay.map(([groupIndex, group]) => { - const id = group.author + ":" + group.system + ":" + group.valueSetName; + {valueSetOptionsToDisplay.map(([vsNameAuthorSystem, vs]) => { + const id = vs.author + ":" + vs.system + ":" + vs.valueSetName; return ( = ({ title={ } content={ } headingLevel="h3" @@ -251,12 +244,10 @@ export const QUERY_CUSTOMIZATION_CONFIRMATION_BODY = * @returns A count of the number of items in each of the DibbsConceptTypes */ const countDibbsConceptTypeToVsMapItems = (obj: { - [vsNameAuthorSystem: string]: VsGrouping; + [vsNameAuthorSystem: string]: DibbsValueSet; }) => { - return Object.values(obj).reduce((runningSum, gvs) => { - gvs.items.forEach((vs) => { - runningSum += vs.concepts.length; - }); + return Object.values(obj).reduce((runningSum, vs) => { + runningSum += vs.concepts.length; return runningSum; }, 0); }; diff --git a/query-connector/src/app/query/components/customizeQuery/CustomizeQueryAccordionBody.tsx b/query-connector/src/app/query/components/customizeQuery/CustomizeQueryAccordionBody.tsx index 2ca46b3ed..1e8693a6b 100644 --- a/query-connector/src/app/query/components/customizeQuery/CustomizeQueryAccordionBody.tsx +++ b/query-connector/src/app/query/components/customizeQuery/CustomizeQueryAccordionBody.tsx @@ -1,17 +1,17 @@ import styles from "./customizeQuery.module.scss"; import Table from "../../designSystem/table/Table"; -import { VsGrouping } from "@/app/utils/valueSetTranslation"; +import { DibbsValueSet } from "@/app/constants"; import classNames from "classnames"; import Checkbox from "../../designSystem/checkbox/Checkbox"; type CustomizeQueryAccordionBodyProps = { - group: VsGrouping; + valueSet: DibbsValueSet; toggleInclude: ( groupIndex: string, valueSetIndex: number, conceptIndex: number, ) => void; - groupIndex: string; + vsName: string; }; type ValueSetIndexedConcept = { @@ -24,16 +24,16 @@ type ValueSetIndexedConcept = { /** * Styling component to render the body table for the customize query components * @param param0 - props for rendering - * @param param0.group - Matched concept associated with the query that + * @param param0.valueSet - Matched concept associated with the query that * contains valuesets to filter query on * @param param0.toggleInclude - Listener event to handle a concept inclusion/ * exclusion check - * @param param0.groupIndex - Index corresponding to group + * @param param0.vsName - Identifier for the value set * @returns JSX Fragment for the accordion body */ const CustomizeQueryAccordionBody: React.FC< CustomizeQueryAccordionBodyProps -> = ({ group, toggleInclude, groupIndex }) => { +> = ({ valueSet, toggleInclude, vsName }) => { return ( @@ -44,17 +44,15 @@ const CustomizeQueryAccordionBody: React.FC< - {group.items + {valueSet.concepts .reduce((acc, vs, vsIndex) => { - vs.concepts.forEach((c) => { - acc.push({ ...c, vsIndex: vsIndex }); - }); + acc.push({ ...vs, vsIndex: vsIndex }); return acc; }, [] as ValueSetIndexedConcept[]) .map((item, conceptIndex) => ( { - toggleInclude(groupIndex, item.vsIndex, conceptIndex); + toggleInclude(vsName, item.vsIndex, conceptIndex); }} className={classNames( "tableRowWithHover_clickable", @@ -68,7 +66,7 @@ const CustomizeQueryAccordionBody: React.FC< id={item.code} checked={item.include} onChange={() => { - toggleInclude(groupIndex, item.vsIndex, conceptIndex); + toggleInclude(vsName, item.vsIndex, conceptIndex); }} /> diff --git a/query-connector/src/app/query/components/customizeQuery/CustomizeQueryAccordionHeader.tsx b/query-connector/src/app/query/components/customizeQuery/CustomizeQueryAccordionHeader.tsx index 462908538..330d8bba1 100644 --- a/query-connector/src/app/query/components/customizeQuery/CustomizeQueryAccordionHeader.tsx +++ b/query-connector/src/app/query/components/customizeQuery/CustomizeQueryAccordionHeader.tsx @@ -1,11 +1,11 @@ -import { VsGrouping } from "@/app/utils/valueSetTranslation"; +import { DibbsValueSet } from "@/app/constants"; import styles from "./customizeQuery.module.scss"; import Checkbox from "../../designSystem/checkbox/Checkbox"; type CustomizeQueryAccordionProps = { handleSelectAllChange: (groupIndex: string, checked: boolean) => void; - groupIndex: string; - group: VsGrouping; + vsIndex: string; + valueSet: DibbsValueSet; }; /** @@ -13,24 +13,19 @@ type CustomizeQueryAccordionProps = { * @param param0 - props for rendering * @param param0.handleSelectAllChange * Listner function to include all valuesets when checkbox is selected - * @param param0.groupIndex - index corresponding to group - * @param param0.group - matched concept containing all rendered valuesets + * @param param0.vsIndex - index corresponding to group + * @param param0.valueSet - matched concept containing all rendered valuesets * @returns A component that renders the customization query body */ const CustomizeQueryAccordionHeader: React.FC = ({ handleSelectAllChange, - groupIndex, - group, + vsIndex, + valueSet, }) => { - const selectedTotal = group.items.reduce((sum, vs) => { - sum += vs.concepts.length; - return sum; - }, 0); - const selectedCount = group.items.reduce((sum, vs) => { - const includedConcepts = vs.concepts.filter((c) => c.include); - sum += includedConcepts.length; - return sum; - }, 0); + const selectedTotal = valueSet.concepts.length; + const selectedCount = valueSet.concepts.filter( + (concept) => concept.include, + ).length; const isMinusState = selectedCount !== selectedTotal && selectedCount !== 0; return ( @@ -38,23 +33,24 @@ const CustomizeQueryAccordionHeader: React.FC = ({ className={`${styles.accordionHeader} display-flex flex-no-wrap flex-align-start customize-query-header`} > { handleSelectAllChange( - groupIndex, + vsIndex, isMinusState ? false : selectedCount !== selectedTotal, ); }} className={styles.checkboxCell} />
- {`${group.valueSetName}`} + {`${valueSet.valueSetName}`} - Author: {group.author}{" "} - System: {group.system} + Author: {valueSet.author}{" "} + System:{" "} + {valueSet.system}
{`${selectedCount} of ${selectedTotal} selected`} diff --git a/query-connector/src/app/query/components/customizeQuery/CustomizeQueryNav.tsx b/query-connector/src/app/query/components/customizeQuery/CustomizeQueryNav.tsx index 4d0ece84c..a081421aa 100644 --- a/query-connector/src/app/query/components/customizeQuery/CustomizeQueryNav.tsx +++ b/query-connector/src/app/query/components/customizeQuery/CustomizeQueryNav.tsx @@ -1,14 +1,14 @@ import { DibbsConceptType } from "@/app/constants"; import styles from "./customizeQuery.module.scss"; import CustomizeQueryBulkSelect from "./CustomizeQueryBulkSelect"; -import { ConceptTypeToVsNameToVsGroupingMap } from "@/app/utils/valueSetTranslation"; +import { temp__ConceptTypeToVsNameToVsGroupingMap } from "@/app/utils/valueSetTranslation"; import { Button } from "@trussworks/react-uswds"; type CustomizeQueryNavProps = { activeTab: DibbsConceptType; handleTabChange: (tabName: DibbsConceptType) => void; handleSelectAllForTab: (checked: boolean) => void; - valueSetOptions: ConceptTypeToVsNameToVsGroupingMap; + valueSetOptions: temp__ConceptTypeToVsNameToVsGroupingMap; }; /** @@ -29,19 +29,13 @@ const CustomizeQueryNav: React.FC = ({ }) => { const activeItems = valueSetOptions[activeTab] ?? {}; - const hasSelectableItems = Object.values(activeItems).some( - (group) => group.items.length > 0, - ); + const hasSelectableItems = Object.values(activeItems).some((vs) => !!vs); const allItemsDeselected = Object.values(activeItems) - .flatMap((groupedValSets) => - groupedValSets.items.flatMap((i) => i.includeValueSet), - ) + .flatMap((vs) => vs.includeValueSet) .every((p) => !p); const allItemsSelected = Object.values(activeItems) - .flatMap((groupedValSets) => - groupedValSets.items.flatMap((i) => i.includeValueSet), - ) + .flatMap((vs) => vs.includeValueSet) .every((p) => p); return ( diff --git a/query-connector/src/app/queryBuilding/utils.ts b/query-connector/src/app/queryBuilding/utils.ts index 1f469119a..2682a5fef 100644 --- a/query-connector/src/app/queryBuilding/utils.ts +++ b/query-connector/src/app/queryBuilding/utils.ts @@ -2,6 +2,7 @@ import { DibbsValueSet } from "../constants"; import { VsGrouping, ConceptTypeToVsNameToVsGroupingMap, + temp__ConceptTypeToVsNameToVsGroupingMap } from "../utils/valueSetTranslation"; // The structure of the data that's coming from the backend @@ -37,6 +38,11 @@ export type ConditionToConceptTypeToValueSetGroupingMap = { [conditionId: string]: ConceptTypeToVsNameToVsGroupingMap; }; +export type temp__ConditionToConceptTypeToValueSetGroupingMap = { + [conditionId: string]: temp__ConceptTypeToVsNameToVsGroupingMap; +}; + + export type QueryDetailsResult = { query_name: string; id: string; diff --git a/query-connector/src/app/utils/valueSetTranslation.ts b/query-connector/src/app/utils/valueSetTranslation.ts index 551e435fa..f20454a3a 100644 --- a/query-connector/src/app/utils/valueSetTranslation.ts +++ b/query-connector/src/app/utils/valueSetTranslation.ts @@ -1,7 +1,8 @@ import { DibbsConceptType, DibbsValueSet } from "../constants"; import { ConditionIdToValueSetArrayMap, - ConditionToConceptTypeToValueSetGroupingMap, + // ConditionToConceptTypeToValueSetGroupingMap, + temp__ConditionToConceptTypeToValueSetGroupingMap, } from "../queryBuilding/utils"; // ValueSets that share the same name, author, system unique identifier @@ -12,6 +13,12 @@ export type VsGrouping = { items: DibbsValueSet[]; }; +export type temp__ConceptTypeToVsNameToVsGroupingMap = { + [dibbsConceptType in DibbsConceptType]: { + [name: string]: DibbsValueSet; + }; +}; + export type ConceptTypeToVsNameToVsGroupingMap = { [dibbsConceptType in DibbsConceptType]: { [name: string]: VsGrouping; @@ -43,7 +50,7 @@ export type ConceptOption = { code: string; display: string; include: boolean }; */ export function groupValueSetsByNameAuthorSystem( valueSetsToGroup: DibbsValueSet[], -): Record { +): Record { const results = valueSetsToGroup.reduce( (acc, row) => { // Check if both author and code_system are defined @@ -58,15 +65,8 @@ export function groupValueSetsByNameAuthorSystem( } const groupKey = `${valueSetName}:${author}:${system}`; - if (!acc[groupKey]) { + acc[groupKey] = { - valueSetName: valueSetName, - author: author, - system: system, - items: [], - }; - } - acc[groupKey].items.push({ valueSetId: row.valueSetId, valueSetVersion: row.valueSetVersion, valueSetName: row.valueSetName, @@ -79,10 +79,10 @@ export function groupValueSetsByNameAuthorSystem( concepts: row.concepts.map((c) => { return { ...c }; }), - }); + } return acc; }, - {} as Record, + {} as Record, ); return results; @@ -109,8 +109,8 @@ export function generateValueSetGroupingsByDibbsConceptType( */ export function groupValueSetGroupingByConditionId( conditionIdToValueSetArrayMap: ConditionIdToValueSetArrayMap, -): ConditionToConceptTypeToValueSetGroupingMap { - const results: ConditionToConceptTypeToValueSetGroupingMap = {}; +): temp__ConditionToConceptTypeToValueSetGroupingMap { + const results: temp__ConditionToConceptTypeToValueSetGroupingMap = {}; Object.entries(conditionIdToValueSetArrayMap).forEach( ([conditionId, valueSetArray]) => { @@ -144,7 +144,7 @@ function generateValueSetGroupingsByConceptType(valueSetsByConceptType: { return acc; }, {} as { - [key in DibbsConceptType]: { [vsName: string]: VsGrouping }; + [key in DibbsConceptType]: { [vsName: string]: DibbsValueSet }; }, ); }