From a2a142af0d2ac1ad69c25357e5a84de545bb2852 Mon Sep 17 00:00:00 2001 From: Ian Bolton Date: Wed, 17 Jul 2024 15:31:34 -0400 Subject: [PATCH] :bug: Address targets falling out of sync after label selection changes Signed-off-by: Ian Bolton --- .../analysis-wizard/set-options.tsx | 40 ++++++---- .../analysis-wizard/set-targets.tsx | 49 ++---------- .../applications/analysis-wizard/utils.ts | 79 ++++++++++++++++++- 3 files changed, 110 insertions(+), 58 deletions(-) diff --git a/client/src/app/pages/applications/analysis-wizard/set-options.tsx b/client/src/app/pages/applications/analysis-wizard/set-options.tsx index 8efc561b35..f5597388d1 100644 --- a/client/src/app/pages/applications/analysis-wizard/set-options.tsx +++ b/client/src/app/pages/applications/analysis-wizard/set-options.tsx @@ -29,6 +29,11 @@ import { DEFAULT_SELECT_MAX_HEIGHT } from "@app/Constants"; import { useFetchTargets } from "@app/queries/targets"; import defaultSources from "./sources"; import { QuestionCircleIcon } from "@patternfly/react-icons"; +import { + findLabelBySelector, + isLabelInFormLabels, + updateSelectedTargetsBasedOnLabels, +} from "./utils"; export const SetOptions: React.FC = () => { const { t } = useTranslation(); @@ -41,6 +46,7 @@ export const SetOptions: React.FC = () => { excludedRulesTags, autoTaggingEnabled, advancedAnalysisEnabled, + selectedTargets, } = watch(); const [isSelectTargetsOpen, setSelectTargetsOpen] = React.useState(false); @@ -119,27 +125,33 @@ export const SetOptions: React.FC = () => { isOpen={isSelectTargetsOpen} onSelect={(_, selection) => { const selectionWithLabelSelector = `konveyor.io/target=${selection}`; - const matchingLabel = - defaultTargetsAndTargetsLabels.find( - (label) => label.label === selectionWithLabelSelector - ) || ""; - - const formLabelLabels = formLabels.map( - (formLabel) => formLabel.label + const matchingLabel = findLabelBySelector( + defaultTargetsAndTargetsLabels, + selectionWithLabelSelector ); + let updatedFormLabels = []; if ( matchingLabel && - !formLabelLabels.includes(matchingLabel.label) + !isLabelInFormLabels(formLabels, matchingLabel.label) ) { - onChange([...formLabels, matchingLabel]); + updatedFormLabels = [...formLabels, matchingLabel]; + onChange(updatedFormLabels); } else { - onChange( - formLabels.filter( - (formLabel) => - formLabel.label !== selectionWithLabelSelector - ) + updatedFormLabels = formLabels.filter( + (formLabel) => + formLabel.label !== selectionWithLabelSelector ); + onChange(updatedFormLabels); } + + const updatedSelectedTargets = + updateSelectedTargetsBasedOnLabels( + updatedFormLabels, + selectedTargets, + targets + ); + setValue("selectedTargets", updatedSelectedTargets); + onBlur(); setSelectTargetsOpen(!isSelectTargetsOpen); }} diff --git a/client/src/app/pages/applications/analysis-wizard/set-targets.tsx b/client/src/app/pages/applications/analysis-wizard/set-targets.tsx index 9d0f0577bd..d2d191d2d2 100644 --- a/client/src/app/pages/applications/analysis-wizard/set-targets.tsx +++ b/client/src/app/pages/applications/analysis-wizard/set-targets.tsx @@ -19,6 +19,7 @@ import { useFetchTargets } from "@app/queries/targets"; import { Application, TagCategory, Target } from "@app/api/models"; import { useFetchTagCategories } from "@app/queries/tags"; import { SimpleSelectCheckbox } from "@app/components/SimpleSelectCheckbox"; +import { getUpdatedFormLabels, updateSelectedTargets } from "./utils"; interface SetTargetsProps { applications: Application[]; } @@ -101,61 +102,23 @@ export const SetTargets: React.FC = ({ applications }) => { selectedLabelName: string, target: Target ) => { - const updatedSelectedTargets = getUpdatedSelectedTargets( - isSelecting, - target + const updatedSelectedTargets = updateSelectedTargets( + target.id, + selectedTargets ); const updatedFormLabels = getUpdatedFormLabels( isSelecting, selectedLabelName, - target + target, + formLabels ); setValue("formLabels", updatedFormLabels); setValue("selectedTargets", updatedSelectedTargets); }; - const getUpdatedSelectedTargets = (isSelecting: boolean, target: Target) => { - const { selectedTargets } = values; - if (isSelecting) { - return [...selectedTargets, target.id]; - } - return selectedTargets.filter((id) => id !== target.id); - }; - - const getUpdatedFormLabels = ( - isSelecting: boolean, - selectedLabelName: string, - target: Target - ) => { - const { formLabels } = values; - if (target.custom) { - const customTargetLabelNames = target.labels?.map((label) => label.name); - const otherSelectedLabels = formLabels?.filter( - (formLabel) => !customTargetLabelNames?.includes(formLabel.name) - ); - return isSelecting && target.labels - ? [...otherSelectedLabels, ...target.labels] - : otherSelectedLabels; - } else { - const otherSelectedLabels = formLabels?.filter( - (formLabel) => formLabel.name !== selectedLabelName - ); - if (isSelecting) { - const matchingLabel = target.labels?.find( - (label) => label.name === selectedLabelName - ); - return matchingLabel - ? [...otherSelectedLabels, matchingLabel] - : otherSelectedLabels; - } - return otherSelectedLabels; - } - }; - const allProviders = targets.flatMap((target) => target.provider); - const languageOptions = Array.from(new Set(allProviders)); return ( diff --git a/client/src/app/pages/applications/analysis-wizard/utils.ts b/client/src/app/pages/applications/analysis-wizard/utils.ts index 9075478cad..07494fc4c4 100644 --- a/client/src/app/pages/applications/analysis-wizard/utils.ts +++ b/client/src/app/pages/applications/analysis-wizard/utils.ts @@ -1,5 +1,5 @@ import * as React from "react"; -import { Application } from "@app/api/models"; +import { Application, Target, TargetLabel } from "@app/api/models"; import { AnalysisMode, ANALYSIS_MODES } from "./schema"; export const isApplicationBinaryEnabled = ( @@ -60,3 +60,80 @@ export const useAnalyzableApplicationsByMode = ( ), [applications] ); + +export const updateSelectedTargets = ( + targetId: number, + selectedTargetIDs: number[] +) => { + const isSelected = selectedTargetIDs.includes(targetId); + return isSelected + ? selectedTargetIDs.filter((id) => id !== targetId) + : [...selectedTargetIDs, targetId]; +}; + +export const getUpdatedFormLabels = ( + isSelecting: boolean, + selectedLabelName: string, + target: Target, + formLabels: TargetLabel[] +) => { + if (target.custom) { + const customTargetLabelNames = target.labels?.map((label) => label.name); + const otherSelectedLabels = formLabels?.filter( + (formLabel) => !customTargetLabelNames?.includes(formLabel.name) + ); + return isSelecting && target.labels + ? [...otherSelectedLabels, ...target.labels] + : otherSelectedLabels; + } else { + const otherSelectedLabels = formLabels?.filter( + (formLabel) => formLabel.name !== selectedLabelName + ); + if (isSelecting) { + const matchingLabel = target.labels?.find( + (label) => label.name === selectedLabelName + ); + return matchingLabel + ? [...otherSelectedLabels, matchingLabel] + : otherSelectedLabels; + } + return otherSelectedLabels; + } +}; +export const findLabelBySelector = (labels: TargetLabel[], selector: string) => + labels.find((label) => label.label === selector) || ""; + +export const isLabelInFormLabels = (formLabels: TargetLabel[], label: string) => + formLabels.some((formLabel) => formLabel.label === label); + +export const labelToTargetId = (labelName: string, targets: Target[]) => { + const target = targets.find( + (t) => t.labels?.some((l) => l.name === labelName) + ); + return target ? target.id : null; +}; + +export const updateSelectedTargetsBasedOnLabels = ( + currentFormLabels: TargetLabel[], + selectedTargets: number[], + targets: Target[] +) => { + const newSelectedTargets = currentFormLabels.reduce( + (acc: number[], formLabel) => { + const targetId = labelToTargetId(formLabel.name, targets); + if (targetId && !acc.includes(targetId)) { + acc.push(targetId); + } + return acc; + }, + [] + ); + + const filteredSelectedTargets = selectedTargets.filter((targetId) => + currentFormLabels.some( + (formLabel) => labelToTargetId(formLabel.name, targets) === targetId + ) + ); + + return [...new Set([...newSelectedTargets, ...filteredSelectedTargets])]; +};