Skip to content

AGENT-1143: add reset button to disconnected assisted installer ui #2866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/assisted-disconnected-ui/src/components/ClusterPage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { SingleClusterPage } from '@openshift-assisted/ui-lib/ocm';
import { useParams } from 'react-router-dom-v5-compat';
import ResetSingleClusterModal from './ResetSingleClusterModal';

const ClusterPage = () => {
const { clusterId } = useParams() as { clusterId: string };
return <SingleClusterPage clusterId={clusterId} />;
return <SingleClusterPage clusterId={clusterId} resetModal={<ResetSingleClusterModal />} />;
};

export default ClusterPage;
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import {
OpenShiftVersionsContextProvider,
NewFeatureSupportLevelProvider,
NewClusterWizard,
ModalDialogsContextProvider,
} from '@openshift-assisted/ui-lib/ocm';
import { Alert, PageSection, PageSectionVariants } from '@patternfly/react-core';
import { useNavigate } from 'react-router-dom-v5-compat';
import ResetSingleClusterModal from './ResetSingleClusterModal';

const CreateClusterWizard = () => {
const [clusterId, isLoading, error] = useCluster();
Expand All @@ -32,11 +34,14 @@ const CreateClusterWizard = () => {
return (
<AlertsContextProvider>
<ClusterWizardContextProvider>
<OpenShiftVersionsContextProvider>
<NewFeatureSupportLevelProvider loadingUi={<ClusterLoading />}>
<NewClusterWizard />
</NewFeatureSupportLevelProvider>
</OpenShiftVersionsContextProvider>
<ModalDialogsContextProvider>
<OpenShiftVersionsContextProvider>
<NewFeatureSupportLevelProvider loadingUi={<ClusterLoading />}>
<NewClusterWizard />
<ResetSingleClusterModal />
</NewFeatureSupportLevelProvider>
</OpenShiftVersionsContextProvider>
</ModalDialogsContextProvider>
</ClusterWizardContextProvider>
</AlertsContextProvider>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React from 'react';
import {
Button,
Modal,
ModalVariant,
ButtonVariant,
Text,
TextContent,
Stack,
StackItem,
Alert,
} from '@patternfly/react-core';
import { useModalDialogsContext, ClustersService } from '@openshift-assisted/ui-lib/ocm';
import {
getApiErrorMessage,
handleApiError,
useTranslation,
} from '@openshift-assisted/ui-lib/common';
import { useNavigate } from 'react-router-dom-v5-compat';

const ResetSingleClusterModal: React.FC = () => {
const navigate = useNavigate();
const [isLoading, setIsLoading] = React.useState(false);
const [error, setError] = React.useState<{ title: string; message: string }>();
const { resetSingleClusterDialog } = useModalDialogsContext();
const { data, isOpen, close: onClose } = resetSingleClusterDialog;
const cluster = data?.cluster;
const { t } = useTranslation();

if (!cluster) {
return null;
}

const handleClose = () => {
setError(undefined);
onClose();
};

const handleResetAsync = async () => {
try {
setError(undefined);
setIsLoading(true);
await ClustersService.remove(cluster.id);
navigate(`/`);
} catch (e) {
handleApiError(e, () => {
setError({
title: t('ai:Failed to reset cluster installation'),
message: getApiErrorMessage(e),
});
});
} finally {
setIsLoading(false);
}
};

const actions = [
<Button
key="reset"
variant={ButtonVariant.danger}
onClick={() => void handleResetAsync()}
isDisabled={isLoading}
isLoading={isLoading}
>
{t('ai:Reset')}
</Button>,
<Button key="cancel" variant={ButtonVariant.link} onClick={handleClose} isDisabled={isLoading}>
{t('ai:Cancel')}
</Button>,
];

return (
<Modal
title={t('ai:Reset cluster')}
titleIconVariant="warning"
isOpen={isOpen}
variant={ModalVariant.small}
actions={actions}
onClose={handleClose}
>
<Stack hasGutter>
<StackItem>
<TextContent>
<Text component="p">
{t('ai:This will remove all current configurations and will revert to the defaults.')}
</Text>

<Text component="p">{t('ai:Are you sure you want to reset the cluster?')}</Text>
</TextContent>
</StackItem>
{error && (
<StackItem>
<Alert isInline variant="danger" title={error.title}>
{error.message}
</Alert>
</StackItem>
)}
</Stack>
</Modal>
);
};

export default ResetSingleClusterModal;
5 changes: 4 additions & 1 deletion libs/locales/.i18next-parser.config.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const defaultNS = process.env.TRANSLATION_NAMESPACE || 'translation';

module.exports = {
input: '../ui-lib/lib/**/*.{js,jsx,ts,tsx}',
input: [
'../ui-lib/lib/**/*.{js,jsx,ts,tsx}',
'../../apps/assisted-disconnected-ui/src/**/*.{js,jsx,ts,tsx}',
],
contextSeparator: '_',
createOldCatalogs: false,
keySeparator: false,
Expand Down
5 changes: 5 additions & 0 deletions libs/locales/lib/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
"ai:Approve host to join infrastructure environment": "Approve host to join infrastructure environment",
"ai:Approve hosts dialog": "Approve hosts dialog",
"ai:Approve hosts to join infrastructure environment": "Approve hosts to join infrastructure environment",
"ai:Are you sure you want to reset the cluster?": "Are you sure you want to reset the cluster?",
"ai:arm64": "arm64",
"ai:arm64 is not supported in this OpenShift version": "arm64 is not supported in this OpenShift version",
"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.",
Expand Down Expand Up @@ -327,6 +328,7 @@
"ai:Failed to fetch cluster credentials.": "Failed to fetch cluster credentials.",
"ai:Failed to get Provisioning Configuration": "Failed to get Provisioning Configuration",
"ai:Failed to patch assisted-image-service route for new domain.": "Failed to patch assisted-image-service route for new domain.",
"ai:Failed to reset cluster installation": "Failed to reset cluster installation",
"ai:Failed to save ClusterDeployment": "Failed to save ClusterDeployment",
"ai:Failed to save configuration": "Failed to save configuration",
"ai:Failed to save host selection.": "Failed to save host selection.",
Expand Down Expand Up @@ -706,6 +708,8 @@
"ai:Report a bug": "Report a bug",
"ai:Required field": "Required field",
"ai:Required.": "Required.",
"ai:Reset": "Reset",
"ai:Reset cluster": "Reset cluster",
"ai:Reset host": "Reset host",
"ai:Resetting": "Resetting",
"ai:Returning to the infrastructure environment": "Returning to the infrastructure environment",
Expand Down Expand Up @@ -846,6 +850,7 @@
"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.",
"ai:This subnet range is not available on all hosts": "This subnet range is not available on all hosts",
"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.",
"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.",
"ai:Time": "Time",
"ai:Time synced between host and service": "Time synced between host and service",
"ai:to add a number.": "to add a number.",
Expand Down
14 changes: 14 additions & 0 deletions libs/ui-lib/lib/common/components/ui/WizardFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type WizardFooterGenericProps = {
onNext?: () => void;
onBack?: () => void;
onCancel?: () => void;
onReset?: () => void;
isNextDisabled?: boolean;
isBackDisabled?: boolean;
isSubmitting?: boolean;
Expand All @@ -41,6 +42,7 @@ export const WizardFooter: React.FC<WizardFooterProps> = ({
onNext,
onBack,
onCancel,
onReset,
isNextDisabled,
isBackDisabled,
leftExtraActions,
Expand Down Expand Up @@ -95,6 +97,18 @@ export const WizardFooter: React.FC<WizardFooterProps> = ({
</Button>
</ActionListItem>
)}
{onReset && cluster && (
<ActionListItem>
<Button
variant={ButtonVariant.link}
name="reset"
onClick={onReset}
isDisabled={isSubmitting}
>
{t('ai:Reset')}
</Button>
</ActionListItem>
)}
{isSubmitting && (
<ActionListItem>
<Text component={TextVariants.small}>
Expand Down
1 change: 1 addition & 0 deletions libs/ui-lib/lib/common/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as useStateSafely } from './useStateSafely';
export { default as useDeepCompareMemoize } from './useDeepCompareMemoize';
export { useTranslation } from './use-translation-wrapper';
1 change: 1 addition & 0 deletions libs/ui-lib/lib/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export * from './config';
export * from './features';
export * from './reducers';
export * from './selectors';
export * from './hooks';
export { ResourceUIState } from './types/resource-ui-state';
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useTranslation } from '../../../common/hooks/use-translation-wrapper';
import { onFetchEvents } from '../fetching/fetchEvents';
import { Cluster } from '@openshift-assisted/types/assisted-installer-service';
import { useFeature } from '../../hooks/use-feature';
import { useModalDialogsContext } from '../hosts/ModalDialogsContext';

type ClusterValidationSectionProps = {
cluster?: Cluster;
Expand Down Expand Up @@ -84,9 +85,15 @@ const ClusterWizardFooter = ({
const { alerts } = useAlerts();
const navigate = useNavigate();
const isSingleClusterFeatureEnabled = useFeature('ASSISTED_INSTALLER_SINGLE_CLUSTER_FEATURE');
const { currentStepId } = useClusterWizardContext();
const { resetSingleClusterDialog } = useModalDialogsContext();

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

const handleReset = React.useCallback(() => {
resetSingleClusterDialog.open({ cluster });
}, [resetSingleClusterDialog, cluster]);

const alertsSection = alerts.length ? <Alerts /> : undefined;

const errorsSection = (
Expand All @@ -103,6 +110,11 @@ const ClusterWizardFooter = ({
alerts={alertsSection}
errors={errorsSection}
onCancel={isSingleClusterFeatureEnabled ? undefined : onCancel || handleCancel}
onReset={
isSingleClusterFeatureEnabled && currentStepId !== 'cluster-details'
? handleReset
: undefined
}
leftExtraActions={additionalActions}
cluster={cluster}
onFetchEvents={onFetchEvents}
Expand Down
13 changes: 11 additions & 2 deletions libs/ui-lib/lib/ocm/components/clusters/ClusterPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ import { OpenShiftVersionsContextProvider } from '../clusterWizard/OpenShiftVers
const ClusterPageGeneric = ({
clusterId,
showBreadcrumbs = false,
resetModal,
}: {
clusterId: string;
showBreadcrumbs?: boolean;
resetModal?: React.ReactNode;
}) => {
const fetchCluster = useFetchCluster(clusterId);
const dispatch = useDispatch();
Expand Down Expand Up @@ -183,6 +185,7 @@ const ClusterPageGeneric = ({
{uiState === ResourceUIState.UPDATE_ERROR && <ClusterUpdateErrorModal />}
<CancelInstallationModal />
<ResetClusterModal />
{resetModal}
<DiscoveryImageModal />
</NewFeatureSupportLevelProvider>
</OpenShiftVersionsContextProvider>
Expand All @@ -197,9 +200,15 @@ const ClusterPageGeneric = ({
return <Navigate to="/clusters" />;
};

export const SingleClusterPage = ({ clusterId }: { clusterId: string }) => (
export const SingleClusterPage = ({
clusterId,
resetModal,
}: {
clusterId: string;
resetModal: React.ReactNode;
}) => (
<AlertsContextProvider>
<ClusterPageGeneric clusterId={clusterId} />
<ClusterPageGeneric clusterId={clusterId} resetModal={resetModal} />
</AlertsContextProvider>
);

Expand Down
9 changes: 8 additions & 1 deletion libs/ui-lib/lib/ocm/components/hosts/ModalDialogsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ type MassDeleteHostDialogProps = {
reloadCluster: VoidFunction;
};

type ResetSingleClusterDialogProps = {
cluster?: Cluster;
};

type ModalDialogsDataTypes = {
eventsDialog: HostIdAndHostname;
editHostDialog: EditHostProps;
Expand All @@ -53,6 +57,7 @@ type ModalDialogsDataTypes = {
UpdateDay2ApiVipDialog: void;
massUpdateHostnameDialog: MassUpdateHostnameDialogProps;
massDeleteHostDialog: MassDeleteHostDialogProps;
resetSingleClusterDialog: ResetSingleClusterDialogProps;
};

type DialogId =
Expand All @@ -67,7 +72,8 @@ type DialogId =
| 'day2DiscoveryImageDialog'
| 'UpdateDay2ApiVipDialog'
| 'massUpdateHostnameDialog'
| 'massDeleteHostDialog';
| 'massDeleteHostDialog'
| 'resetSingleClusterDialog';

export type ModalDialogsContextType = {
[key in DialogId]: {
Expand All @@ -91,6 +97,7 @@ const dialogIds: DialogId[] = [
'UpdateDay2ApiVipDialog',
'massUpdateHostnameDialog',
'massDeleteHostDialog',
'resetSingleClusterDialog',
];

const ModalDialogsContext = React.createContext<ModalDialogsContextType | undefined>(undefined);
Expand Down
Loading