Skip to content

Commit e8178c7

Browse files
committed
add reset button to disconnected assisted installer ui
Signed-off-by: Elay Aharoni <elayaha@gmail.com>
1 parent bae0700 commit e8178c7

File tree

11 files changed

+183
-11
lines changed

11 files changed

+183
-11
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,114 @@
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+
LoadingState,
18+
useTranslation,
19+
} from '@openshift-assisted/ui-lib/common';
20+
import { useNavigate } from 'react-router-dom-v5-compat';
21+
22+
const ResetSingleClusterModal: React.FC = () => {
23+
const navigate = useNavigate();
24+
const [isLoading, setIsLoading] = React.useState(false);
25+
const [error, setError] = React.useState<{ title: string; message: string }>();
26+
const { resetSingleClusterDialog } = useModalDialogsContext();
27+
const { data, isOpen, close: onClose } = resetSingleClusterDialog;
28+
const cluster = data?.cluster;
29+
const { t } = useTranslation();
30+
31+
if (!cluster) {
32+
return null;
33+
}
34+
35+
const handleClose = () => {
36+
setError(undefined);
37+
onClose();
38+
};
39+
40+
const handleResetAsync = async () => {
41+
try {
42+
setError(undefined);
43+
setIsLoading(true);
44+
await ClustersService.remove(cluster.id);
45+
navigate(`/`);
46+
} catch (e) {
47+
handleApiError(e, () => {
48+
setError({
49+
title: t('ai:Failed to reset cluster installation'),
50+
message: getApiErrorMessage(e),
51+
});
52+
});
53+
} finally {
54+
setIsLoading(false);
55+
}
56+
};
57+
58+
const getModalContent = () => {
59+
if (isLoading) {
60+
return <LoadingState content={t('ai:Resetting cluster installation...')} />;
61+
}
62+
63+
return (
64+
<Stack hasGutter>
65+
<StackItem>
66+
<TextContent>
67+
<Text component="p">
68+
{t('ai:This will remove all current configurations and will revert to the defaults.')}
69+
</Text>
70+
71+
<Text component="p">{t('ai:Are you sure you want to reset the cluster?')}</Text>
72+
</TextContent>
73+
</StackItem>
74+
{error && (
75+
<StackItem>
76+
<Alert isInline variant="danger" title={error.title}>
77+
{error.message}
78+
</Alert>
79+
;
80+
</StackItem>
81+
)}
82+
</Stack>
83+
);
84+
};
85+
86+
const actions = [
87+
<Button
88+
key="reset"
89+
variant={ButtonVariant.danger}
90+
onClick={() => void handleResetAsync()}
91+
isDisabled={isLoading}
92+
>
93+
{t('ai:Reset')}
94+
</Button>,
95+
<Button key="cancel" variant={ButtonVariant.link} onClick={handleClose} isDisabled={isLoading}>
96+
{t('ai:Cancel')}
97+
</Button>,
98+
];
99+
100+
return (
101+
<Modal
102+
title={t('ai:Reset cluster')}
103+
titleIconVariant="warning"
104+
isOpen={isOpen}
105+
variant={ModalVariant.small}
106+
actions={actions}
107+
onClose={handleClose}
108+
>
109+
{getModalContent()}
110+
</Modal>
111+
);
112+
};
113+
114+
export default ResetSingleClusterModal;

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

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

33
module.exports = {
4-
input: '../ui-lib/lib/**/*.{js,jsx,ts,tsx}',
4+
input: ['../ui-lib/lib/**/*.{js,jsx,ts,tsx}', '../../apps/**/**/*.{js,jsx,ts,tsx}'],
55
contextSeparator: '_',
66
createOldCatalogs: false,
77
keySeparator: false,

libs/locales/lib/en/translation.json

+9-1
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,8 +708,11 @@
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",
715+
"ai:Resetting cluster installation...": "Resetting cluster installation...",
711716
"ai:Returning to the infrastructure environment": "Returning to the infrastructure environment",
712717
"ai:Review and create": "Review and create",
713718
"ai:Role": "Role",
@@ -846,6 +851,7 @@
846851
"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.",
847852
"ai:This subnet range is not available on all hosts": "This subnet range is not available on all hosts",
848853
"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.",
854+
"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.",
849855
"ai:Time": "Time",
850856
"ai:Time synced between host and service": "Time synced between host and service",
851857
"ai:to add a number.": "to add a number.",
@@ -930,5 +936,7 @@
930936
"ai:You have opted out of formatting bootable disks on some hosts. To ensure the hosts reboot into the expected installation disk, manual user intervention may be required during OpenShift installation.": "You have opted out of formatting bootable disks on some hosts. To ensure the hosts reboot into the expected installation disk, manual user intervention might be required during OpenShift installation.",
931937
"ai:You'll first need to have a storage operator in order to create the storage class. If you don't have one installed, we recommend OpenShift Data Foundation operator, but you may use any.": "You need a storage operator installed, such as OpenShift Data Foundation, to create the storage class.",
932938
"ai:Your libvirt virtual machines should be configured to restart automatically after a reboot. You can check this by running:": "Your libvirt virtual machines should be configured to restart automatically after a reboot. You can check this by running:",
933-
"ai:Your own NTP (Network Time Protocol) sources": "Your own NTP (Network Time Protocol) sources"
939+
"ai:Your own NTP (Network Time Protocol) sources": "Your own NTP (Network Time Protocol) sources",
940+
"hostsNetworkConfigurationType": "hostsNetworkConfigurationType",
941+
"machineNetworks": "machineNetworks"
934942
}

libs/ui-lib/lib/common/components/ui/WizardFooter.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type WizardFooterGenericProps = {
2020
onNext?: () => void;
2121
onBack?: () => void;
2222
onCancel?: () => void;
23+
onReset?: () => void;
2324
isNextDisabled?: boolean;
2425
isBackDisabled?: boolean;
2526
isSubmitting?: boolean;
@@ -41,6 +42,7 @@ export const WizardFooter: React.FC<WizardFooterProps> = ({
4142
onNext,
4243
onBack,
4344
onCancel,
45+
onReset,
4446
isNextDisabled,
4547
isBackDisabled,
4648
leftExtraActions,
@@ -95,6 +97,18 @@ export const WizardFooter: React.FC<WizardFooterProps> = ({
9597
</Button>
9698
</ActionListItem>
9799
)}
100+
{onReset && cluster && (
101+
<ActionListItem>
102+
<Button
103+
variant={ButtonVariant.link}
104+
name="reset"
105+
onClick={onReset}
106+
isDisabled={isSubmitting}
107+
>
108+
{t('ai:Reset')}
109+
</Button>
110+
</ActionListItem>
111+
)}
98112
{isSubmitting && (
99113
<ActionListItem>
100114
<Text component={TextVariants.small}>

libs/ui-lib/lib/common/hooks/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { default as useStateSafely } from './useStateSafely';
22
export { default as useDeepCompareMemoize } from './useDeepCompareMemoize';
3+
export { useTranslation } from './use-translation-wrapper';

libs/ui-lib/lib/common/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ export * from './config';
99
export * from './features';
1010
export * from './reducers';
1111
export * from './selectors';
12+
export * from './hooks';
1213
export { ResourceUIState } from './types/resource-ui-state';

libs/ui-lib/lib/ocm/components/clusterWizard/ClusterWizardFooter.tsx

+12
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { useTranslation } from '../../../common/hooks/use-translation-wrapper';
1616
import { onFetchEvents } from '../fetching/fetchEvents';
1717
import { Cluster } from '@openshift-assisted/types/assisted-installer-service';
1818
import { useFeature } from '../../hooks/use-feature';
19+
import { useModalDialogsContext } from '../hosts/ModalDialogsContext';
1920

2021
type ClusterValidationSectionProps = {
2122
cluster?: Cluster;
@@ -84,9 +85,15 @@ const ClusterWizardFooter = ({
8485
const { alerts } = useAlerts();
8586
const navigate = useNavigate();
8687
const isSingleClusterFeatureEnabled = useFeature('ASSISTED_INSTALLER_SINGLE_CLUSTER_FEATURE');
88+
const { currentStepId } = useClusterWizardContext();
89+
const { resetSingleClusterDialog } = useModalDialogsContext();
8790

8891
const handleCancel = React.useCallback(() => navigate('/cluster-list'), [navigate]);
8992

93+
const handleReset = React.useCallback(() => {
94+
resetSingleClusterDialog.open({ cluster });
95+
}, [resetSingleClusterDialog, cluster]);
96+
9097
const alertsSection = alerts.length ? <Alerts /> : undefined;
9198

9299
const errorsSection = (
@@ -103,6 +110,11 @@ const ClusterWizardFooter = ({
103110
alerts={alertsSection}
104111
errors={errorsSection}
105112
onCancel={isSingleClusterFeatureEnabled ? undefined : onCancel || handleCancel}
113+
onReset={
114+
isSingleClusterFeatureEnabled && currentStepId !== 'cluster-details'
115+
? handleReset
116+
: undefined
117+
}
106118
leftExtraActions={additionalActions}
107119
cluster={cluster}
108120
onFetchEvents={onFetchEvents}

libs/ui-lib/lib/ocm/components/clusters/ClusterPage.tsx

+11-2
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@ import { OpenShiftVersionsContextProvider } from '../clusterWizard/OpenShiftVers
3838
const ClusterPageGeneric = ({
3939
clusterId,
4040
showBreadcrumbs = false,
41+
resetModal,
4142
}: {
4243
clusterId: string;
4344
showBreadcrumbs?: boolean;
45+
resetModal?: React.ReactNode;
4446
}) => {
4547
const fetchCluster = useFetchCluster(clusterId);
4648
const dispatch = useDispatch();
@@ -183,6 +185,7 @@ const ClusterPageGeneric = ({
183185
{uiState === ResourceUIState.UPDATE_ERROR && <ClusterUpdateErrorModal />}
184186
<CancelInstallationModal />
185187
<ResetClusterModal />
188+
{resetModal}
186189
<DiscoveryImageModal />
187190
</NewFeatureSupportLevelProvider>
188191
</OpenShiftVersionsContextProvider>
@@ -197,9 +200,15 @@ const ClusterPageGeneric = ({
197200
return <Navigate to="/clusters" />;
198201
};
199202

200-
export const SingleClusterPage = ({ clusterId }: { clusterId: string }) => (
203+
export const SingleClusterPage = ({
204+
clusterId,
205+
resetModal,
206+
}: {
207+
clusterId: string;
208+
resetModal: React.ReactNode;
209+
}) => (
201210
<AlertsContextProvider>
202-
<ClusterPageGeneric clusterId={clusterId} />
211+
<ClusterPageGeneric clusterId={clusterId} resetModal={resetModal} />
203212
</AlertsContextProvider>
204213
);
205214

libs/ui-lib/lib/ocm/components/hosts/ModalDialogsContext.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ type MassDeleteHostDialogProps = {
4040
reloadCluster: VoidFunction;
4141
};
4242

43+
type ResetSingleClusterDialogProps = {
44+
cluster?: Cluster;
45+
};
46+
4347
type ModalDialogsDataTypes = {
4448
eventsDialog: HostIdAndHostname;
4549
editHostDialog: EditHostProps;
@@ -53,6 +57,7 @@ type ModalDialogsDataTypes = {
5357
UpdateDay2ApiVipDialog: void;
5458
massUpdateHostnameDialog: MassUpdateHostnameDialogProps;
5559
massDeleteHostDialog: MassDeleteHostDialogProps;
60+
resetSingleClusterDialog: ResetSingleClusterDialogProps;
5661
};
5762

5863
type DialogId =
@@ -67,7 +72,8 @@ type DialogId =
6772
| 'day2DiscoveryImageDialog'
6873
| 'UpdateDay2ApiVipDialog'
6974
| 'massUpdateHostnameDialog'
70-
| 'massDeleteHostDialog';
75+
| 'massDeleteHostDialog'
76+
| 'resetSingleClusterDialog';
7177

7278
export type ModalDialogsContextType = {
7379
[key in DialogId]: {
@@ -91,6 +97,7 @@ const dialogIds: DialogId[] = [
9197
'UpdateDay2ApiVipDialog',
9298
'massUpdateHostnameDialog',
9399
'massDeleteHostDialog',
100+
'resetSingleClusterDialog',
94101
];
95102

96103
const ModalDialogsContext = React.createContext<ModalDialogsContextType | undefined>(undefined);

0 commit comments

Comments
 (0)