Skip to content

Commit

Permalink
ISPN-14697 Role detail
Browse files Browse the repository at this point in the history
  • Loading branch information
karesti committed Oct 25, 2023
1 parent e4e0b1a commit bf3cba2
Show file tree
Hide file tree
Showing 13 changed files with 275 additions and 16 deletions.
96 changes: 96 additions & 0 deletions src/app/AccessManagement/RoleDetail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React, { useEffect, useState } from 'react';
import {
Card,
CardBody,
Nav,
NavItem,
NavList,
PageSection,
PageSectionVariants,
Text,
TextContent,
TextVariants
} from '@patternfly/react-core';
import { useTranslation } from 'react-i18next';
import { DataContainerBreadcrumb } from '@app/Common/DataContainerBreadcrumb';
import { global_spacer_md, global_spacer_sm } from '@patternfly/react-tokens';
import { RoleGeneral } from '@app/AccessManagement/RoleDetailContent/RoleGeneral';
import { RolePermissions } from '@app/AccessManagement/RoleDetailContent/RolePermissions';
import { RoleCaches } from '@app/AccessManagement/RoleDetailContent/RoleCaches';

const RoleDetail = (props) => {
const roleName = decodeURIComponent(props.computedMatch.params.roleName);
const { t } = useTranslation();
const brandname = t('brandname.brandname');
const [activeTabKey, setActiveTabKey] = useState('0');
const [showGeneralDescription, setShowGeneralDescription] = useState(true);
const [showPermissions, setShowPermissions] = useState(false);
const [showCaches, setShowCaches] = useState(false);

useEffect(() => {
setShowGeneralDescription(activeTabKey === '0');
setShowPermissions(activeTabKey === '1');
setShowCaches(activeTabKey === '2');
}, [activeTabKey]);

interface AccessTab {
key: string;
name: string;
}

const handleTabClick = (event, nav) => {
setActiveTabKey(nav.itemId);
};

const tabs: AccessTab[] = [
{ name: t('access-management.role.tab-general'), key: '0' },
{ name: t('access-management.role.tab-permissions'), key: '1' },
{ name: t('access-management.role.tab-caches'), key: '2' }
];

const buildTabs = () => {
return (
<Nav data-cy="navigationTabs" onSelect={handleTabClick} variant={'tertiary'}>
<NavList>
{tabs.map((tab) => (
<NavItem
aria-label={'nav-item-' + tab.name}
key={'nav-item-' + tab.key}
itemId={tab.key}
isActive={activeTabKey === tab.key}
>
{tab.name}
</NavItem>
))}
</NavList>
</Nav>
);
};

const buildSelectedContent = (
<Card>
<CardBody>
{showGeneralDescription && <RoleGeneral name={roleName}/>}
{showPermissions && <RolePermissions />}
{showCaches && <RoleCaches />}
</CardBody>
</Card>
);

return (
<>
<PageSection variant={PageSectionVariants.light} style={{ paddingBottom: 0 }}>
<DataContainerBreadcrumb parentPage={'/access-management'}
label={'access-management.title'}
currentPage={t('access-management.role.breadcrumb', { roleName: roleName })} />
<TextContent style={{ marginTop: global_spacer_md.value, marginBottom: global_spacer_sm.value }}>
<Text component={TextVariants.h1}>{roleName}</Text>
</TextContent>
{buildTabs()}
</PageSection>
<PageSection variant={PageSectionVariants.default}>{buildSelectedContent}</PageSection>
</>
);
};

export { RoleDetail };
12 changes: 12 additions & 0 deletions src/app/AccessManagement/RoleDetailContent/RoleCaches.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useTranslation } from 'react-i18next';

const RoleCaches = () => {
const { t } = useTranslation();
const brandname = t('brandname.brandname');

return (
"Caches"
)
};

export { RoleCaches };
71 changes: 71 additions & 0 deletions src/app/AccessManagement/RoleDetailContent/RoleGeneral.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useTranslation } from 'react-i18next';
import { useDescribeRole } from '@app/services/rolesHook';
import {
Button,
ButtonVariant,
Card,
CardBody,
EmptyState,
EmptyStateActions,
EmptyStateBody,
EmptyStateFooter,
EmptyStateHeader,
EmptyStateIcon,
EmptyStateVariant,
Spinner,
Text,
TextContent
} from '@patternfly/react-core';
import * as React from 'react';
import { ExclamationCircleIcon } from '@patternfly/react-icons';
import { global_danger_color_200 } from '@patternfly/react-tokens';
import { Link } from 'react-router-dom';

const RoleGeneral= (props: { name: string }) => {
const { t } = useTranslation();
const {role, loading, error} = useDescribeRole(props.name);
const brandname = t('brandname.brandname');

const buildDetailContent = () => {
if (loading && !role) {
return (
<Spinner size='xl' />
);
}

if (error !== '') {
return (
<EmptyState variant={EmptyStateVariant.sm}>
<EmptyStateHeader
titleText={t('access-management.role.error', {roleName: props.name})}
icon={<EmptyStateIcon icon={ExclamationCircleIcon} color={global_danger_color_200.value} />}
headingLevel="h2"
/>
<EmptyStateBody>{error}</EmptyStateBody>
<EmptyStateFooter>
<EmptyStateActions>
<Link
to={{
pathname: '/access-management',
search: location.search
}}
>
<Button variant={ButtonVariant.secondary}>{t('common.actions.back')}</Button>
</Link>
</EmptyStateActions>
</EmptyStateFooter>
</EmptyState>
)
}

return (
<TextContent>
<Text>{role?.description}</Text>
</TextContent>
)
}

return buildDetailContent();
};

export { RoleGeneral };
12 changes: 12 additions & 0 deletions src/app/AccessManagement/RoleDetailContent/RolePermissions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useTranslation } from 'react-i18next';

const RolePermissions = () => {
const { t } = useTranslation();
const brandname = t('brandname.brandname');

return (
"Permissions"
)
};

export { RolePermissions };
10 changes: 8 additions & 2 deletions src/app/AccessManagement/RoleTableDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import {
Bullseye,
Button,
Expand Down Expand Up @@ -120,7 +121,12 @@ const RoleTableDisplay = () => {
<LockIcon className="role-icon" />
</Icon>
)}
{row.name}
<Link
data-cy={`detailLink-${row.name}`}
key={row.name}
to={{ pathname: '/role/' + encodeURIComponent(row.name), search: location.search }}
>{row.name}
</Link>
</Td>
<Td dataLabel={columnNames.permissions} width={30}>
{
Expand Down Expand Up @@ -258,7 +264,7 @@ const RoleTableDisplay = () => {
}
}}
>
{columnNames.name}
{columnNames.name}
</Th>
<Th>{columnNames.permissions}</Th>
<Th>{columnNames.description}</Th>
Expand Down
2 changes: 1 addition & 1 deletion src/app/Caches/Create/CreateCacheWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ const CreateCacheWizard = (props: { cacheManager: CacheManager; create: boolean
onClick={(event) => getPreviousStep(event, activeStep, onBack)}
data-cy="wizardBackButton"
>
{t('caches.create.back-button-label')}
{t('common.actions.back')}
</Button>
</ToolbarItem>
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/Caches/DetailCache.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ const DetailCache = (props: { cacheName: string }) => {
search: location.search
}}
>
<Button variant={ButtonVariant.secondary}>{t('caches.actions.back')}</Button>
<Button variant={ButtonVariant.secondary}>{t('common.actions.back')}</Button>
</Link>
</EmptyStateActions>
</EmptyStateFooter>
Expand Down
12 changes: 7 additions & 5 deletions src/app/Common/DataContainerBreadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Breadcrumb, BreadcrumbItem } from '@patternfly/react-core';
import { Link } from 'react-router-dom';
import * as React from 'react';
import { useTranslation } from 'react-i18next';

const DataContainerBreadcrumb = (props: { currentPage: string; cacheName?: string }) => {
const DataContainerBreadcrumb = (props: { currentPage: string; parentPage?: string, cacheName?: string, label?: string }) => {
const { t } = useTranslation();
const addCacheName = () => {
if (props.cacheName) {
return (
Expand All @@ -20,17 +22,17 @@ const DataContainerBreadcrumb = (props: { currentPage: string; cacheName?: strin
}
return;
};

const label = props.label? props.label : 'cache-managers.title';
return (
<Breadcrumb>
<BreadcrumbItem data-cy="dataContainerLink">
<BreadcrumbItem data-cy={props.parentPage? props.parentPage + 'Link' : 'dataContainerLink'}>
<Link
to={{
pathname: '/',
pathname: props.parentPage ? props.parentPage : '/',
search: location.search
}}
>
Data container
{t(label)}
</Link>
</BreadcrumbItem>
{addCacheName()}
Expand Down
2 changes: 1 addition & 1 deletion src/app/IndexManagement/IndexManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ const IndexManagement = (props) => {
}}
>
<Button variant={ButtonVariant.link} data-cy="backButton">
{t('caches.index.button-back-to-cache-detail')}
{t('common.actions.back')}
</Button>
</Link>
</ToolbarItem>
Expand Down
19 changes: 14 additions & 5 deletions src/app/assets/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
"configuration-docs-link": "https://infinispan.org/docs/stable/titles/configuring/configuring.html",
"default-roles-docs-link": "https://infinispan.org/docs/stable/titles/server/server.html#default-user-roles_server-getting-started"
},
"common" : {
"actions" : {
"back": "Back"
}
},
"layout": {
"console-name": "Server Management Console",
"skip-to-content": "Skip to Content",
Expand Down Expand Up @@ -219,7 +224,6 @@
"templates-placeholder": "Select a cache template",
"create-button-label": "Create",
"next-button-label": "Next",
"back-button-label": "Back",
"cancel-button-label": "Cancel",
"download-button-label": "Download",
"getting-started": {
Expand Down Expand Up @@ -534,8 +538,7 @@
"action-see-less": "See fewer cache details",
"action-see-more": "See more cache details",
"action-manage-indexes": "Manage indexes",
"refresh": "Refresh",
"back": "Back"
"refresh": "Refresh"
},
"entries": {
"action-edit": "Edit",
Expand Down Expand Up @@ -642,8 +645,7 @@
"size": "Index size in bytes",
"indexing-status": "Indexing",
"button-clear": "Clear index",
"button-rebuild": "Rebuild index",
"button-back-to-cache-detail": "Back"
"button-rebuild": "Rebuild index"
},
"rebalancing": {
"rebalancing": "Rebalancing",
Expand Down Expand Up @@ -886,6 +888,13 @@
"modal-delete-description-2": "You can always recreate the role.",
"delete-success": "Role {{name}} has been deleted",
"delete-error": "Unexpected error deleting the role {{name}}"
},
"role" : {
"breadcrumb": "Detail of role {{roleName}}",
"tab-general": "General settings",
"tab-permissions": "Permissions",
"tab-caches": "Accessible caches",
"error": "An error occurred while retrieving the role {{roleName}}"
}
}
}
12 changes: 11 additions & 1 deletion src/app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ConsoleServices } from '@services/ConsoleServices';
import { ConsoleACL } from '@services/securityService';
import { NotAuthorized } from '@app/NotAuthorized/NotAuthorized';
import { NotFound } from '@app/NotFound/NotFound';
import { RoleDetail } from '@app/AccessManagement/RoleDetail';

let routeFocusTimer: number;

Expand Down Expand Up @@ -186,7 +187,16 @@ const routes: IAppRoute[] = [
title: 'Connected Clients',
menu: true,
admin: true
}
},
{
component: RoleDetail,
exact: true,
label: 'Role detail',
path: '/role/:roleName',
title: 'Role detail',
menu: false,
admin: true
},
];

const AppRoutes = (props: { init: string }) => {
Expand Down
26 changes: 26 additions & 0 deletions src/app/services/rolesHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,29 @@ export function useDeleteRole(roleName: string, call: () => void) {
onDeleteRole
};
}

export function useDescribeRole(roleName: string) {
const [role, setRole] = useState<Role>();
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');

useEffect(() => {
if (loading) {
ConsoleServices.security()
.describeRole(
roleName
)
.then((either) => {
if (either.isRight()) {
setRole(either.value);
} else {
setError(either.value.message);
}
}).finally(() => setLoading(false));
}
}, [loading])

return {
role, loading, error, setLoading
};
}
Loading

0 comments on commit bf3cba2

Please sign in to comment.