Skip to content

Commit

Permalink
Merge pull request #892 from GowthamShanmugam/app_manage_policy
Browse files Browse the repository at this point in the history
Manage data policy list view
  • Loading branch information
openshift-merge-robot authored Jun 21, 2023
2 parents 4335581 + 04fafa5 commit 7561878
Show file tree
Hide file tree
Showing 24 changed files with 1,630 additions and 37 deletions.
16 changes: 15 additions & 1 deletion locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,21 @@
"{{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",
"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.",
"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",
"Search": "Search",
"Search input": "Search input",
"Secondary actions": "Secondary actions",
"Actions": "Actions",
"My policies": "My policies",
"Assign policy": "Assign policy",
"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 Down Expand Up @@ -817,7 +832,6 @@
"BackingStore details": "BackingStore details",
"Tiers": "Tiers",
"Placement": "Placement",
"Policy type": "Policy type",
"Namespace Policy": "Namespace Policy",
"BucketClass details": "BucketClass details",
"Edit Bucket Class Resources": "Edit Bucket Class Resources",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ import {
} from '../../../hooks';
import { DRPolicyModel } from '../../../models';
import { DRPolicyKind } from '../../../types';
import { getReplicationType, findAppsUsingDRPolicy } from '../../../utils';
import {
getReplicationType,
findAppsUsingDRPolicy,
isDRPolicyValidated,
} from '../../../utils';
import EmptyPage from '../../empty-state-page/empty-page';
import { ConnectedApplicationsModal } from '../../modals/connected-apps-modal/connected-apps-modal';
import {
Expand Down Expand Up @@ -60,9 +64,6 @@ const DRPolicyRow: React.FC<RowProps<DRPolicyKind, RowData>> = ({
const clusterNames = obj?.spec?.drClusters?.map((clusterName) => (
<p key={clusterName}> {clusterName} </p>
));
const condition = obj?.status?.conditions?.find(
(statusCondition) => statusCondition.type === 'Validated'
);
const filteredApps = findAppsUsingDRPolicy(applicationRefs, obj);
const appCount = filteredApps?.length;

Expand All @@ -77,7 +78,7 @@ const DRPolicyRow: React.FC<RowProps<DRPolicyKind, RowData>> = ({
{obj?.metadata?.name}
</TableData>
<TableData {...tableColumnInfo[1]} activeColumnIDs={activeColumnIDs}>
{condition?.status === 'True' ? t('Validated') : t('Not Validated')}
{isDRPolicyValidated(obj) ? t('Validated') : t('Not Validated')}
</TableData>
<TableData {...tableColumnInfo[2]} activeColumnIDs={activeColumnIDs}>
{clusterNames}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
import {
findDRType,
getProtectedPVCsFromDRPC,
getRemoteNSFromAppSet,
getRemoteNamespaceFromAppSet,
} from '../../../utils';
import { StorageDashboard, STATUS_QUERIES } from '../queries';
import { AlertsCard } from './alert-card/alert-card';
Expand Down Expand Up @@ -129,7 +129,8 @@ export const DRDashboard: React.FC = () => {
protectedPVCs: getProtectedPVCsFromDRPC(drPlacementControl),
replicationType: findDRType(currentDrClusters),
syncInterval: drPolicy?.spec?.schedulingInterval,
workloadNamespace: getRemoteNSFromAppSet(application),
workloadNamespace:
getRemoteNamespaceFromAppSet(application),
failoverCluster: drPlacementControl?.spec?.failoverCluster,
preferredCluster:
drPlacementControl?.spec?.preferredCluster,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as React from 'react';
import { ArgoApplicationSetModel } from '@odf/mco/models';
import { ArgoApplicationSetKind } from '@odf/mco/types';
import { getGVKofResource, referenceForModel } from '@odf/shared/utils';
import { ApplicationSetParser } from './parsers/application-set-parser';

export const AppManageDataPolicy: React.FC<ACMActionCallbackProps> = ({
resource,
close,
isOpen,
}) => {
const gvk = getGVKofResource(resource);
return (
<>
{gvk === referenceForModel(ArgoApplicationSetModel) && (
<ApplicationSetParser
application={resource}
isOpen={isOpen}
close={close}
/>
)}
</>
);
};

export type ACMActionCallbackProps = {
isOpen: boolean;
resource: ArgoApplicationSetKind;
close: () => void;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import * as React from 'react';
import { StatusBox } from '@odf/shared/generic/status-box';
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
import { Modal, ModalVariant } from '@patternfly/react-core';
import { PolicyListView } from './policy-list-view';
import {
ManagePolicyStateType,
ModalActionContext,
ModalViewContext,
initialPolicyState,
managePolicyStateReducer,
} from './utils/reducer';
import { ApplicationType } from './utils/types';

export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
applicaitonInfo,
loaded,
loadError,
isOpen,
close,
}) => {
const [state, dispatch] = React.useReducer(
managePolicyStateReducer,
initialPolicyState
);
const { t } = useCustomTranslation();

const setModalContext = (modalViewContext: ModalViewContext) => {
dispatch({
type: ManagePolicyStateType.SET_MODAL_VIEW_CONTEXT,
payload: modalViewContext,
});
};

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

const setError = (error: string) =>
dispatch({
type: ManagePolicyStateType.SET_ERROR,
context: state.modalViewContext,
payload: error,
});

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.'
)}
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 && (
<PolicyListView
dataPolicyInfo={applicaitonInfo?.dataPolicies}
state={state.policyListView}
dispatch={dispatch}
setModalContext={setModalContext}
setModalActionContext={setModalActionContext}
setError={setError}
/>
)
) : (
<StatusBox loadError={loadError} loaded={loaded} />
)}
</Modal>
);
};

export type AppManagePoliciesModalProps = {
applicaitonInfo: ApplicationType;
loaded: boolean;
loadError: any;
isOpen: boolean;
close?: () => void;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import * as React from 'react';
import { utcDateTimeFormatter } from '@odf/shared/details-page/datetime';
import { getName } from '@odf/shared/selectors';
import {
GreenCheckCircleIcon,
RedExclamationCircleIcon,
} from '@odf/shared/status/icons';
import {
RowComponentType,
SelectableTable,
TableColumnProps,
} from '@odf/shared/table/selectable-table';
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
import { StatusIconAndText } from '@openshift-console/dynamic-plugin-sdk';
import { TFunction } from 'i18next';
import { Text } from '@patternfly/react-core';
import {
ActionsColumn,
IAction,
SortByDirection,
} from '@patternfly/react-table';
import { Td } from '@patternfly/react-table';
import { ModalActionContext, ModalViewContext } from '../utils/reducer';
import { DataPolicyType } from '../utils/types';
import '../style.scss';

const sortRows = (
a: DataPolicyType,
b: DataPolicyType,
c: SortByDirection,
sortField: string
) => {
const negation = c !== SortByDirection.asc;
const aValue = a?.[sortField] || '';
const bValue = b?.[sortField] || '';
const sortVal = (aValue as string).localeCompare(bValue as string);
return negation ? -sortVal : sortVal;
};

const getColumnNames = (t: TFunction) => [
t('Policy name'),
t('Policy type'),
t('Status'),
t('Activity'),
t('Assigned on'),
];

const PolicyListViewTableRow: React.FC<RowComponentType<DataPolicyType>> = ({
row: policy,
extraProps,
}) => {
const { t } = useCustomTranslation();
const columnNames = getColumnNames(t);
const { kind, isValidated, activity, assignedOn } = policy;
const { setPolicy, setModalContext }: RowExtraProps = extraProps;
const status = isValidated ? t('Validated') : t('Not Validated');
const assignedDateStr = utcDateTimeFormatter.format(new Date(assignedOn));

const RowActions = (t: TFunction): IAction[] => [
{
title: t('View configurations'),
onClick: () => {
setPolicy(policy, ModalViewContext.POLICY_CONFIGURATON_VIEW);
setModalContext(ModalViewContext.POLICY_CONFIGURATON_VIEW);
},
},
{
title: t('Unassign policy'),
onClick: () => {
setPolicy(policy, ModalViewContext.UNASSIGN_POLICY_VIEW);
setModalContext(ModalViewContext.UNASSIGN_POLICY_VIEW);
},
},
];

return (
<>
<Td translate={null} dataLabel={columnNames[0]}>
{getName(policy)}
</Td>
<Td translate={null} dataLabel={columnNames[1]}>
{kind}
</Td>
<Td translate={null} dataLabel={columnNames[2]}>
<StatusIconAndText
title={status}
icon={
isValidated ? (
<GreenCheckCircleIcon />
) : (
<RedExclamationCircleIcon />
)
}
/>
</Td>
<Td translate={null} dataLabel={columnNames[3]}>
<Text className={!activity ? 'text-muted' : ''}>
{!!activity ? activity : t('No activity')}
</Text>
</Td>
<Td translate={null} dataLabel={columnNames[4]}>
{assignedDateStr}
</Td>
<Td translate={null} isActionCell>
<ActionsColumn
items={RowActions(t)}
isDisabled={!!policy?.metadata?.deletionTimestamp}
/>
</Td>
</>
);
};

export const PolicyListViewTable: React.FC<PolicyListViewTableProps> = ({
policies,
selectedPolicies,
modalActionContext,
setPolicy,
setPolicies,
setModalContext,
setModalActionContext,
}) => {
const { t } = useCustomTranslation();
const setSelectedRowList = (selectedRowList: DataPolicyType[]) => {
setPolicies(selectedRowList);
modalActionContext === ModalActionContext.UN_ASSIGNING_POLICIES &&
setModalActionContext(selectedRowList.length === 0 && null);
};

const columns: TableColumnProps[] = React.useMemo(() => {
const columnNames = getColumnNames(t);
return [
{
columnName: columnNames[0],
sortFunction: (a, b, c) => sortRows(a, b, c, 'metadata.name'),
},
{
columnName: columnNames[1],
sortFunction: (a, b, c) => sortRows(a, b, c, 'kind'),
},
{
columnName: columnNames[2],
sortFunction: (a, b, c) => sortRows(a, b, c, 'isValidated'),
},
{
columnName: columnNames[3],
sortFunction: (a, b, c) => sortRows(a, b, c, 'activity'),
},
{
columnName: columnNames[4],
sortFunction: (a, b, c) => sortRows(a, b, c, 'assignedOn'),
},
{ columnName: '' },
];
}, [t]);

return (
<div className="mco-manage-policies__listViewTable--padding">
<SelectableTable<DataPolicyType>
columns={columns}
rows={policies}
RowComponent={PolicyListViewTableRow}
selectedRows={selectedPolicies}
setSelectedRows={setSelectedRowList}
extraProps={{
setPolicy,
setModalContext,
}}
/>
</div>
);
};

type RowExtraProps = {
setModalContext: (modalViewContext: ModalViewContext) => void;
setPolicy: (
policies: DataPolicyType,
modalViewContext: ModalViewContext
) => void;
};

type PolicyListViewTableProps = RowExtraProps & {
policies: DataPolicyType[];
selectedPolicies: DataPolicyType[];
modalActionContext: ModalActionContext;
setPolicies: (policies: DataPolicyType[]) => void;
setModalActionContext: (modalActionContext: ModalActionContext) => void;
};
Loading

0 comments on commit 7561878

Please sign in to comment.