Wizard: support tailored customizations
This splits the policy and profile selectors as they're drifting apart. Instead of querying the customizations attached to the profile, query the customizations attached to the policy, as these take into account tailoring. As a result unnecessary customizations won't be added.
This commit is contained in:
parent
a69a09fa4f
commit
3d545ed8ae
3 changed files with 370 additions and 218 deletions
|
|
@ -3,17 +3,20 @@ import React from 'react';
|
|||
import { Alert } from '@patternfly/react-core';
|
||||
|
||||
import OscapProfileInformation from './components/OscapProfileInformation';
|
||||
import PolicySelector from './components/PolicySelector';
|
||||
import ProfileSelector from './components/ProfileSelector';
|
||||
|
||||
import { useAppSelector } from '../../../../store/hooks';
|
||||
import {
|
||||
selectComplianceProfileID,
|
||||
selectComplianceType,
|
||||
selectImageTypes,
|
||||
} from '../../../../store/wizardSlice';
|
||||
|
||||
const Oscap = () => {
|
||||
const oscapProfile = useAppSelector(selectComplianceProfileID);
|
||||
const environments = useAppSelector(selectImageTypes);
|
||||
const complianceType = useAppSelector(selectComplianceType);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -24,7 +27,7 @@ const Oscap = () => {
|
|||
title="OpenSCAP profiles are not compatible with WSL images."
|
||||
/>
|
||||
)}
|
||||
<ProfileSelector />
|
||||
{complianceType === 'openscap' ? <ProfileSelector /> : <PolicySelector />}
|
||||
{oscapProfile && <OscapProfileInformation />}
|
||||
{oscapProfile && (
|
||||
<Alert
|
||||
|
|
|
|||
|
|
@ -0,0 +1,314 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import {
|
||||
FormGroup,
|
||||
MenuToggle,
|
||||
MenuToggleElement,
|
||||
Select,
|
||||
SelectOption,
|
||||
} from '@patternfly/react-core';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import {
|
||||
usePoliciesQuery,
|
||||
PolicyRead,
|
||||
} from '../../../../../store/complianceApi';
|
||||
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
|
||||
import {
|
||||
Filesystem,
|
||||
Services,
|
||||
useGetOscapCustomizationsForPolicyQuery,
|
||||
useLazyGetOscapCustomizationsForPolicyQuery,
|
||||
} from '../../../../../store/imageBuilderApi';
|
||||
import {
|
||||
changeCompliance,
|
||||
selectDistribution,
|
||||
selectCompliancePolicyID,
|
||||
selectCompliancePolicyTitle,
|
||||
addPackage,
|
||||
addPartition,
|
||||
changeFileSystemConfigurationType,
|
||||
removePackage,
|
||||
clearPartitions,
|
||||
changeEnabledServices,
|
||||
changeMaskedServices,
|
||||
changeDisabledServices,
|
||||
clearKernelAppend,
|
||||
addKernelArg,
|
||||
} from '../../../../../store/wizardSlice';
|
||||
import { useHasSpecificTargetOnly } from '../../../utilities/hasSpecificTargetOnly';
|
||||
import { parseSizeUnit } from '../../../utilities/parseSizeUnit';
|
||||
import { Partition, Units } from '../../FileSystem/FileSystemTable';
|
||||
import { removeBetaFromRelease } from '../removeBetaFromRelease';
|
||||
|
||||
type ComplianceSelectOptionPropType = {
|
||||
policy: PolicyRead;
|
||||
};
|
||||
|
||||
type ComplianceSelectOptionValueType = {
|
||||
policyID: string;
|
||||
profileID: string;
|
||||
toString: () => string;
|
||||
};
|
||||
|
||||
const ComplianceSelectOption = ({ policy }: ComplianceSelectOptionPropType) => {
|
||||
const selectObj = (
|
||||
policyID: string,
|
||||
profileID: string,
|
||||
title: string
|
||||
): ComplianceSelectOptionValueType => ({
|
||||
policyID,
|
||||
profileID,
|
||||
toString: () => title,
|
||||
});
|
||||
|
||||
const descr = (
|
||||
<>
|
||||
Threshold: {policy.compliance_threshold}
|
||||
<br />
|
||||
Active systems: {policy.total_system_count}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<SelectOption
|
||||
key={policy.id}
|
||||
value={selectObj(policy.id!, policy.ref_id!, policy.title!)}
|
||||
description={descr}
|
||||
>
|
||||
{policy.title}
|
||||
</SelectOption>
|
||||
);
|
||||
};
|
||||
|
||||
const PolicySelector = () => {
|
||||
const policyID = useAppSelector(selectCompliancePolicyID);
|
||||
const policyTitle = useAppSelector(selectCompliancePolicyTitle);
|
||||
const release = removeBetaFromRelease(useAppSelector(selectDistribution));
|
||||
const majorVersion = release.split('-')[1];
|
||||
const hasWslTargetOnly = useHasSpecificTargetOnly('wsl');
|
||||
const dispatch = useAppDispatch();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selected, setSelected] = useState<string>('None');
|
||||
|
||||
const {
|
||||
data: policies,
|
||||
isFetching: isFetchingPolicies,
|
||||
isSuccess: isSuccessPolicies,
|
||||
} = usePoliciesQuery({
|
||||
filter: `os_major_version=${majorVersion}`,
|
||||
});
|
||||
|
||||
const { data: currentProfileData } = useGetOscapCustomizationsForPolicyQuery(
|
||||
{
|
||||
distribution: release,
|
||||
policy: policyID!,
|
||||
},
|
||||
{ skip: !policyID }
|
||||
);
|
||||
|
||||
const [trigger] = useLazyGetOscapCustomizationsForPolicyQuery();
|
||||
|
||||
useEffect(() => {
|
||||
if (!policies || policies.data === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (policyID && !policyTitle) {
|
||||
for (const p of policies.data) {
|
||||
const pol = p as PolicyRead;
|
||||
if (pol.id === policyID) {
|
||||
dispatch(
|
||||
changeCompliance({
|
||||
policyID: pol.id,
|
||||
profileID: pol.ref_id,
|
||||
policyTitle: pol.title,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [isSuccessPolicies]);
|
||||
|
||||
const handleToggle = () => {
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
dispatch(
|
||||
changeCompliance({
|
||||
profileID: undefined,
|
||||
policyID: undefined,
|
||||
policyTitle: undefined,
|
||||
})
|
||||
);
|
||||
clearOscapPackages(currentProfileData?.packages || []);
|
||||
dispatch(changeFileSystemConfigurationType('automatic'));
|
||||
handleServices(undefined);
|
||||
dispatch(clearKernelAppend());
|
||||
};
|
||||
|
||||
const handlePackages = (
|
||||
oldOscapPackages: string[],
|
||||
newOscapPackages: string[]
|
||||
) => {
|
||||
clearOscapPackages(oldOscapPackages);
|
||||
|
||||
for (const pkg of newOscapPackages) {
|
||||
dispatch(
|
||||
addPackage({
|
||||
name: pkg,
|
||||
summary: 'Required by chosen compliance policy',
|
||||
repository: 'distro',
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const clearOscapPackages = (oscapPackages: string[]) => {
|
||||
for (const pkg of oscapPackages) {
|
||||
dispatch(removePackage(pkg));
|
||||
}
|
||||
};
|
||||
|
||||
const handlePartitions = (oscapPartitions: Filesystem[]) => {
|
||||
dispatch(clearPartitions());
|
||||
|
||||
const newPartitions = oscapPartitions.map((filesystem) => {
|
||||
const [size, unit] = parseSizeUnit(filesystem.min_size);
|
||||
const partition: Partition = {
|
||||
mountpoint: filesystem.mountpoint,
|
||||
min_size: size.toString(),
|
||||
unit: unit as Units,
|
||||
id: uuidv4(),
|
||||
};
|
||||
return partition;
|
||||
});
|
||||
|
||||
if (newPartitions) {
|
||||
dispatch(changeFileSystemConfigurationType('manual'));
|
||||
for (const partition of newPartitions) {
|
||||
dispatch(addPartition(partition));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleServices = (services: Services | undefined) => {
|
||||
dispatch(changeEnabledServices(services?.enabled || []));
|
||||
dispatch(changeMaskedServices(services?.masked || []));
|
||||
dispatch(changeDisabledServices(services?.disabled || []));
|
||||
};
|
||||
|
||||
const handleKernelAppend = (kernelAppend: string | undefined) => {
|
||||
dispatch(clearKernelAppend());
|
||||
|
||||
if (kernelAppend) {
|
||||
const kernelArgsArray = kernelAppend.split(' ');
|
||||
for (const arg of kernelArgsArray) {
|
||||
dispatch(addKernelArg(arg));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const applyChanges = (selection: ComplianceSelectOptionValueType) => {
|
||||
if (selection.policyID === undefined) {
|
||||
// handle user has selected 'None' case
|
||||
handleClear();
|
||||
} else {
|
||||
const oldOscapPackages = currentProfileData?.packages || [];
|
||||
trigger(
|
||||
{
|
||||
distribution: release,
|
||||
policy: selection.policyID,
|
||||
},
|
||||
true // preferCacheValue
|
||||
)
|
||||
.unwrap()
|
||||
.then((response) => {
|
||||
const oscapPartitions = response.filesystem || [];
|
||||
const newOscapPackages = response.packages || [];
|
||||
handlePartitions(oscapPartitions);
|
||||
handlePackages(oldOscapPackages, newOscapPackages);
|
||||
handleServices(response.services);
|
||||
handleKernelAppend(response.kernel?.append);
|
||||
const compl = selection as ComplianceSelectOptionValueType;
|
||||
setSelected(compl.toString());
|
||||
dispatch(
|
||||
changeCompliance({
|
||||
profileID: compl.profileID,
|
||||
policyID: compl.policyID,
|
||||
policyTitle: compl.toString(),
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelect = (
|
||||
_event: React.MouseEvent<Element, MouseEvent>,
|
||||
selection: string
|
||||
) => {
|
||||
if (selection) {
|
||||
applyChanges(selection as unknown as ComplianceSelectOptionValueType);
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const complianceOptions = () => {
|
||||
if (!policies || policies.data === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const res = [
|
||||
<SelectOption
|
||||
key="compliance-none-option"
|
||||
value={{ toString: () => 'None', compareTo: () => false }}
|
||||
>
|
||||
None
|
||||
</SelectOption>,
|
||||
];
|
||||
for (const p of policies.data) {
|
||||
if (p === undefined) {
|
||||
continue;
|
||||
}
|
||||
const pol = p as PolicyRead;
|
||||
res.push(<ComplianceSelectOption key={pol.id} policy={pol} />);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
const toggleCompliance = (toggleRef: React.Ref<MenuToggleElement>) => (
|
||||
<MenuToggle
|
||||
ouiaId="compliancePolicySelect"
|
||||
ref={toggleRef}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
isExpanded={isOpen}
|
||||
isDisabled={isFetchingPolicies || hasWslTargetOnly}
|
||||
style={
|
||||
{
|
||||
width: '200px',
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
{selected}
|
||||
</MenuToggle>
|
||||
);
|
||||
|
||||
return (
|
||||
<FormGroup data-testid="profiles-form-group" label="Policy">
|
||||
<Select
|
||||
isScrollable
|
||||
isOpen={isOpen}
|
||||
onSelect={handleSelect}
|
||||
onOpenChange={handleToggle}
|
||||
selected={selected}
|
||||
toggle={toggleCompliance}
|
||||
shouldFocusFirstItemOnOpen={false}
|
||||
>
|
||||
{complianceOptions()}
|
||||
</Select>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default PolicySelector;
|
||||
|
|
@ -25,10 +25,6 @@ import {
|
|||
useLazyGetOscapCustomizationsQuery,
|
||||
useBackendPrefetch,
|
||||
} from '../../../../../store/backendApi';
|
||||
import {
|
||||
usePoliciesQuery,
|
||||
PolicyRead,
|
||||
} from '../../../../../store/complianceApi';
|
||||
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
|
||||
import {
|
||||
DistributionProfileItem,
|
||||
|
|
@ -40,8 +36,6 @@ import {
|
|||
changeCompliance,
|
||||
selectDistribution,
|
||||
selectComplianceProfileID,
|
||||
selectCompliancePolicyID,
|
||||
selectCompliancePolicyTitle,
|
||||
addPackage,
|
||||
addPartition,
|
||||
changeFileSystemConfigurationType,
|
||||
|
|
@ -104,59 +98,15 @@ const OScapSelectOption = ({
|
|||
);
|
||||
};
|
||||
|
||||
type ComplianceSelectOptionPropType = {
|
||||
policy: PolicyRead;
|
||||
};
|
||||
|
||||
type ComplianceSelectOptionValueType = {
|
||||
policyID: string;
|
||||
profileID: string;
|
||||
toString: () => string;
|
||||
};
|
||||
|
||||
const ComplianceSelectOption = ({ policy }: ComplianceSelectOptionPropType) => {
|
||||
const selectObj = (
|
||||
policyID: string,
|
||||
profileID: string,
|
||||
title: string
|
||||
): ComplianceSelectOptionValueType => ({
|
||||
policyID,
|
||||
profileID,
|
||||
toString: () => title,
|
||||
});
|
||||
|
||||
const descr = (
|
||||
<>
|
||||
Threshold: {policy.compliance_threshold}
|
||||
<br />
|
||||
Active systems: {policy.total_system_count}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<SelectOption
|
||||
key={policy.id}
|
||||
value={selectObj(policy.id!, policy.ref_id!, policy.title!)}
|
||||
description={descr}
|
||||
>
|
||||
{policy.title}
|
||||
</SelectOption>
|
||||
);
|
||||
};
|
||||
|
||||
const ProfileSelector = () => {
|
||||
const policyID = useAppSelector(selectCompliancePolicyID);
|
||||
const policyTitle = useAppSelector(selectCompliancePolicyTitle);
|
||||
const profileID = useAppSelector(selectComplianceProfileID);
|
||||
const release = removeBetaFromRelease(useAppSelector(selectDistribution));
|
||||
const majorVersion = release.split('-')[1];
|
||||
const hasWslTargetOnly = useHasSpecificTargetOnly('wsl');
|
||||
const dispatch = useAppDispatch();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [inputValue, setInputValue] = useState<string>('');
|
||||
const [filterValue, setFilterValue] = useState<string>('');
|
||||
const [selectOptions, setSelectOptions] = useState<string[]>([]);
|
||||
const [selected, setSelected] = useState<string>('None');
|
||||
const complianceType = useAppSelector(selectComplianceType);
|
||||
const prefetchProfile = useBackendPrefetch('getOscapCustomizations');
|
||||
|
||||
|
|
@ -166,27 +116,9 @@ const ProfileSelector = () => {
|
|||
isSuccess,
|
||||
isError,
|
||||
refetch,
|
||||
} = useGetOscapProfilesQuery(
|
||||
{
|
||||
distribution: release,
|
||||
},
|
||||
{
|
||||
skip: complianceType === 'compliance',
|
||||
}
|
||||
);
|
||||
|
||||
const {
|
||||
data: policies,
|
||||
isFetching: isFetchingPolicies,
|
||||
isSuccess: isSuccessPolicies,
|
||||
} = usePoliciesQuery(
|
||||
{
|
||||
filter: `os_major_version=${majorVersion}`,
|
||||
},
|
||||
{
|
||||
skip: complianceType === 'openscap',
|
||||
}
|
||||
);
|
||||
} = useGetOscapProfilesQuery({
|
||||
distribution: release,
|
||||
});
|
||||
|
||||
const { data: currentProfileData } = useGetOscapCustomizationsQuery(
|
||||
{
|
||||
|
|
@ -210,28 +142,6 @@ const ProfileSelector = () => {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!policies || policies.data === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (policyID && !policyTitle) {
|
||||
for (const p of policies.data) {
|
||||
const pol = p as PolicyRead;
|
||||
if (pol.id === policyID) {
|
||||
dispatch(
|
||||
changeCompliance({
|
||||
policyID: pol.id,
|
||||
profileID: pol.ref_id,
|
||||
policyTitle: pol.title,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [isSuccessPolicies]);
|
||||
|
||||
useEffect(() => {
|
||||
let filteredProfiles = profiles;
|
||||
|
||||
|
|
@ -361,9 +271,7 @@ const ProfileSelector = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const applyChanges = (
|
||||
selection: OScapSelectOptionValueType | ComplianceSelectOptionValueType
|
||||
) => {
|
||||
const applyChanges = (selection: OScapSelectOptionValueType) => {
|
||||
if (selection.profileID === undefined) {
|
||||
// handle user has selected 'None' case
|
||||
handleClear();
|
||||
|
|
@ -384,25 +292,13 @@ const ProfileSelector = () => {
|
|||
handlePackages(oldOscapPackages, newOscapPackages);
|
||||
handleServices(response.services);
|
||||
handleKernelAppend(response.kernel?.append);
|
||||
if (complianceType === 'openscap') {
|
||||
dispatch(
|
||||
changeCompliance({
|
||||
profileID: selection.profileID,
|
||||
policyID: undefined,
|
||||
policyTitle: undefined,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
const compl = selection as ComplianceSelectOptionValueType;
|
||||
setSelected(compl.toString());
|
||||
dispatch(
|
||||
changeCompliance({
|
||||
profileID: compl.profileID,
|
||||
policyID: compl.policyID,
|
||||
policyTitle: compl.toString(),
|
||||
})
|
||||
);
|
||||
}
|
||||
dispatch(
|
||||
changeCompliance({
|
||||
profileID: selection.profileID,
|
||||
policyID: undefined,
|
||||
policyTitle: undefined,
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -414,38 +310,11 @@ const ProfileSelector = () => {
|
|||
if (selection) {
|
||||
setInputValue(selection);
|
||||
setFilterValue('');
|
||||
applyChanges(
|
||||
selection as unknown as
|
||||
| OScapSelectOptionValueType
|
||||
| ComplianceSelectOptionValueType
|
||||
);
|
||||
applyChanges(selection as unknown as OScapSelectOptionValueType);
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const complianceOptions = () => {
|
||||
if (!policies || policies.data === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const res = [
|
||||
<SelectOption
|
||||
key="compliance-none-option"
|
||||
value={{ toString: () => 'None', compareTo: () => false }}
|
||||
>
|
||||
None
|
||||
</SelectOption>,
|
||||
];
|
||||
for (const p of policies.data) {
|
||||
if (p === undefined) {
|
||||
continue;
|
||||
}
|
||||
const pol = p as PolicyRead;
|
||||
res.push(<ComplianceSelectOption key={pol.id} policy={pol} />);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
const toggleOpenSCAP = (toggleRef: React.Ref<MenuToggleElement>) => (
|
||||
<MenuToggle
|
||||
data-testid="profileSelect"
|
||||
|
|
@ -480,85 +349,51 @@ const ProfileSelector = () => {
|
|||
</MenuToggle>
|
||||
);
|
||||
|
||||
const toggleCompliance = (toggleRef: React.Ref<MenuToggleElement>) => (
|
||||
<MenuToggle
|
||||
ref={toggleRef}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
isExpanded={isOpen}
|
||||
isDisabled={isFetchingPolicies}
|
||||
style={
|
||||
{
|
||||
width: '200px',
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
{selected}
|
||||
</MenuToggle>
|
||||
);
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
data-testid="profiles-form-group"
|
||||
label={complianceType === 'openscap' ? <OpenSCAPFGLabel /> : <>Policy</>}
|
||||
>
|
||||
{complianceType === 'openscap' && (
|
||||
<Select
|
||||
isScrollable
|
||||
isOpen={isOpen}
|
||||
selected={profileID}
|
||||
onSelect={handleSelect}
|
||||
onOpenChange={handleToggle}
|
||||
toggle={toggleOpenSCAP}
|
||||
shouldFocusFirstItemOnOpen={false}
|
||||
popperProps={{
|
||||
maxWidth: '50vw',
|
||||
}}
|
||||
>
|
||||
<SelectList>
|
||||
{isFetching && (
|
||||
<FormGroup data-testid="profiles-form-group" label={<OpenSCAPFGLabel />}>
|
||||
<Select
|
||||
isScrollable
|
||||
isOpen={isOpen}
|
||||
selected={profileID}
|
||||
onSelect={handleSelect}
|
||||
onOpenChange={handleToggle}
|
||||
toggle={toggleOpenSCAP}
|
||||
shouldFocusFirstItemOnOpen={false}
|
||||
popperProps={{
|
||||
maxWidth: '50vw',
|
||||
}}
|
||||
>
|
||||
<SelectList>
|
||||
{isFetching && (
|
||||
<SelectOption
|
||||
value="loader"
|
||||
data-testid="openscap-profiles-fetching"
|
||||
>
|
||||
<Spinner size="lg" />
|
||||
</SelectOption>
|
||||
)}
|
||||
{selectOptions.length > 0 &&
|
||||
[
|
||||
<SelectOption
|
||||
value="loader"
|
||||
data-testid="openscap-profiles-fetching"
|
||||
key="oscap-none-option"
|
||||
value={{ toString: () => 'None', compareTo: () => false }}
|
||||
>
|
||||
<Spinner size="lg" />
|
||||
</SelectOption>
|
||||
)}
|
||||
{selectOptions.length > 0 &&
|
||||
[
|
||||
<SelectOption
|
||||
key="oscap-none-option"
|
||||
value={{ toString: () => 'None', compareTo: () => false }}
|
||||
>
|
||||
None
|
||||
</SelectOption>,
|
||||
].concat(
|
||||
selectOptions.map(
|
||||
(name: DistributionProfileItem, index: number) => (
|
||||
<OScapSelectOption key={index} profile_id={name} />
|
||||
)
|
||||
None
|
||||
</SelectOption>,
|
||||
].concat(
|
||||
selectOptions.map(
|
||||
(name: DistributionProfileItem, index: number) => (
|
||||
<OScapSelectOption key={index} profile_id={name} />
|
||||
)
|
||||
)}
|
||||
{isSuccess && selectOptions.length === 0 && (
|
||||
<SelectOption isDisabled>
|
||||
{`No results found for "${filterValue}"`}
|
||||
</SelectOption>
|
||||
)
|
||||
)}
|
||||
</SelectList>
|
||||
</Select>
|
||||
)}
|
||||
{complianceType === 'compliance' && (
|
||||
<Select
|
||||
isScrollable
|
||||
isOpen={isOpen}
|
||||
onSelect={handleSelect}
|
||||
onOpenChange={handleToggle}
|
||||
selected={selected}
|
||||
toggle={toggleCompliance}
|
||||
shouldFocusFirstItemOnOpen={false}
|
||||
>
|
||||
{complianceOptions()}
|
||||
</Select>
|
||||
)}
|
||||
{isSuccess && selectOptions.length === 0 && (
|
||||
<SelectOption isDisabled>
|
||||
{`No results found for "${filterValue}"`}
|
||||
</SelectOption>
|
||||
)}
|
||||
</SelectList>
|
||||
</Select>
|
||||
{isError && (
|
||||
<Alert
|
||||
title="Error fetching the profiles"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue