debian-image-builder-frontend/src/Components/CreateImageWizard/steps/Review/ReviewStepTextLists.tsx
regexowl d8bb3485d8 Wizard: Add v5 PF slug to classNames
This adds v5 PF slug to classNames throughout the code base, making the formatting work properly.
2024-11-25 16:54:59 +01:00

760 lines
23 KiB
TypeScript

import React, { useEffect, useMemo } from 'react';
import {
Alert,
Button,
Popover,
Text,
TextContent,
TextList,
TextListItem,
TextListVariants,
TextListItemVariants,
TextVariants,
FormGroup,
} from '@patternfly/react-core';
import { ExclamationTriangleIcon } from '@patternfly/react-icons';
import ActivationKeyInformation from './../Registration/ActivationKeyInformation';
import {
PackagesTable,
RepositoriesTable,
SnapshotTable,
} from './ReviewStepTables';
import { FSReviewTable } from './ReviewStepTables';
import {
RELEASES,
RHEL_8,
RHEL_8_FULL_SUPPORT,
RHEL_8_MAINTENANCE_SUPPORT,
RHEL_9,
targetOptions,
UNIT_GIB,
} from '../../../../constants';
import { useListSnapshotsByDateMutation } from '../../../../store/contentSourcesApi';
import { useAppSelector } from '../../../../store/hooks';
import { useGetSourceListQuery } from '../../../../store/provisioningApi';
import { useShowActivationKeyQuery } from '../../../../store/rhsmApi';
import {
selectActivationKey,
selectArchitecture,
selectAwsAccountId,
selectAwsShareMethod,
selectAzureShareMethod,
selectAzureSource,
selectAzureResourceGroup,
selectAzureSubscriptionId,
selectAzureTenantId,
selectAwsSourceId,
selectBlueprintDescription,
selectBlueprintName,
selectCustomRepositories,
selectDistribution,
selectGcpAccountType,
selectGcpEmail,
selectGcpShareMethod,
selectPackages,
selectGroups,
selectRegistrationType,
selectFileSystemConfigurationType,
selectRecommendedRepositories,
selectSnapshotDate,
selectUseLatest,
selectPartitions,
selectFirstBootScript,
} from '../../../../store/wizardSlice';
import { toMonthAndYear, yyyyMMddFormat } from '../../../../Utilities/time';
import {
getConversionFactor,
MinimumSizePopover,
Partition,
} from '../FileSystem/FileSystemTable';
import { MajorReleasesLifecyclesChart } from '../ImageOutput/ReleaseLifecycle';
import OscapProfileInformation from '../Oscap/OscapProfileInformation';
import { PopoverActivation } from '../Registration/ActivationKeysList';
const ExpirationWarning = () => {
return (
<div className="pf-v5-u-mr-sm pf-v5-u-font-size-sm pf-v5-u-warning-color-200">
<ExclamationTriangleIcon /> Expires 14 days after creation
</div>
);
};
export const ImageOutputList = () => {
const distribution = useAppSelector(selectDistribution);
const arch = useAppSelector(selectArchitecture);
return (
<TextContent>
{distribution === RHEL_8 && (
<>
<Text className="pf-v5-u-font-size-sm">
{RELEASES.get(distribution)} will be supported through{' '}
{toMonthAndYear(RHEL_8_FULL_SUPPORT[1])}, with optional ELS support
through {toMonthAndYear(RHEL_8_MAINTENANCE_SUPPORT[1])}. Consider
building an image with {RELEASES.get(RHEL_9)} to extend the support
period.
</Text>
<FormGroup label="Release lifecycle">
<MajorReleasesLifecyclesChart />
</FormGroup>
<br />
</>
)}
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Release
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{RELEASES.get(distribution)}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Architecture
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>{arch}</TextListItem>
</TextList>
</TextContent>
);
};
export const FSCList = () => {
const fileSystemConfigurationType = useAppSelector(
selectFileSystemConfigurationType
);
const partitions = useAppSelector(selectPartitions);
return (
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Configuration type
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="partitioning-auto-manual"
>
{fileSystemConfigurationType === 'manual' ? 'Manual' : 'Automatic'}
{fileSystemConfigurationType === 'manual' && (
<>
{' '}
<Popover
position="bottom"
headerContent="Partitions"
hasAutoWidth
minWidth="30rem"
bodyContent={<FSReviewTable />}
>
<Button
data-testid="file-system-configuration-popover"
variant="link"
aria-label="File system configuration info"
aria-describedby="file-system-configuration-info"
className="pf-v5-u-pt-0 pf-v5-u-pb-0"
>
View partitions
</Button>
</Popover>
</>
)}
</TextListItem>
{fileSystemConfigurationType === 'manual' && (
<>
<TextListItem component={TextListItemVariants.dt}>
Image size (minimum) <MinimumSizePopover />
</TextListItem>
<MinSize partitions={partitions} />
</>
)}
</TextList>
</TextContent>
);
};
type MinSizeProps = {
partitions: Partition[];
};
export const MinSize = ({ partitions }: MinSizeProps) => {
let minSize = '';
if (partitions) {
let size = 0;
for (const partition of partitions) {
size += Number(partition.min_size) * getConversionFactor(partition.unit);
}
size = Number((size / UNIT_GIB).toFixed(1));
if (size < 1) {
minSize = `Less than 1 GiB`;
} else {
minSize = `${size} GiB`;
}
}
return (
<TextListItem component={TextListItemVariants.dd}> {minSize} </TextListItem>
);
};
export const TargetEnvAWSList = () => {
const { isSuccess } = useGetSourceListQuery({
provider: 'aws',
});
const awsAccountId = useAppSelector(selectAwsAccountId);
const awsShareMethod = useAppSelector(selectAwsShareMethod);
const sourceId = useAppSelector(selectAwsSourceId);
const { source } = useGetSourceListQuery(
{
provider: 'aws',
},
{
selectFromResult: ({ data }) => ({
source: data?.data?.find((source) => source.id === sourceId),
}),
}
);
return (
<TextContent>
<Text component={TextVariants.h3}>{targetOptions.aws}</Text>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Image type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Red Hat hosted image
<br />
<ExpirationWarning />
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Shared to account
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{awsAccountId}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
{awsShareMethod === 'sources' ? 'Source' : null}
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{isSuccess && awsShareMethod === 'sources' ? source?.name : null}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Default region
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
us-east-1
</TextListItem>
</TextList>
</TextContent>
);
};
export const TargetEnvGCPList = () => {
const accountType = useAppSelector(selectGcpAccountType);
const sharedMethod = useAppSelector(selectGcpShareMethod);
const email = useAppSelector(selectGcpEmail);
return (
<TextContent>
<Text component={TextVariants.h3}>{targetOptions.gcp}</Text>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Image type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Red Hat hosted image
<br />
<ExpirationWarning />
</TextListItem>
<>
{sharedMethod === 'withInsights' ? (
<>
<TextListItem component={TextListItemVariants.dt}>
Shared with
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Red Hat Insights only
<br />
</TextListItem>
</>
) : (
<>
<TextListItem component={TextListItemVariants.dt}>
Account type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{accountType === 'group'
? 'Google group'
: accountType === 'serviceAccount'
? 'Service account'
: accountType === 'user'
? 'Google account'
: 'Domain'}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
{accountType === 'domain' ? 'Domain' : 'Principal'}
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{email || accountType}
</TextListItem>
</>
)}
</>
</TextList>
</TextContent>
);
};
export const TargetEnvAzureList = () => {
const { data: rawAzureSources, isSuccess: isSuccessAzureSources } =
useGetSourceListQuery({ provider: 'azure' });
const shareMethod = useAppSelector(selectAzureShareMethod);
const tenantId = useAppSelector(selectAzureTenantId);
const azureSource = useAppSelector(selectAzureSource);
const azureResourceGroup = useAppSelector(selectAzureResourceGroup);
const subscriptionId = useAppSelector(selectAzureSubscriptionId);
return (
<TextContent>
<Text component={TextVariants.h3}>{targetOptions.azure}</Text>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Image type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Red Hat hosted image
<br />
<ExpirationWarning />
</TextListItem>
{shareMethod === 'sources' && isSuccessAzureSources && (
<>
<TextListItem component={TextListItemVariants.dt}>
Azure Source
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{
rawAzureSources.data?.find(
(source) => source.id === azureSource
)?.name
}
</TextListItem>
</>
)}
{shareMethod === 'manual' && (
<>
<TextListItem component={TextListItemVariants.dt}>
Azure tenant ID
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{tenantId}
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Subscription ID
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{subscriptionId}
</TextListItem>
</>
)}
<TextListItem component={TextListItemVariants.dt}>
Resource group
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{azureResourceGroup}
</TextListItem>
</TextList>
</TextContent>
);
};
export const TargetEnvOciList = () => {
return (
<TextContent>
<Text component={TextVariants.h3}>{targetOptions.oci}</Text>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Object Storage URL
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
The URL for the built image will be ready to copy
</TextListItem>
</TextList>
</TextContent>
);
};
export const TargetEnvOtherList = () => {
return (
<>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Image type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Built image will be available for download
</TextListItem>
</TextList>
</>
);
};
export const ContentList = ({
snapshottingEnabled,
}: {
snapshottingEnabled: boolean;
}) => {
const customRepositories = useAppSelector(selectCustomRepositories);
const packages = useAppSelector(selectPackages);
const groups = useAppSelector(selectGroups);
const recommendedRepositories = useAppSelector(selectRecommendedRepositories);
const snapshotDate = useAppSelector(selectSnapshotDate);
const useLatest = useAppSelector(selectUseLatest);
const customAndRecommendedRepositoryUUIDS = useMemo(
() =>
[
...customRepositories.map(({ id }) => id),
...recommendedRepositories.map(({ uuid }) => uuid),
] as string[],
[customRepositories, recommendedRepositories]
);
const [listSnapshotsByDate, { data, isSuccess, isLoading, isError }] =
useListSnapshotsByDateMutation();
useEffect(() => {
listSnapshotsByDate({
apiListSnapshotByDateRequest: {
repository_uuids: customAndRecommendedRepositoryUUIDS,
date: useLatest ? yyyyMMddFormat(new Date()) : snapshotDate,
},
});
}, [
customAndRecommendedRepositoryUUIDS,
listSnapshotsByDate,
snapshotDate,
useLatest,
]);
const duplicatePackages = packages.filter(
(item, index) => packages.indexOf(item) !== index
);
const noRepositoriesSelected =
customAndRecommendedRepositoryUUIDS.length === 0;
const hasSnapshotDateAfter = data?.data?.some(({ is_after }) => is_after);
const snapshottingText = useMemo(() => {
switch (true) {
case isLoading:
return '';
case useLatest:
return 'Use latest';
case !!snapshotDate:
return `State as of ${snapshotDate}`;
default:
return '';
}
}, [isLoading, useLatest, snapshotDate]);
return (
<>
<TextContent>
<TextList component={TextListVariants.dl}>
{snapshottingEnabled ? (
<>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Repository snapshot
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="snapshot-method"
>
<Popover
position="bottom"
headerContent={
useLatest
? 'Repositories as of today'
: `Repositories as of ${snapshotDate}`
}
hasAutoWidth
minWidth="60rem"
bodyContent={
<SnapshotTable snapshotForDate={data?.data || []} />
}
>
<Button
variant="link"
aria-label="Snapshot method"
className="pf-v5-u-p-0"
isDisabled={noRepositoriesSelected || isLoading || isError}
isLoading={isLoading}
>
{snapshottingText}
</Button>
</Popover>
{!useLatest &&
!isLoading &&
isSuccess &&
hasSnapshotDateAfter ? (
<Alert
variant="warning"
isInline
isPlain
title="A snapshot for this date is not available for some repositories."
/>
) : (
''
)}
</TextListItem>
</>
) : (
''
)}
<TextListItem component={TextListItemVariants.dt}>
Custom repositories
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="custom-repositories-count"
>
{customRepositories?.length + recommendedRepositories.length > 0 ? (
<Popover
position="bottom"
headerContent="Custom repositories"
hasAutoWidth
minWidth="30rem"
bodyContent={<RepositoriesTable />}
>
<Button
variant="link"
aria-label="About custom repositories"
className="pf-v5-u-p-0"
>
{customRepositories?.length +
recommendedRepositories.length || 0}
</Button>
</Popover>
) : (
0
)}
</TextListItem>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Additional packages
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="chosen-packages-count"
>
{packages?.length > 0 || groups?.length > 0 ? (
<Popover
position="bottom"
headerContent="Additional packages"
hasAutoWidth
minWidth="60rem"
bodyContent={<PackagesTable />}
>
<Button
variant="link"
aria-label="About packages"
className="pf-v5-u-p-0"
>
{packages?.length + groups?.length}
</Button>
</Popover>
) : (
0
)}
</TextListItem>
</TextList>
</TextContent>
{duplicatePackages.length > 0 && (
<Alert
title="Can not guarantee where some selected packages will come from"
variant="warning"
isInline
>
Some of the packages added to this image belong to multiple added
repositories. We can not guarantee which repository the package will
come from.
</Alert>
)}
</>
);
};
export const RegisterLaterList = () => {
return (
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Registration type
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
Register the system later
</TextListItem>
</TextList>
</TextContent>
);
};
export const RegisterNowList = () => {
const activationKey = useAppSelector(selectActivationKey);
const registrationType = useAppSelector(selectRegistrationType);
const { isError } = useShowActivationKeyQuery(
// @ts-ignore type of 'activationKey' might not be strictly compatible with the expected type for 'name'.
{ name: activationKey },
{
skip: !activationKey,
}
);
return (
<>
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Registration type
</TextListItem>
<TextListItem
component={TextListItemVariants.dd}
data-testid="review-registration"
>
<TextList isPlain>
{registrationType?.startsWith('register-now') && (
<TextListItem>
Register with Red Hat Subscription Manager (RHSM)
<br />
</TextListItem>
)}
{(registrationType === 'register-now-insights' ||
registrationType === 'register-now-rhc') && (
<TextListItem>
Connect to Red Hat Insights
<br />
</TextListItem>
)}
{registrationType === 'register-now-rhc' && (
<TextListItem>
Use remote host configuration (rhc) utility
<br />
</TextListItem>
)}
</TextList>
</TextListItem>
<TextListItem component={TextListItemVariants.dt}>
Activation key <PopoverActivation />
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
<ActivationKeyInformation />
</TextListItem>
</TextList>
</TextContent>
{isError && (
<Alert
title="Information about the activation key unavailable"
variant="danger"
isPlain
isInline
>
Information about the activation key cannot be loaded. Please check
the key was not removed and try again later.
</Alert>
)}
</>
);
};
export const DetailsList = () => {
const blueprintName = useAppSelector(selectBlueprintName);
const blueprintDescription = useAppSelector(selectBlueprintDescription);
return (
<TextContent>
<TextList component={TextListVariants.dl}>
{blueprintName && (
<>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Blueprint name
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{blueprintName}
</TextListItem>
</>
)}
{blueprintDescription && (
<>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
Description
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{blueprintDescription}
</TextListItem>
</>
)}
</TextList>
</TextContent>
);
};
export const OscapList = () => {
return <OscapProfileInformation allowChangingCompliancePolicy={true} />;
};
export const FirstBootList = () => {
const isFirstbootEnabled = !!useAppSelector(selectFirstBootScript);
return (
<TextContent>
<TextList component={TextListVariants.dl}>
<TextListItem
component={TextListItemVariants.dt}
className="pf-v5-u-min-width"
>
First boot script
</TextListItem>
<TextListItem component={TextListItemVariants.dd}>
{isFirstbootEnabled ? 'Enabled' : 'Disabled'}
</TextListItem>
</TextList>
</TextContent>
);
};