Skip to content

Commit

Permalink
Assign policy wizard
Browse files Browse the repository at this point in the history
Signed-off-by: Gowtham Shanmugasundaram <[email protected]>
  • Loading branch information
GowthamShanmugam committed Jun 28, 2023
1 parent 4241c81 commit 0c69e3c
Show file tree
Hide file tree
Showing 24 changed files with 1,308 additions and 189 deletions.
40 changes: 34 additions & 6 deletions locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -190,17 +190,47 @@
"{{selected}} of {{total}} selected": "{{selected}} of {{total}} selected",
"subscription-selector": "subscription-selector",
"Select the subscriptions groups you wish to replicate via": "Select the subscriptions groups you wish to replicate via",
"Assign Policy": "Assign Policy",
"Secure your application by assigning a policy from the available policy templates.": "Secure your application by assigning a policy from the available policy templates.",
"Manage Policy": "Manage Policy",
"Assign policy to protect the application and ensure quick recovery. Unassign policy from an application when they no longer require to be managed.": "Assign policy to protect the application and ensure quick recovery. Unassign policy from an application when they no longer require to be managed.",
"PVC details": "PVC details",
"Review and assign": "Review and assign",
"Assign": "Assign",
"New policy assigned to application.": "New policy assigned to application.",
"Unable to assign policy to application.": "Unable to assign policy to application.",
"Next": "Next",
"Back": "Back",
"Assign policy nav": "Assign policy nav",
"Assign policy content": "Assign policy content",
"Manage list view alert": "Manage list view alert",
"Confirm unassign": "Confirm unassign",
"Policy configuration details": "Policy configuration details",
"Replication type": "Replication type",
"Sync interval": "Sync interval",
"Cluster": "Cluster",
"Replication status": "Replication status",
"Last sync {{time}}": "Last sync {{time}}",
"Application resources protected": "Application resources protected",
"placement": "placement",
"placements": "placements",
"PVC label selector": "PVC label selector",
"Policy type": "Policy type",
"Assigned on": "Assigned on",
"View configurations": "View configurations",
"Unassign policy": "Unassign policy",
"No activity": "No activity",
"Relocate in progress": "Relocate in progress",
"Failover in progress": "Failover in progress",
"Delete": "Delete",
"Select a placement": "Select a placement",
"{{count}} selected_one": "{{count}} selected",
"{{count}} selected_other": "{{count}} selected",
"Select labels": "Select labels",
"Use PVC label selectors to effortlessly specify the application resources that need protection.": "Use PVC label selectors to effortlessly specify the application resources that need protection.",
"If no label is provided, all PVCs will be protected. Define your preferences to protect specific resources.": "If no label is provided, all PVCs will be protected. Define your preferences to protect specific resources.",
"Application resource": "Application resource",
"Add application resource": "Add application resource",
"Policy": "Policy",
"Select a policy": "Select a policy",
"Search": "Search",
"Search input": "Search input",
"Secondary actions": "Secondary actions",
Expand All @@ -212,6 +242,8 @@
"Unable to unassign all selected policies for the application.": "Unable to unassign all selected policies for the application.",
"My policies": "My policies",
"Assign policy": "Assign policy",
"Relocate in progress": "Relocate in progress",
"Failover in progress": "Failover in progress",
"List all the connected applications under a policy.": "List all the connected applications under a policy.",
"Application name": "Application name",
"application name search": "application name search",
Expand All @@ -237,7 +269,6 @@
"A selector label to DR protect only specific PVCs within an application.": "A selector label to DR protect only specific PVCs within an application.",
"When multiple applications are selected, DR protection will be applied for all the PVCs under the application's namespace.": "When multiple applications are selected, DR protection will be applied for all the PVCs under the application's namespace.",
"Protect all PVCs within the application's namespace": "Protect all PVCs within the application's namespace",
"Assign": "Assign",
"Asynchronous": "Asynchronous",
"Synchronous": "Synchronous",
"{{async}}, interval: {{interval}}": "{{async}}, interval: {{interval}}",
Expand Down Expand Up @@ -426,7 +457,6 @@
"Try Again": "Try Again",
"Finish": "Finish",
"Go To PVC List": "Go To PVC List",
"Delete": "Delete",
"BlockPool Update Form": "BlockPool Update Form",
"Filesystem name": "Filesystem name",
"Enter filesystem name": "Enter filesystem name",
Expand Down Expand Up @@ -482,8 +512,6 @@
"Create BucketClass": "Create BucketClass",
"Create new BucketClass": "Create new BucketClass",
"BucketClass is a CRD representing a class for buckets that defines tiering policies and data placements for an OBC.": "BucketClass is a CRD representing a class for buckets that defines tiering policies and data placements for an OBC.",
"Next": "Next",
"Back": "Back",
"Edit BucketClass Resource": "Edit BucketClass Resource",
"{{storeType}} represents a storage target to be used as the underlying storage for the data in Multicloud Object Gateway buckets.": "{{storeType}} represents a storage target to be used as the underlying storage for the data in Multicloud Object Gateway buckets.",
"What is a BackingStore?": "What is a BackingStore?",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as React from 'react';
import { StatusBox } from '@odf/shared/generic/status-box';
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
import { TFunction } from 'i18next';
import { Modal, ModalVariant } from '@patternfly/react-core';
import { AssignPolicyView } from './assign-policy-view';
import { PolicyListView } from './policy-list-view';
import {
ManagePolicyStateType,
Expand All @@ -11,10 +13,29 @@ import {
initialPolicyState,
managePolicyStateReducer,
} from './utils/reducer';
import { ApplicationType } from './utils/types';
import { ApplicationType, DataPolicyType } from './utils/types';

const getModalTitle = (modalViewContext: ModalViewContext, t: TFunction) => {
if (modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW) {
return {
title: t('Assign Policy'),
description: t(
'Secure your application by assigning a policy from the available policy templates.'
),
};
} else {
return {
title: t('Manage Policy'),
description: t(
'Assign policy to protect the application and ensure quick recovery. Unassign policy from an application when they no longer require to be managed.'
),
};
}
};

export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
applicaitonInfo,
dataPolicies,
loaded,
loadError,
isOpen,
Expand All @@ -33,35 +54,41 @@ export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
});
};

const setModalActionContext = (modalViewContext: ModalActionContext) =>
const setModalActionContext = (
modalActionContext: ModalActionContext,
modalViewContext?: ModalViewContext
) =>
dispatch({
type: ManagePolicyStateType.SET_MODAL_ACTION_CONTEXT,
context: state.modalViewContext,
payload: modalViewContext,
context: modalViewContext || state.modalViewContext,
payload: modalActionContext,
});

const setMessage = (message: MessageType) => {
const setMessage = (
message: MessageType,
modalViewContext?: ModalViewContext
) => {
dispatch({
type: ManagePolicyStateType.SET_MESSAGE,
context: state.modalViewContext,
context: modalViewContext || state.modalViewContext,
payload: message,
});
};

const modalTitle = getModalTitle(state.modalViewContext, t);

return (
<Modal
title={t('Manage Policy')}
description={t(
'Assign policy to protect the application and ensure quick recovery. Unassign policy from an application when they no longer require to be managed.'
)}
title={modalTitle.title}
description={modalTitle.description}
variant={ModalVariant.large}
isOpen={isOpen}
aria-label="Manage policy modal"
aria-describedby="manage-policy-modal"
onClose={close}
>
{loaded && !loadError ? (
state.modalViewContext === ModalViewContext.POLICY_LIST_VIEW && (
(state.modalViewContext === ModalViewContext.POLICY_LIST_VIEW && (
<PolicyListView
dataPolicyInfo={applicaitonInfo?.dataPolicies}
state={state.policyListView}
Expand All @@ -70,7 +97,18 @@ export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
setModalActionContext={setModalActionContext}
setMessage={setMessage}
/>
)
)) ||
(state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW && (
<AssignPolicyView
applicaitonInfo={applicaitonInfo}
dataPolicies={dataPolicies}
state={state.assignPolicyView}
dispatch={dispatch}
setModalContext={setModalContext}
setModalActionContext={setModalActionContext}
setMessage={setMessage}
/>
))
) : (
<StatusBox loadError={loadError} loaded={loaded} />
)}
Expand All @@ -80,6 +118,7 @@ export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({

export type AppManagePoliciesModalProps = {
applicaitonInfo: ApplicationType;
dataPolicies: DataPolicyType[];
loaded: boolean;
loadError: any;
isOpen: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import * as React from 'react';
import { ModalBody } from '@odf/shared/modals/Modal';
import { getName } from '@odf/shared/selectors';
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
import { getErrorMessage } from '@odf/shared/utils';
import { TFunction } from 'i18next';
import { Wizard, WizardStep, AlertVariant } from '@patternfly/react-core';
import { PolicyConfigViewer } from './helper/policy-config-viewer';
import { PVCDetailsWizardContent } from './helper/pvc-details-wizard-content';
import { SelectPolicyWizardContent } from './helper/select-policy-wizard-content';
import { assignPromises } from './utils/k8s-utils';
import {
AssignPolicyViewState,
ManagePolicyStateAction,
ManagePolicyStateType,
MessageType,
ModalActionContext,
ModalViewContext,
} from './utils/reducer';
import {
ApplicationType,
DRPolicyType,
DataPolicyType,
PlacementType,
} from './utils/types';

export const createSteps = (
workloadNamespace: string,
placements: PlacementType[],
dataPolicies: DRPolicyType[],
state: AssignPolicyViewState,
stepIdReached: number,
t: TFunction,
setPolicy: (policy?: DataPolicyType) => void
): WizardStep[] => [
{
id: 1,
name: t('Select policy'),
component: (
<SelectPolicyWizardContent
dataPolicies={dataPolicies}
state={state}
setPolicy={setPolicy}
/>
),
enableNext: !!getName(state.policy),
},
{
id: 2,
name: t('PVC details'),
component: (
<PVCDetailsWizardContent
placements={placements}
state={state}
workloadNamespace={workloadNamespace}
setPolicy={setPolicy}
/>
),
canJumpTo: stepIdReached >= 2,
enableNext: !!state.policy?.placementControInfo?.length,
},
{
id: 3,
name: t('Review and assign'),
component: (
<PolicyConfigViewer
policyName={getName(state.policy)}
clusters={state.policy.drClusters}
isPolicyValidated={state.policy.isValidated}
replicationType={state.policy.replicationType}
syncInterval={state.policy.schedulingInterval}
drPlacementControlInfo={state.policy.placementControInfo}
defaultSelectionText=""
hideSelector
/>
),
nextButtonText: t('Assign'),
canJumpTo: stepIdReached >= 3,
},
];

export const AssignPolicyView: React.FC<AssignPolicyViewProps> = ({
state,
applicaitonInfo,
dataPolicies,
setModalContext,
setModalActionContext,
setMessage,
dispatch,
}) => {
const { t } = useCustomTranslation();
const [stepIdReached, setStepIdReached] = React.useState(1);

const onNext = ({ id }: WizardStep) => {
if (id) {
if (typeof id === 'string') {
id = parseInt(id, 10);
}
setStepIdReached(stepIdReached < id ? id : stepIdReached);
}
};

const setPolicy = (policy: DataPolicyType = {}) =>
dispatch({
type: ManagePolicyStateType.SET_SELECTED_POLICY,
context: ModalViewContext.ASSIGN_POLICY_VIEW,
payload: policy,
});

const assignPolicy = () => {
const updateContext = async (
title: string,
description: string,
variant: AlertVariant,
actionContext: ModalActionContext
) => {
// inject a message into list view
setMessage(
{
title,
description,
variant,
},
ModalViewContext.POLICY_LIST_VIEW
);
setModalActionContext(actionContext, ModalViewContext.POLICY_LIST_VIEW);
// switch to list policy view
setModalContext(ModalViewContext.POLICY_LIST_VIEW);
// reset policy info
setPolicy();
};
// assign DRPolicy
const promises = assignPromises(state.policy);
Promise.all(promises)
.then(() => {
updateContext(
t('New policy assigned to application.'),
'',
AlertVariant.success,
ModalActionContext.ASSIGN_POLICY_SUCCEEDED
);
})
.catch((error) => {
updateContext(
t('Unable to assign policy to application.'),
getErrorMessage(error),
AlertVariant.danger,
ModalActionContext.ASSIGN_POLICY_FAILED
);
});
};

return (
<ModalBody>
<Wizard
cancelButtonText={t('Cancel')}
nextButtonText={t('Next')}
backButtonText={t('Back')}
navAriaLabel={t('Assign policy nav')}
mainAriaLabel={t('Assign policy content')}
onSave={assignPolicy}
onClose={() => {
setModalContext(ModalViewContext.POLICY_LIST_VIEW);
// reset policy info
setPolicy();
}}
steps={createSteps(
applicaitonInfo?.workloadNamespace,
applicaitonInfo?.placements,
dataPolicies,
state,
stepIdReached,
t,
setPolicy
)}
onNext={onNext}
height={450}
/>
</ModalBody>
);
};

type AssignPolicyViewProps = {
state: AssignPolicyViewState;
applicaitonInfo: ApplicationType;
dataPolicies: DataPolicyType[];
dispatch: React.Dispatch<ManagePolicyStateAction>;
setModalContext: (modalViewContext: ModalViewContext) => void;
setModalActionContext: (
modalActionContext: ModalActionContext,
modalViewContext?: ModalViewContext
) => void;
setMessage: (error: MessageType, modalViewContext?: ModalViewContext) => void;
};
Loading

0 comments on commit 0c69e3c

Please sign in to comment.