CreateImageWizardV2: search package groups in distro repositories
By prepending an `@`, users can search for package groups. A single `@` just lists all groups.
This commit is contained in:
parent
69367ba1d9
commit
177e4b227c
5 changed files with 402 additions and 119 deletions
|
|
@ -12,6 +12,7 @@ const config: ConfigFile = {
|
|||
'listRepositories',
|
||||
'listRepositoriesRpms',
|
||||
'searchRpm',
|
||||
'searchPackageGroup',
|
||||
'listFeatures',
|
||||
'listSnapshotsByDate',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { ReactElement, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
Bullseye,
|
||||
|
|
@ -52,6 +52,7 @@ import {
|
|||
useCreateRepositoryMutation,
|
||||
useListRepositoriesQuery,
|
||||
useSearchRpmMutation,
|
||||
useSearchPackageGroupMutation,
|
||||
} from '../../../../store/contentSourcesApi';
|
||||
import { useAppSelector } from '../../../../store/hooks';
|
||||
import {
|
||||
|
|
@ -59,12 +60,15 @@ import {
|
|||
useGetArchitecturesQuery,
|
||||
} from '../../../../store/imageBuilderApi';
|
||||
import {
|
||||
removePackage,
|
||||
selectArchitecture,
|
||||
selectPackages,
|
||||
selectGroups,
|
||||
selectCustomRepositories,
|
||||
selectDistribution,
|
||||
addPackage,
|
||||
removePackage,
|
||||
addGroup,
|
||||
removeGroup,
|
||||
addRecommendedRepository,
|
||||
removeRecommendedRepository,
|
||||
selectRecommendedRepositories,
|
||||
|
|
@ -80,6 +84,13 @@ export type IBPackageWithRepositoryInfo = {
|
|||
repository: PackageRepository;
|
||||
};
|
||||
|
||||
export type GroupWithRepositoryInfo = {
|
||||
name: string;
|
||||
description: string;
|
||||
repository: PackageRepository;
|
||||
package_list: string[];
|
||||
};
|
||||
|
||||
export enum RepoToggle {
|
||||
INCLUDED = 'toggle-included-repos',
|
||||
OTHER = 'toggle-other-repos',
|
||||
|
|
@ -108,6 +119,7 @@ const Packages = () => {
|
|||
const customRepositories = useAppSelector(selectCustomRepositories);
|
||||
const recommendedRepositories = useAppSelector(selectRecommendedRepositories);
|
||||
const packages = useAppSelector(selectPackages);
|
||||
const groups = useAppSelector(selectGroups);
|
||||
|
||||
const { data: distroRepositories, isSuccess: isSuccessDistroRepositories } =
|
||||
useGetArchitecturesQuery({
|
||||
|
|
@ -149,6 +161,7 @@ const Packages = () => {
|
|||
|
||||
const debouncedSearchTerm = useDebounce(searchTerm.trim());
|
||||
const debouncedSearchTermLengthOf1 = debouncedSearchTerm.length === 1;
|
||||
const debouncedSearchTermIsGroup = debouncedSearchTerm.startsWith('@');
|
||||
|
||||
const [
|
||||
searchRecommendedRpms,
|
||||
|
|
@ -168,10 +181,56 @@ const Packages = () => {
|
|||
},
|
||||
] = useSearchRpmMutation();
|
||||
|
||||
const [
|
||||
searchDistroGroups,
|
||||
{
|
||||
data: dataDistroGroups,
|
||||
isSuccess: isSuccessDistroGroups,
|
||||
isLoading: isLoadingDistroGroups,
|
||||
},
|
||||
] = useSearchPackageGroupMutation();
|
||||
|
||||
const [createRepository, { isLoading: createLoading }] =
|
||||
useCreateRepositoryMutation();
|
||||
|
||||
const sortfn = (a: string, b: string) => {
|
||||
const aPkg = a.toLowerCase();
|
||||
const bPkg = b.toLowerCase();
|
||||
// check exact match first
|
||||
if (aPkg === debouncedSearchTerm) {
|
||||
return -1;
|
||||
}
|
||||
if (bPkg === debouncedSearchTerm) {
|
||||
return 1;
|
||||
}
|
||||
// check for packages that start with the search term
|
||||
if (
|
||||
aPkg.startsWith(debouncedSearchTerm) &&
|
||||
!bPkg.startsWith(debouncedSearchTerm)
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
if (
|
||||
bPkg.startsWith(debouncedSearchTerm) &&
|
||||
!aPkg.startsWith(debouncedSearchTerm)
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
// if both (or neither) start with the search term
|
||||
// sort alphabetically
|
||||
if (aPkg < bPkg) {
|
||||
return -1;
|
||||
}
|
||||
if (bPkg < aPkg) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedSearchTermIsGroup) {
|
||||
return;
|
||||
}
|
||||
if (debouncedSearchTerm.length > 1 && isSuccessDistroRepositories) {
|
||||
searchDistroRpms({
|
||||
apiContentUnitSearchRequest: {
|
||||
|
|
@ -227,6 +286,35 @@ const Packages = () => {
|
|||
arch,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!debouncedSearchTermIsGroup) {
|
||||
return;
|
||||
}
|
||||
if (isSuccessDistroRepositories) {
|
||||
searchDistroGroups({
|
||||
apiContentUnitSearchRequest: {
|
||||
search: debouncedSearchTerm.substr(1),
|
||||
urls: distroRepositories
|
||||
.filter((archItem) => {
|
||||
return archItem.arch === arch;
|
||||
})[0]
|
||||
.repositories.flatMap((repo) => {
|
||||
if (!repo.baseurl) {
|
||||
throw new Error(`Repository ${repo} missing baseurl`);
|
||||
}
|
||||
return repo.baseurl;
|
||||
}),
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [
|
||||
customRepositories,
|
||||
searchDistroGroups,
|
||||
debouncedSearchTerm,
|
||||
toggleSourceRepos,
|
||||
epelRepoUrlByDistribution,
|
||||
]);
|
||||
|
||||
const EmptySearch = () => {
|
||||
return (
|
||||
<Tr>
|
||||
|
|
@ -591,39 +679,27 @@ const Packages = () => {
|
|||
packages,
|
||||
toggleSelected,
|
||||
toggleSourceRepos,
|
||||
]).sort((a, b) => {
|
||||
const aPkg = a.name.toLowerCase();
|
||||
const bPkg = b.name.toLowerCase();
|
||||
// check exact match first
|
||||
if (aPkg === debouncedSearchTerm) {
|
||||
return -1;
|
||||
]).sort((a, b) => sortfn(a.name, b.name));
|
||||
|
||||
const transformedGroups = useMemo(() => {
|
||||
let transformedDistroGroups: GroupWithRepositoryInfo[] = [];
|
||||
if (isSuccessDistroGroups) {
|
||||
transformedDistroGroups = dataDistroGroups!.map((values) => ({
|
||||
name: values.id!,
|
||||
description: values.description!,
|
||||
repository: 'distro',
|
||||
package_list: values.package_list!,
|
||||
}));
|
||||
}
|
||||
if (bPkg === debouncedSearchTerm) {
|
||||
return 1;
|
||||
}
|
||||
// check for packages that start with the search term
|
||||
if (
|
||||
aPkg.startsWith(debouncedSearchTerm) &&
|
||||
!bPkg.startsWith(debouncedSearchTerm)
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
if (
|
||||
bPkg.startsWith(debouncedSearchTerm) &&
|
||||
!aPkg.startsWith(debouncedSearchTerm)
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
// if both (or neither) start with the search term
|
||||
// sort alphabetically
|
||||
if (aPkg < bPkg) {
|
||||
return -1;
|
||||
}
|
||||
if (bPkg < aPkg) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
return transformedDistroGroups;
|
||||
}, [
|
||||
dataDistroGroups,
|
||||
debouncedSearchTerm,
|
||||
isSuccessDistroGroups,
|
||||
groups,
|
||||
toggleSelected,
|
||||
toggleSourceRepos,
|
||||
]).sort((a, b) => sortfn(a.name, b.name));
|
||||
|
||||
const handleSearch = async (
|
||||
event: React.FormEvent<HTMLInputElement>,
|
||||
|
|
@ -665,6 +741,18 @@ const Packages = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleGroupSelect = (
|
||||
grp: GroupWithRepositoryInfo,
|
||||
_: number,
|
||||
isSelecting: boolean
|
||||
) => {
|
||||
if (isSelecting) {
|
||||
dispatch(addGroup(grp));
|
||||
} else {
|
||||
dispatch(removeGroup(grp.name));
|
||||
}
|
||||
};
|
||||
|
||||
const handleFilterToggleClick = (event: React.MouseEvent) => {
|
||||
const id = event.currentTarget.id;
|
||||
setPage(1);
|
||||
|
|
@ -766,7 +854,103 @@ const Packages = () => {
|
|||
};
|
||||
|
||||
const composePkgTable = () => {
|
||||
return transformedPackages
|
||||
let rows: ReactElement[] = [];
|
||||
rows = rows.concat(
|
||||
transformedGroups
|
||||
.slice(computeStart(), computeEnd())
|
||||
.map((grp, rowIndex) => (
|
||||
<Tr key={`${grp.name}-${rowIndex}`} data-testid="package-row">
|
||||
<Td
|
||||
select={{
|
||||
isSelected: groups.some((g) => g.name === grp.name),
|
||||
rowIndex: rowIndex,
|
||||
onSelect: (event, isSelecting) =>
|
||||
handleGroupSelect(grp, rowIndex, isSelecting),
|
||||
}}
|
||||
/>
|
||||
<Td>
|
||||
@{grp.name}
|
||||
<Popover
|
||||
minWidth="25rem"
|
||||
bodyContent={
|
||||
<div
|
||||
style={
|
||||
grp.package_list.length > 0
|
||||
? { height: '40em', overflow: 'scroll' }
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{grp.package_list.length > 0 ? (
|
||||
<Table variant="compact">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Included packages</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{grp.package_list.map((pkg) => (
|
||||
<Tr key={`details-${pkg}`}>
|
||||
<Td>{pkg}</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
) : (
|
||||
<Text>This group has no packages</Text>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant="plain"
|
||||
aria-label="About included repositories"
|
||||
component="span"
|
||||
className="pf-u-p-0"
|
||||
isInline
|
||||
>
|
||||
<HelpIcon className="pf-u-ml-xs" />
|
||||
</Button>
|
||||
</Popover>
|
||||
</Td>
|
||||
<Td>
|
||||
{grp.description ? (
|
||||
grp.description
|
||||
) : (
|
||||
<span className="not-available">Not available</span>
|
||||
)}
|
||||
</Td>
|
||||
{grp.repository === 'distro' ? (
|
||||
<>
|
||||
<Td>
|
||||
<img
|
||||
src={
|
||||
'/apps/frontend-assets/red-hat-logos/logo_hat-only.svg'
|
||||
}
|
||||
alt="Red Hat logo"
|
||||
height={RH_ICON_SIZE}
|
||||
width={RH_ICON_SIZE}
|
||||
/>{' '}
|
||||
Red Hat repository
|
||||
</Td>
|
||||
<Td>Supported</Td>
|
||||
</>
|
||||
) : grp.repository === 'custom' ? (
|
||||
<>
|
||||
<Td>Third party repository</Td>
|
||||
<Td>Not supported</Td>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Td className="not-available">Not available</Td>
|
||||
<Td className="not-available">Not available</Td>
|
||||
</>
|
||||
)}
|
||||
</Tr>
|
||||
))
|
||||
);
|
||||
|
||||
rows = rows.concat(
|
||||
transformedPackages
|
||||
.slice(computeStart(), computeEnd())
|
||||
.map((pkg, rowIndex) => (
|
||||
<Tr key={`${pkg.name}-${rowIndex}`} data-testid="package-row">
|
||||
|
|
@ -789,7 +973,15 @@ const Packages = () => {
|
|||
{pkg.repository === 'distro' ? (
|
||||
<>
|
||||
<Td>
|
||||
<RedHatRepository />
|
||||
<img
|
||||
src={
|
||||
'/apps/frontend-assets/red-hat-logos/logo_hat-only.svg'
|
||||
}
|
||||
alt="Red Hat logo"
|
||||
height={RH_ICON_SIZE}
|
||||
width={RH_ICON_SIZE}
|
||||
/>{' '}
|
||||
Red Hat repository
|
||||
</Td>
|
||||
<Td>Supported</Td>
|
||||
</>
|
||||
|
|
@ -804,8 +996,8 @@ const Packages = () => {
|
|||
<Icon status="warning">
|
||||
<OptimizeIcon />
|
||||
</Icon>{' '}
|
||||
EPEL {distribution.startsWith('rhel-8') ? '8' : '9'} Everything
|
||||
x86_64
|
||||
EPEL {distribution.startsWith('rhel-8') ? '8' : '9'}{' '}
|
||||
Everything x86_64
|
||||
</Td>
|
||||
<Td>Not supported</Td>
|
||||
</>
|
||||
|
|
@ -816,36 +1008,48 @@ const Packages = () => {
|
|||
</>
|
||||
)}
|
||||
</Tr>
|
||||
));
|
||||
))
|
||||
);
|
||||
return rows;
|
||||
};
|
||||
|
||||
const bodyContent = useMemo(() => {
|
||||
switch (true) {
|
||||
case debouncedSearchTermLengthOf1 && transformedPackages.length === 0:
|
||||
case debouncedSearchTermLengthOf1 &&
|
||||
!debouncedSearchTermIsGroup &&
|
||||
transformedPackages.length === 0 &&
|
||||
transformedGroups.length === 0:
|
||||
return TooShort();
|
||||
case (toggleSelected === 'toggle-selected' && packages.length === 0) ||
|
||||
case (toggleSelected === 'toggle-selected' &&
|
||||
packages.length === 0 &&
|
||||
groups.length === 0) ||
|
||||
(!debouncedSearchTerm && toggleSelected === 'toggle-available'):
|
||||
return <EmptySearch />;
|
||||
case (debouncedSearchTerm &&
|
||||
isLoadingRecommendedPackages &&
|
||||
toggleSourceRepos === RepoToggle.OTHER) ||
|
||||
(debouncedSearchTerm &&
|
||||
(isLoadingDistroPackages || isLoadingCustomPackages) &&
|
||||
(isLoadingDistroPackages ||
|
||||
isLoadingCustomPackages ||
|
||||
isLoadingDistroGroups) &&
|
||||
toggleSourceRepos === RepoToggle.INCLUDED):
|
||||
return <Searching />;
|
||||
case debouncedSearchTerm &&
|
||||
transformedPackages.length === 0 &&
|
||||
transformedGroups.length === 0 &&
|
||||
toggleSelected === 'toggle-available':
|
||||
return <NoResultsFound />;
|
||||
case debouncedSearchTerm &&
|
||||
toggleSelected === 'toggle-selected' &&
|
||||
toggleSourceRepos === RepoToggle.OTHER &&
|
||||
packages.length > 0:
|
||||
packages.length > 0 &&
|
||||
groups.length > 0:
|
||||
return <TryLookingUnderIncluded />;
|
||||
case debouncedSearchTerm && transformedPackages.length >= 100:
|
||||
return handleExactMatch();
|
||||
case (debouncedSearchTerm || toggleSelected === 'toggle-selected') &&
|
||||
transformedPackages.length < 100:
|
||||
transformedPackages.length < 100 &&
|
||||
transformedGroups.length < 100:
|
||||
return composePkgTable();
|
||||
default:
|
||||
return <></>;
|
||||
|
|
@ -857,17 +1061,21 @@ const Packages = () => {
|
|||
perPage,
|
||||
debouncedSearchTerm,
|
||||
debouncedSearchTermLengthOf1,
|
||||
debouncedSearchTermIsGroup,
|
||||
isLoadingCustomPackages,
|
||||
isLoadingDistroPackages,
|
||||
isLoadingRecommendedPackages,
|
||||
isSuccessRecommendedPackages,
|
||||
isLoadingDistroGroups,
|
||||
packages.length,
|
||||
groups.length,
|
||||
toggleSelected,
|
||||
toggleSourceRepos,
|
||||
transformedPackages,
|
||||
isSelectingPackage,
|
||||
recommendedRepositories,
|
||||
transformedPackages.length,
|
||||
transformedGroups.length,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
|
@ -886,7 +1094,10 @@ const Packages = () => {
|
|||
data-ouia-component-id="packages-search-input"
|
||||
type="text"
|
||||
validated={
|
||||
debouncedSearchTermLengthOf1 ? 'error' : 'default'
|
||||
debouncedSearchTermLengthOf1 &&
|
||||
!debouncedSearchTermIsGroup
|
||||
? 'error'
|
||||
: 'default'
|
||||
}
|
||||
placeholder="Type to search"
|
||||
aria-label="Search packages"
|
||||
|
|
@ -994,7 +1205,9 @@ const Packages = () => {
|
|||
</ToolbarItem>
|
||||
<ToolbarItem variant="pagination">
|
||||
<Pagination
|
||||
itemCount={transformedPackages.length}
|
||||
itemCount={
|
||||
transformedPackages.length + transformedGroups.length
|
||||
}
|
||||
perPage={perPage}
|
||||
page={page}
|
||||
onSetPage={handleSetPage}
|
||||
|
|
@ -1005,7 +1218,7 @@ const Packages = () => {
|
|||
</ToolbarContent>
|
||||
<ToolbarContent>
|
||||
<CustomHelperText
|
||||
hide={!debouncedSearchTermLengthOf1}
|
||||
hide={!debouncedSearchTermLengthOf1 || debouncedSearchTermIsGroup}
|
||||
textValue="The search value must be greater than 1 character"
|
||||
/>
|
||||
</ToolbarContent>
|
||||
|
|
@ -1030,7 +1243,7 @@ const Packages = () => {
|
|||
<Tbody>{bodyContent}</Tbody>
|
||||
</Table>
|
||||
<Pagination
|
||||
itemCount={transformedPackages.length}
|
||||
itemCount={transformedPackages.length + transformedGroups.length}
|
||||
perPage={perPage}
|
||||
page={page}
|
||||
onSetPage={handleSetPage}
|
||||
|
|
|
|||
|
|
@ -242,11 +242,22 @@ export const mapRequestToState = (request: BlueprintResponse): wizardState => {
|
|||
activationKey: request.customizations.subscription?.['activation-key'],
|
||||
},
|
||||
packages:
|
||||
request.customizations.packages?.map((pkg) => ({
|
||||
request.customizations.packages
|
||||
?.filter((pkg) => !pkg.startsWith('@'))
|
||||
.map((pkg) => ({
|
||||
name: pkg,
|
||||
summary: '',
|
||||
repository: '',
|
||||
})) || [],
|
||||
groups:
|
||||
request.customizations.packages
|
||||
?.filter((grp) => grp.startsWith('@'))
|
||||
.map((grp) => ({
|
||||
name: grp,
|
||||
description: '',
|
||||
repository: '',
|
||||
package_list: [],
|
||||
})) || [],
|
||||
stepValidations: {},
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,16 @@ const injectedRtkApi = api.injectEndpoints({
|
|||
listFeatures: build.query<ListFeaturesApiResponse, ListFeaturesApiArg>({
|
||||
query: () => ({ url: `/features/` }),
|
||||
}),
|
||||
searchPackageGroup: build.mutation<
|
||||
SearchPackageGroupApiResponse,
|
||||
SearchPackageGroupApiArg
|
||||
>({
|
||||
query: (queryArg) => ({
|
||||
url: `/package_groups/names`,
|
||||
method: "POST",
|
||||
body: queryArg.apiContentUnitSearchRequest,
|
||||
}),
|
||||
}),
|
||||
listRepositories: build.query<
|
||||
ListRepositoriesApiResponse,
|
||||
ListRepositoriesApiArg
|
||||
|
|
@ -75,6 +85,12 @@ const injectedRtkApi = api.injectEndpoints({
|
|||
export { injectedRtkApi as contentSourcesApi };
|
||||
export type ListFeaturesApiResponse = /** status 200 OK */ ApiFeatureSet;
|
||||
export type ListFeaturesApiArg = void;
|
||||
export type SearchPackageGroupApiResponse =
|
||||
/** status 200 OK */ ApiSearchPackageGroupResponse[];
|
||||
export type SearchPackageGroupApiArg = {
|
||||
/** request body */
|
||||
apiContentUnitSearchRequest: ApiContentUnitSearchRequest;
|
||||
};
|
||||
export type ListRepositoriesApiResponse =
|
||||
/** status 200 OK */ ApiRepositoryCollectionResponseRead;
|
||||
export type ListRepositoriesApiArg = {
|
||||
|
|
@ -147,6 +163,37 @@ export type ApiFeature = {
|
|||
export type ApiFeatureSet = {
|
||||
[key: string]: ApiFeature;
|
||||
};
|
||||
export type ApiSearchPackageGroupResponse = {
|
||||
/** Description of the package group found */
|
||||
description?: string;
|
||||
/** Package group ID */
|
||||
id?: string;
|
||||
/** Name of package group found */
|
||||
package_group_name?: string;
|
||||
/** Package list of the package group found */
|
||||
package_list?: string[];
|
||||
};
|
||||
export type ErrorsHandlerError = {
|
||||
/** An explanation specific to the problem */
|
||||
detail?: string;
|
||||
/** HTTP status code applicable to the error */
|
||||
status?: number;
|
||||
/** A summary of the problem */
|
||||
title?: string;
|
||||
};
|
||||
export type ErrorsErrorResponse = {
|
||||
errors?: ErrorsHandlerError[];
|
||||
};
|
||||
export type ApiContentUnitSearchRequest = {
|
||||
/** Maximum number of records to return for the search */
|
||||
limit?: number;
|
||||
/** Search string to search content unit names */
|
||||
search?: string;
|
||||
/** URLs of repositories to search */
|
||||
urls?: string[];
|
||||
/** List of repository UUIDs to search */
|
||||
uuids?: string[];
|
||||
};
|
||||
export type ApiSnapshotResponse = {
|
||||
/** Count of each content type */
|
||||
added_counts?: {
|
||||
|
|
@ -316,17 +363,6 @@ export type ApiRepositoryCollectionResponseRead = {
|
|||
links?: ApiLinks;
|
||||
meta?: ApiResponseMetadata;
|
||||
};
|
||||
export type ErrorsHandlerError = {
|
||||
/** An explanation specific to the problem */
|
||||
detail?: string;
|
||||
/** HTTP status code applicable to the error */
|
||||
status?: number;
|
||||
/** A summary of the problem */
|
||||
title?: string;
|
||||
};
|
||||
export type ErrorsErrorResponse = {
|
||||
errors?: ErrorsHandlerError[];
|
||||
};
|
||||
export type ApiRepositoryRequest = {
|
||||
/** Architecture to restrict client usage to */
|
||||
distribution_arch?: string;
|
||||
|
|
@ -375,16 +411,6 @@ export type ApiSearchRpmResponse = {
|
|||
/** Summary of the package found */
|
||||
summary?: string;
|
||||
};
|
||||
export type ApiContentUnitSearchRequest = {
|
||||
/** Maximum number of records to return for the search */
|
||||
limit?: number;
|
||||
/** Search string to search content unit names */
|
||||
search?: string;
|
||||
/** URLs of repositories to search */
|
||||
urls?: string[];
|
||||
/** List of repository UUIDs to search */
|
||||
uuids?: string[];
|
||||
};
|
||||
export type ApiSnapshotForDate = {
|
||||
/** Is the snapshot after the specified date */
|
||||
is_after?: boolean;
|
||||
|
|
@ -404,6 +430,7 @@ export type ApiListSnapshotByDateRequest = {
|
|||
};
|
||||
export const {
|
||||
useListFeaturesQuery,
|
||||
useSearchPackageGroupMutation,
|
||||
useListRepositoriesQuery,
|
||||
useCreateRepositoryMutation,
|
||||
useListRepositoriesRpmsQuery,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ import {
|
|||
Partition,
|
||||
Units,
|
||||
} from '../Components/CreateImageWizardV2/steps/FileSystem/FileSystemConfiguration';
|
||||
import { IBPackageWithRepositoryInfo } from '../Components/CreateImageWizardV2/steps/Packages/Packages';
|
||||
import {
|
||||
GroupWithRepositoryInfo,
|
||||
IBPackageWithRepositoryInfo,
|
||||
} from '../Components/CreateImageWizardV2/steps/Packages/Packages';
|
||||
import { AwsShareMethod } from '../Components/CreateImageWizardV2/steps/TargetEnvironment/Aws';
|
||||
import { AzureShareMethod } from '../Components/CreateImageWizardV2/steps/TargetEnvironment/Azure';
|
||||
import {
|
||||
|
|
@ -90,6 +93,7 @@ export type wizardState = {
|
|||
recommendedRepositories: ApiRepositoryResponseRead[];
|
||||
};
|
||||
packages: IBPackageWithRepositoryInfo[];
|
||||
groups: GroupWithRepositoryInfo[];
|
||||
details: {
|
||||
blueprintName: string;
|
||||
blueprintDescription: string;
|
||||
|
|
@ -153,6 +157,7 @@ const initialState: wizardState = {
|
|||
recommendedRepositories: [],
|
||||
},
|
||||
packages: [],
|
||||
groups: [],
|
||||
details: {
|
||||
blueprintName: '',
|
||||
blueprintDescription: '',
|
||||
|
|
@ -276,6 +281,10 @@ export const selectPackages = (state: RootState) => {
|
|||
return state.wizard.packages;
|
||||
};
|
||||
|
||||
export const selectGroups = (state: RootState) => {
|
||||
return state.wizard.groups;
|
||||
};
|
||||
|
||||
export const selectBlueprintName = (state: RootState) => {
|
||||
return state.wizard.details.blueprintName;
|
||||
};
|
||||
|
|
@ -601,6 +610,26 @@ export const wizardSlice = createSlice({
|
|||
1
|
||||
);
|
||||
},
|
||||
addGroup: (state, action: PayloadAction<GroupWithRepositoryInfo>) => {
|
||||
const existingGrpIndex = state.groups.findIndex(
|
||||
(grp) => grp.name === action.payload.name
|
||||
);
|
||||
|
||||
if (existingGrpIndex !== -1) {
|
||||
state.groups[existingGrpIndex] = action.payload;
|
||||
} else {
|
||||
state.groups.push(action.payload);
|
||||
}
|
||||
},
|
||||
removeGroup: (
|
||||
state,
|
||||
action: PayloadAction<GroupWithRepositoryInfo['name']>
|
||||
) => {
|
||||
state.groups.splice(
|
||||
state.groups.findIndex((grp) => grp.name === action.payload),
|
||||
1
|
||||
);
|
||||
},
|
||||
changeBlueprintName: (state, action: PayloadAction<string>) => {
|
||||
state.details.blueprintName = action.payload;
|
||||
},
|
||||
|
|
@ -684,6 +713,8 @@ export const {
|
|||
removeRecommendedRepository,
|
||||
addPackage,
|
||||
removePackage,
|
||||
addGroup,
|
||||
removeGroup,
|
||||
changeBlueprintName,
|
||||
changeBlueprintDescription,
|
||||
loadWizardState,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue