Skip to content

Commit 2cd4698

Browse files
authored
Merge branch 'master' into grid-in-host-discovery-not-aligned-well
2 parents 2fd5185 + 7a9658a commit 2cd4698

File tree

17 files changed

+233
-59
lines changed

17 files changed

+233
-59
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { SingleClusterPage } from '@openshift-assisted/ui-lib/ocm';
22
import { useParams } from 'react-router-dom-v5-compat';
3+
import ResetSingleClusterModal from './ResetSingleClusterModal';
34

45
const ClusterPage = () => {
56
const { clusterId } = useParams() as { clusterId: string };
6-
return <SingleClusterPage clusterId={clusterId} />;
7+
return <SingleClusterPage clusterId={clusterId} resetModal={<ResetSingleClusterModal />} />;
78
};
89

910
export default ClusterPage;

apps/assisted-disconnected-ui/src/components/CreateClusterWizard.tsx

+10-5
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import {
66
OpenShiftVersionsContextProvider,
77
NewFeatureSupportLevelProvider,
88
NewClusterWizard,
9+
ModalDialogsContextProvider,
910
} from '@openshift-assisted/ui-lib/ocm';
1011
import { Alert, PageSection, PageSectionVariants } from '@patternfly/react-core';
1112
import { useNavigate } from 'react-router-dom-v5-compat';
13+
import ResetSingleClusterModal from './ResetSingleClusterModal';
1214

