diff --git a/client/src/app/components/target-card/target-card.css b/client/src/app/components/target-card/target-card.css index 6bc10c8299..bcec7a2865 100644 --- a/client/src/app/components/target-card/target-card.css +++ b/client/src/app/components/target-card/target-card.css @@ -2,10 +2,28 @@ background: none; } -.select-card__component__empty-state { - padding: 0 !important; +/* do NOT change background-color or box-shadow on hover with the TargetCard is selectable */ +.target-card.pf-m-selectable { + --pf-v5-c-card--m-selectable--hover--BackgroundColor: var( + --pf-v5-c-card--BackgroundColor + ); + /* + --pf-v5-c-card--m-selectable--hover--BoxShadow: var( + --pf-v5-c-card--BoxShadow + ); + */ } +/* + A way to force the select box to always have a white background, + even when the card is selected +*/ +/* +.target-label-choice-container { + background-color: var(--pf-v5-global--BackgroundColor--100); +} +*/ + .grabbable { cursor: move; /* fallback if grab cursor is unsupported */ cursor: grab; diff --git a/client/src/app/components/target-card/target-card.tsx b/client/src/app/components/target-card/target-card.tsx index de63ed27bd..ba78bab2a7 100644 --- a/client/src/app/components/target-card/target-card.tsx +++ b/client/src/app/components/target-card/target-card.tsx @@ -1,14 +1,10 @@ -import "./target-card.css"; import * as React from "react"; import { - EmptyState, EmptyStateIcon, Title, - EmptyStateVariant, Card, CardBody, DropdownItem, - Text, Flex, FlexItem, Button, @@ -18,26 +14,26 @@ import { PanelMain, PanelMainBody, Panel, + Stack, + StackItem, + Bullseye, } from "@patternfly/react-core"; -import { - Select, - SelectOption, - SelectVariant, - SelectOptionObject, -} from "@patternfly/react-core/deprecated"; import { GripVerticalIcon, InfoCircleIcon } from "@patternfly/react-icons"; import spacing from "@patternfly/react-styles/css/utilities/Spacing/spacing"; import { useTranslation } from "react-i18next"; -import { KebabDropdown } from "../KebabDropdown"; import DefaultImage from "@app/images/Icon-Red_Hat-Virtual_server_stack-A-Black-RGB.svg"; import { Target, TargetLabel } from "@app/api/models"; +import { KebabDropdown } from "../KebabDropdown"; import useFetchImageDataUrl from "./hooks/useFetchImageDataUrl"; +import { SimpleSelectBasic } from "../SimpleSelectBasic"; + +import "./target-card.css"; +import { localeNumericCompare } from "@app/utils/utils"; export interface TargetCardProps { item: Target; cardSelected?: boolean; - isEditable?: boolean; onCardClick?: ( isSelecting: boolean, targetLabelName: string, @@ -51,14 +47,16 @@ export interface TargetCardProps { onDelete?: () => void; } -// Force display dropdown box even though there only one option available. -// This is a business rule to guarantee that option is always present. +/** + * Force display dropdown box even though there only one option available. + * This is a business rule to guarantee that option is always present. + */ const forceSelect = ["Azure"]; export const TargetCard: React.FC = ({ item: target, - readOnly, - cardSelected, + readOnly = false, + cardSelected = false, formLabels, onCardClick, onSelectedCardTargetChange, @@ -67,65 +65,82 @@ export const TargetCard: React.FC = ({ onDelete, }) => { const { t } = useTranslation(); - const [isCardSelected, setCardSelected] = React.useState(cardSelected); const imageDataUrl = useFetchImageDataUrl(target); - const prevSelectedLabel = - formLabels?.find((formLabel) => { - const labelNames = target?.labels?.map((label) => label.name); - return labelNames?.includes(formLabel.name); - })?.name || ""; - - const [isLabelSelectOpen, setLabelSelectOpen] = React.useState(false); - - const [selectedLabelName, setSelectedLabelName] = React.useState( - prevSelectedLabel || - target?.labels?.[0]?.name || - `${target?.name || "target"}-Empty` + const targetLabels = (target?.labels ?? []).sort((a, b) => + localeNumericCompare(b.label, a.label) ); - const handleCardClick = (event: React.MouseEvent) => { - const eventTarget = event.target as HTMLElement; + const [selectedLabelName, setSelectedLabelName] = React.useState( + () => { + const prevSelectedLabel = + formLabels?.find((formLabel) => { + const labelNames = targetLabels.map((label) => label.name); + return labelNames?.includes(formLabel.name); + })?.name || ""; - if (eventTarget.tagName === "BUTTON" || eventTarget.tagName === "LABEL") { - event.preventDefault(); + return ( + prevSelectedLabel || + targetLabels[0]?.name || + `${target?.name || "target"}-Empty` + ); } + ); - setCardSelected(!isCardSelected); + const handleCardClick = () => { if (onCardClick && selectedLabelName) { - onCardClick(!isCardSelected, selectedLabelName, target); + onCardClick(!cardSelected, selectedLabelName, target); } }; - const handleLabelSelection = ( - event: React.MouseEvent | React.ChangeEvent, - selection: string | SelectOptionObject - ) => { - event.stopPropagation(); - setLabelSelectOpen(false); - setSelectedLabelName(selection as string); - if (isCardSelected && onSelectedCardTargetChange) { - onSelectedCardTargetChange(selection as string); + const handleLabelSelection = (selection: string) => { + setSelectedLabelName(selection); + if (cardSelected && onSelectedCardTargetChange) { + onSelectedCardTargetChange(selection); } }; + + const TargetLogo = () => ( + Card logo { + e.currentTarget.src = DefaultImage; + }} + /> + ); + + const labelChoices = + target.choice || forceSelect.includes(target.name) ? targetLabels : []; + + const idCard = `target-${target.name.replace(/\s/g, "-")}`; + const idProv = `${idCard}-provider-${target.provider?.replace(/\s/g, "-")}`; + return ( ); 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 d2d191d2d2..f4c419a16d 100644 --- a/client/src/app/pages/applications/analysis-wizard/set-targets.tsx +++ b/client/src/app/pages/applications/analysis-wizard/set-targets.tsx @@ -20,6 +20,7 @@ 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[]; } @@ -38,7 +39,7 @@ export const SetTargets: React.FC = ({ applications }) => { const formLabels = watch("formLabels"); const selectedTargets = watch("selectedTargets"); - const { tagCategories, isFetching, fetchError } = useFetchTagCategories(); + const { tagCategories } = useFetchTagCategories(); const findCategoryForTag = (tagId: number) => { return tagCategories.find( @@ -121,6 +122,15 @@ export const SetTargets: React.FC = ({ applications }) => { const allProviders = targets.flatMap((target) => target.provider); const languageOptions = Array.from(new Set(allProviders)); + const targetsToRender: Target[] = !targetOrderSetting.isSuccess + ? [] + : targetOrderSetting.data + .map((targetId) => targets.find((target) => target.id === targetId)) + .filter(Boolean) + .filter((target) => + providers.some((p) => target.provider?.includes(p) ?? false) + ); + return (
{ @@ -140,15 +150,16 @@ export const SetTargets: React.FC = ({ applications }) => { options={languageOptions?.map((language): SelectOptionProps => { return { children:
{language}
, - value: language, }; })} onChange={(selection) => { setProviders(selection as string[]); }} + id="filter-by-language" toggleId="action-select-toggle" /> + {values.selectedTargets.length === 0 && values.customRulesFiles.length === 0 && !values.sourceRepository && ( @@ -158,41 +169,24 @@ export const SetTargets: React.FC = ({ applications }) => { title={t("wizard.label.skipTargets")} /> )} + - {targetOrderSetting.isSuccess - ? targetOrderSetting.data.map((id, index) => { - const matchingTarget = targets.find((target) => target.id === id); - const isSelected = selectedTargets?.includes(id); - - if ( - matchingTarget && - providers?.some((p) => matchingTarget?.provider?.includes(p)) - ) { - return ( - - { - handleOnSelectedCardTargetChange(selectedTarget); - }} - onCardClick={(isSelecting, selectedLabelName, target) => { - handleOnCardClick( - isSelecting, - selectedLabelName, - target - ); - }} - formLabels={formLabels} - /> - - ); - } else { - return null; - } - }) - : null} + {targetsToRender.map((target) => ( + + { + handleOnSelectedCardTargetChange(selectedTarget); + }} + onCardClick={(isSelecting, selectedLabelName, target) => { + handleOnCardClick(isSelecting, selectedLabelName, target); + }} + formLabels={formLabels} + /> + + ))} );