1315
const CreateClusterWizard = () => {
1416
const [clusterId, isLoading, error] = useCluster();
@@ -32,11 +34,14 @@ const CreateClusterWizard = () => {
3234
return (
3335
<AlertsContextProvider>
3436
<ClusterWizardContextProvider>
35-
<OpenShiftVersionsContextProvider>
36-
<NewFeatureSupportLevelProvider loadingUi={<ClusterLoading />}>
37-
<NewClusterWizard />
38-
</NewFeatureSupportLevelProvider>
39-
</OpenShiftVersionsContextProvider>
37+
<ModalDialogsContextProvider>
38+
<OpenShiftVersionsContextProvider>
39+
<NewFeatureSupportLevelProvider loadingUi={<ClusterLoading />}>
40+
<NewClusterWizard />
41+
<ResetSingleClusterModal />
42+
</NewFeatureSupportLevelProvider>
43+
</OpenShiftVersionsContextProvider>
44+
</ModalDialogsContextProvider>
4045
</ClusterWizardContextProvider>
4146
</AlertsContextProvider>
4247
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import React from 'react';
2+
import {
3+
Button,
4+
Modal,
5+
ModalVariant,
6+
ButtonVariant,
7+
Text,
8+
TextContent,
9+
Stack,
10+
StackItem,
11+
Alert,
12+
} from '@patternfly/react-core';
13+
import { useModalDialogsContext, ClustersService } from '@openshift-assisted/ui-lib/ocm';
14+
import {
15+
getApiErrorMessage,
16+
handleApiError,
17+
useTranslation,
18+
} from '@openshift-assisted/ui-lib/common';
19+
import { useNavigate } from 'react-router-dom-v5-compat';
20+
21+
const ResetSingleClusterModal: React.FC = () => {
22+
const navigate = useNavigate();
23+
const [isLoading, setIsLoading] = React.useState(false);
24+
const [error, setError] = React.useState<{ title: string; message: string }>();
25+
const { resetSingleClusterDialog } = useModalDialogsContext();
26+
const { data, isOpen, close: onClose } = resetSingleClusterDialog;
27+
const cluster = data?.cluster;
28+
const { t } = useTranslation();
29+
30+
if (!cluster) {
31+
return null;
32+
}
33+
34+
const handleClose = () => {
35+
setError(undefined);
36+
onClose();
37+
};
38+
39+
const handleResetAsync = async () => {
40+
try {
41+
setError(undefined);
42+
setIsLoading(true);
43+
await ClustersService.remove(cluster.id);
44+
navigate(`/`);
45+
} catch (e) {
46+
handleApiError(e, () => {
47+
setError({
48+
title: t('ai:Failed to reset cluster installation'),
49+
message: getApiErrorMessage(e),
50+
});
51+
});
52+
} finally {
53+
setIsLoading(false);
54+
}
55+
};
56+
57+
const actions = [
58+
<Button
59+
key="reset"
60+
variant={ButtonVariant.danger}
61+
onClick={() => void handleResetAsync()}
62+
isDisabled={isLoading}
63+
isLoading={isLoading}
64+
>
65+
{t('ai:Reset')}
66+
</Button>,
67+
<Button key="cancel" variant={ButtonVariant.link} onClick={handleClose} isDisabled={isLoading}>
68+
{t('ai:Cancel')}
69+
</Button>,
70+
];
71+
72+
return (
73+
<Modal
74+
title={t('ai:Reset cluster')}
75+
titleIconVariant="warning"
76+
isOpen={isOpen}
77+
variant={ModalVariant.small}
78+
actions={actions}
79+
onClose={handleClose}
80+
>
81+
<Stack hasGutter>
82+
<StackItem>
83+
<TextContent>
84+
<Text component="p">
85+
{t('ai:This will remove all current configurations and will revert to the defaults.')}
86+
</Text>
87+
88+
<Text component="p">{t('ai:Are you sure you want to reset the cluster?')}</Text>
89+
</TextContent>
90+
</StackItem>
91+
{error && (
92+
<StackItem>
93+
<Alert isInline variant="danger" title={error.title}>
94+
{error.message}
95+
</Alert>
96+
</StackItem>
97+
)}
98+
</Stack>
99+
</Modal>
100+
);
101+
};
102+
103+
export default ResetSingleClusterModal;

libs/locales/.i18next-parser.config.cjs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
const defaultNS = process.env.TRANSLATION_NAMESPACE || 'translation';
22

33
module.exports = {
4-
input: '../ui-lib/lib/**/*.{js,jsx,ts,tsx}',
4+
input: [
5+
'../ui-lib/lib/**/*.{js,jsx,ts,tsx}',
6+
'../../apps/assisted-disconnected-ui/src/**/*.{js,jsx,ts,tsx}',
7+
],
58
contextSeparator: '_',
69
createOldCatalogs: false,
710
keySeparator: false,

libs/locales/lib/en/translation.json

+5
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
"ai:Approve host to join infrastructure environment": "Approve host to join infrastructure environment",
103103
"ai:Approve hosts dialog": "Approve hosts dialog",
104104
"ai:Approve hosts to join infrastructure environment": "Approve hosts to join infrastructure environment",
105+
"ai:Are you sure you want to reset the cluster?": "Are you sure you want to reset the cluster?",
105106
"ai:arm64": "arm64",
106107
"ai:arm64 is not supported in this OpenShift version": "arm64 is not supported in this OpenShift version",
107108
"ai:At least 3 hosts are required, capable of functioning as control plane nodes.": "At least 3 hosts are required that are capable of functioning as control plane nodes.",
@@ -327,6 +328,7 @@
327328
"ai:Failed to fetch cluster credentials.": "Failed to fetch cluster credentials.",
328329
"ai:Failed to get Provisioning Configuration": "Failed to get Provisioning Configuration",
329330
"ai:Failed to patch assisted-image-service route for new domain.": "Failed to patch assisted-image-service route for new domain.",
331+
"ai:Failed to reset cluster installation": "Failed to reset cluster installation",
330332
"ai:Failed to save ClusterDeployment": "Failed to save ClusterDeployment",
331333
"ai:Failed to save configuration": "Failed to save configuration",
332334
"ai:Failed to save host selection.": "Failed to save host selection.",
@@ -706,6 +708,8 @@
706708
"ai:Report a bug": "Report a bug",
707709
"ai:Required field": "Required field",
708710
"ai:Required.": "Required.",
711+
"ai:Reset": "Reset",
712+
"ai:Reset cluster": "Reset cluster",
709713
"ai:Reset host": "Reset host",
710714
"ai:Resetting": "Resetting",
711715
"ai:Returning to the infrastructure environment": "Returning to the infrastructure environment",
@@ -846,6 +850,7 @@
846850
"ai:This stage may take a while to finish. To view detailed information, click the events log link below.": "This stage might take a while to finish. To view detailed information, click the events log link.",
847851
"ai:This subnet range is not available on all hosts": "This subnet range is not available on all hosts",
848852
"ai:This will determine for the infrastructure environment which kind of hosts would be able to be added. If the hosts that you want to add are using DHCP server, select this option, else, select the static IP.": "This will determine for the infrastructure environment which kind of hosts would be able to be added. If the hosts that you want to add are using DHCP server, select this option. If not, select the static IP.",
853+
"ai:This will remove all current configurations and will revert to the defaults.": "This will remove all current configurations and will revert to the defaults.",
849854
"ai:Time": "Time",
850855
"ai:Time synced between host and service": "Time synced between host and service",
851856
"ai:to add a number.": "to add a number.",

libs/ui-lib/lib/cim/components/ClusterDeployment/ClusterDeploymentDetailsForm.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
ClusterDetailsFormFieldsProps,
1818
} from './ClusterDetailsFormFields';
1919
import { useFormikContext } from 'formik';
20-
import { ClusterDetailsValues, CpuArchitecture } from '../../../common';
20+
import { ClusterDetailsValues, CpuArchitecture, SupportedCpuArchitecture } from '../../../common';
2121
import { ClusterDeploymentWizardContext } from './ClusterDeploymentWizardContext';
2222
import { ValidationSection } from './components/ValidationSection';
2323
import { toNumber } from 'lodash-es';
@@ -117,7 +117,7 @@ const ClusterDeploymentDetailsForm: React.FC<ClusterDeploymentDetailsFormProps>
117117

118118
const highlyAvailableSupported = toNumber(version?.version?.split('.')?.[1]) >= 18;
119119
return [
120-
isMulti ? cpuArchitectures : version?.cpuArchitectures,
120+
(isMulti ? cpuArchitectures : version?.cpuArchitectures) as SupportedCpuArchitecture[],
121121
highlyAvailableSupported && !isMulti,
122122
];
123123
}, [allVersions, values.openshiftVersion]);

libs/ui-lib/lib/cim/components/ClusterDeployment/ClusterDetailsFormFields.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useFormikContext } from 'formik';
55
import { OpenShiftVersionDropdown, OpenShiftVersionModal } from '../../../common';
66
import { StaticTextField } from '../../../common/components/ui/StaticTextField';
77
import { PullSecret } from '../../../common/components/clusters';
8-
import { OpenshiftVersionOptionType } from '../../../common/types';
8+
import { OpenshiftVersionOptionType, SupportedCpuArchitecture } from '../../../common/types';
99
import {
1010
InputField,
1111
RichInputField,
@@ -23,7 +23,7 @@ export type ClusterDetailsFormFieldsProps = {
2323
versions: OpenshiftVersionOptionType[];
2424
allVersions: OpenshiftVersionOptionType[];
2525
isNutanix?: boolean;
26-
cpuArchitectures?: string[];
26+
cpuArchitectures?: SupportedCpuArchitecture[];
2727
allowHighlyAvailable?: boolean;
2828
};
2929

libs/ui-lib/lib/cim/components/Hypershift/DetailsPage/HypershiftKubeconfigDownload.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ const HypershiftKubeconfigDownload = ({
1515
fetchSecret,
1616
}: HypershiftKubeconfigDownloadProps) => {
1717
const { t } = useTranslation();
18-
const kubeconfigSecretName = hostedCluster.status?.customkubeconfig
19-
? hostedCluster.status.customkubeconfig?.name
18+
const kubeconfigSecretName = hostedCluster.status?.customKubeconfig
19+
? hostedCluster.status.customKubeconfig?.name
2020
: hostedCluster.status?.kubeconfig?.name;
2121
const handleKubeconfigDownload = async () => {
2222
const kubeconfigSecretNamespace = hostedCluster.metadata?.namespace;

libs/ui-lib/lib/cim/components/Hypershift/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ export type HostedClusterK8sResource = K8sResourceCommon & {
157157
kubeconfig?: {
158158
name: string;
159159
};
160-
customkubeconfig?: {
160+
customKubeconfig?: {
161161
name: string;
162162
};
163163
kubeadminPassword?: {

libs/ui-lib/lib/cim/components/common/CpuArchitectureDropdown.tsx

+51-40
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as React from 'react';
2-
import { FormGroup } from '@patternfly/react-core';
3-
import { Dropdown, DropdownItem, DropdownToggle } from '@patternfly/react-core/deprecated';
2+
import { Dropdown, DropdownItem, FormGroup, MenuToggle } from '@patternfly/react-core';
43
import { useTranslation } from '../../../common/hooks/use-translation-wrapper';
54
import {
65
architectureData,
@@ -12,30 +11,18 @@ import {
1211
import { useField } from 'formik';
1312
import { useFormikHelpers } from '../../../common/hooks/useFormikHelpers';
1413

15-
const allDropdownItems = {
16-
[CpuArchitecture.x86]: (
17-
<DropdownItem key={'x86_64'} id="x86_64" description={architectureData['x86_64'].description}>
18-
{architectureData['x86_64'].label}
19-
</DropdownItem>
20-
),
21-
[CpuArchitecture.ARM]: (
22-
<DropdownItem key={'arm64'} id="arm64" description={architectureData['arm64'].description}>
23-
{architectureData['arm64'].label}
24-
</DropdownItem>
25-
),
26-
[CpuArchitecture.s390x]: (
27-
<DropdownItem key={'s390x'} id="s390x" description={architectureData['s390x'].description}>
28-
{architectureData['s390x'].label}
29-
</DropdownItem>
30-
),
31-
};
14+
const cimCpuArchitectures = [
15+
CpuArchitecture.x86,
16+
CpuArchitecture.ARM,
17+
CpuArchitecture.s390x,
18+
] as SupportedCpuArchitecture[];
3219

3320
const CpuArchitectureDropdown = ({
3421
isDisabled = false,
3522
cpuArchitectures,
3623
}: {
3724
isDisabled?: boolean;
38-
cpuArchitectures?: string[];
25+
cpuArchitectures?: SupportedCpuArchitecture[];
3926
}) => {
4027
const { t } = useTranslation();
4128
const [{ name, value }, , { setValue }] = useField<SupportedCpuArchitecture | ''>(
@@ -45,44 +32,68 @@ const CpuArchitectureDropdown = ({
4532
const fieldId = getFieldId(name, 'input');
4633
const { setValue: setUserManagedNetworking } = useFormikHelpers<boolean>('userManagedNetworking');
4734

48-
const onCpuArchSelect = (e?: React.SyntheticEvent<HTMLDivElement>) => {
35+
const onCpuArchSelect = (e?: React.MouseEvent<Element, MouseEvent>) => {
4936
const val = e?.currentTarget.id as SupportedCpuArchitecture;
5037
setValue(val);
5138
setUserManagedNetworking(val === CpuArchitecture.s390x);
5239

5340
setCpuArchOpen(false);
5441
};
5542

56-
const dropdownItems = React.useMemo(() => {
57-
if (!cpuArchitectures) {
58-
return Object.values(allDropdownItems);
59-
} else {
60-
return Object.entries(allDropdownItems)
61-
.filter(([key, _]) => cpuArchitectures.includes(key))
62-
.map(([_, val]) => val);
63-
}
64-
}, [cpuArchitectures]);
43+
const dropdownItems = React.useMemo(
44+
() =>
45+
cimCpuArchitectures.map((arch) => {
46+
const isItemEnabled = !cpuArchitectures || cpuArchitectures.includes(arch);
47+
const disabledReason = t(
48+
'ai:This option is not available with the selected OpenShift version',
49+
);
50+
return (
51+
<DropdownItem
52+
key={arch}
53+
id={arch}
54+
isAriaDisabled={!isItemEnabled}
55+
selected={arch === value}
56+
description={architectureData[arch].description}
57+
tooltipProps={{ content: disabledReason, position: 'top' }}
58+
onClick={(e: React.MouseEvent) => e.preventDefault()}
59+
>
60+
{architectureData[arch].label}
61+
</DropdownItem>
62+
);
63+
}),
64+
[cpuArchitectures, t, value],
65+
);
6566

6667
React.useEffect(() => {
6768
if (!isDisabled && value !== '' && cpuArchitectures && !cpuArchitectures?.includes(value)) {
68-
setValue('');
69+
const defaultVal = cpuArchitectures?.[0] || CpuArchitecture.x86;
70+
setValue(defaultVal);
6971
}
7072
}, [cpuArchitectures, isDisabled, setValue, value]);
7173

7274
return !isDisabled ? (
73-
<FormGroup isInline fieldId={fieldId} label={t('ai:CPU architecture')} isRequired>
75+
<FormGroup
76+
isInline
77+
fieldId={fieldId}
78+
label={t('ai:CPU architecture')}
79+
isRequired
80+
name={'cpuArchiteture'}
81+
>
7482
<Dropdown
75-
toggle={
76-
<DropdownToggle onToggle={() => setCpuArchOpen(!cpuArchOpen)} className="pf-u-w-100">
83+
toggle={(toggleRef) => (
84+
<MenuToggle
85+
ref={toggleRef}
86+
onClick={() => setCpuArchOpen(!cpuArchOpen)}
87+
className="pf-u-w-100"
88+
>
7789
{value ? architectureData[value].label : t('ai:CPU architecture')}
78-
</DropdownToggle>
79-
}
80-
name="cpuArchitecture"
90+
</MenuToggle>
91+
)}
8192
isOpen={cpuArchOpen}
8293
onSelect={onCpuArchSelect}
83-
dropdownItems={dropdownItems}
84-
className="pf-u-w-100"
85-
/>
94+
>
95+
{dropdownItems}
96+
</Dropdown>
8697
</FormGroup>
8798
) : (
8899
<StaticField name={'cpuArchitecture'} label={t('ai:CPU architecture')} isRequired>

0 commit comments

Comments
 (0)