Skip to content
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

Create Users list details drawer #1687

Merged
merged 10 commits into from
Nov 5, 2024
35 changes: 26 additions & 9 deletions .tekton/insights-rbac-ui-pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ spec:
- name: name
value: init
- name: bundle
value: quay.io/konflux-ci/tekton-catalog/task-init:0.2@sha256:092c113b614f6551113f17605ae9cb7e822aa704d07f0e37ed209da23ce392cc
value: quay.io/konflux-ci/tekton-catalog/task-init:0.2@sha256:f239f38bba3a8351c8cb0980fde8e2ee477ded7200178b0f45175e4006ff1dca
- name: kind
value: task
resolver: bundles
Expand Down Expand Up @@ -194,7 +194,7 @@ spec:
- name: name
value: prefetch-dependencies
- name: bundle
value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies:0.1@sha256:fe7234e3824d1e65d6a7aac352e7a6bbce623d90d8d7da9aceeee108ad2c61be
value: quay.io/konflux-ci/tekton-catalog/task-prefetch-dependencies:0.1@sha256:f53fe5482599b39ae2d1004cf09a2026fd9dd3822ab6ef46b51b4a398b0a3232
- name: kind
value: task
resolver: bundles
Expand Down Expand Up @@ -376,7 +376,7 @@ spec:
- name: name
value: buildah
- name: bundle
value: quay.io/konflux-ci/tekton-catalog/task-buildah:0.2@sha256:504e29e807544ccce8469b66e5c2c562c1d4860f87fb7df681021fae9a39b77d
value: quay.io/konflux-ci/tekton-catalog/task-buildah:0.2@sha256:312d8292fd972fc8e8a7ba254b54c31dabf85df731ca33af6823c3f62505ce00
- name: kind
value: task
resolver: bundles
Expand Down Expand Up @@ -415,6 +415,23 @@ spec:
workspaces:
- name: workspace
workspace: workspace
- name: rpms-signature-scan
params:
- name: image-digest
value: $(tasks.build-container.results.IMAGE_DIGEST)
- name: image-url
value: $(tasks.build-container.results.IMAGE_URL)
runAfter:
- build-container
taskRef:
params:
- name: name
value: rpms-signature-scan
- name: bundle
value: quay.io/konflux-ci/tekton-catalog/task-rpms-signature-scan:0.2@sha256:e6a5aa04e8b5e3b13313d14e4f70b68add0db3dbdd2757222c5465f8134fe517
- name: kind
value: task
resolver: bundles
- name: deprecated-base-image-check
params:
- name: IMAGE_URL
Expand All @@ -428,7 +445,7 @@ spec:
- name: name
value: deprecated-image-check
- name: bundle
value: quay.io/konflux-ci/tekton-catalog/task-deprecated-image-check:0.4@sha256:b4f9599f5770ea2e6e4d031224ccc932164c1ecde7f85f68e16e99c98d754003
value: quay.io/konflux-ci/tekton-catalog/task-deprecated-image-check:0.4@sha256:443ffa897ee35e416a0bfd39721c68cbf88cfa5c74c843c5183218d0cd586e82
- name: kind
value: task
resolver: bundles
Expand All @@ -450,7 +467,7 @@ spec:
- name: name
value: clair-scan
- name: bundle
value: quay.io/konflux-ci/tekton-catalog/task-clair-scan:0.2@sha256:28fee4bf5da87f2388c973d9336086749cad8436003f9a514e22ac99735e056b
value: quay.io/konflux-ci/tekton-catalog/task-clair-scan:0.2@sha256:5948fe10f5c37b4dfb2bdb0d765d1b55e9e09f7603e79ca2cd99e88b572bd506
- name: kind
value: task
resolver: bundles
Expand Down Expand Up @@ -492,7 +509,7 @@ spec:
- name: name
value: sast-snyk-check
- name: bundle
value: quay.io/konflux-ci/tekton-catalog/task-sast-snyk-check:0.3@sha256:afa2d6261c083a98665755ed19015010e222240d9db2ce9a84c8faa743273f31
value: quay.io/konflux-ci/tekton-catalog/task-sast-snyk-check:0.3@sha256:a1205ae88927b93cf47f83627f941fb6b97376a0a7dfaed45c4d48a8024b21ed
- name: kind
value: task
resolver: bundles
Expand All @@ -517,7 +534,7 @@ spec:
- name: name
value: clamav-scan
- name: bundle
value: quay.io/konflux-ci/tekton-catalog/task-clamav-scan:0.1@sha256:a94b6523ba0b691dc276e37594321c2eff3594d2753014e5c920803b47627df1
value: quay.io/konflux-ci/tekton-catalog/task-clamav-scan:0.1@sha256:747b43a12eddd40aa8ff12196767ca2648956d87d331d482e8883a7530bf4d5e
- name: kind
value: task
resolver: bundles
Expand All @@ -539,7 +556,7 @@ spec:
- name: name
value: sbom-json-check
- name: bundle
value: quay.io/konflux-ci/tekton-catalog/task-sbom-json-check:0.2@sha256:468b5615993bb6d75df3d66180df5eb8728bbef59efe509eb5ac89b7ac582f16
value: quay.io/konflux-ci/tekton-catalog/task-sbom-json-check:0.2@sha256:f3f441de3002c5654acdff0553fd54cb1409e6bef6ff68e514d1731c9688b5cc
- name: kind
value: task
resolver: bundles
Expand All @@ -559,7 +576,7 @@ spec:
- name: name
value: apply-tags
- name: bundle
value: quay.io/konflux-ci/tekton-catalog/task-apply-tags:0.1@sha256:f485e250fb060060892b633c495a3d7e38de1ec105ae1be48608b0401530ab2c
value: quay.io/konflux-ci/tekton-catalog/task-apply-tags:0.1@sha256:87fd7fc0e937aad1a8db9b6e377d7e444f53394dafde512d68adbea6966a4702
- name: kind
value: task
resolver: bundles
Expand Down
17 changes: 17 additions & 0 deletions .tekton/insights-rbac-ui-push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,23 @@ spec:
workspaces:
- name: workspace
workspace: workspace
- name: rpms-signature-scan
params:
- name: image-digest
value: $(tasks.build-container.results.IMAGE_DIGEST)
- name: image-url
value: $(tasks.build-container.results.IMAGE_URL)
runAfter:
- build-container
taskRef:
params:
- name: name
value: rpms-signature-scan
- name: bundle
value: quay.io/konflux-ci/tekton-catalog/task-rpms-signature-scan:0.2@sha256:7aa4d3c95e2b963e82fdda392f7cb3d61e3dab035416cf4a3a34e43cf3c9c9b8
- name: kind
value: task
resolver: bundles
- name: deprecated-base-image-check
params:
- name: IMAGE_URL
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
## [1.5.3](https://github.com/RedHatInsights/insights-rbac-ui/compare/v1.5.2...v1.5.3) (2024-11-03)


### Bug Fixes

* **deploy:** correct quay repository in frontend config ([#1693](https://github.com/RedHatInsights/insights-rbac-ui/issues/1693)) ([800fbb7](https://github.com/RedHatInsights/insights-rbac-ui/commit/800fbb7bf3efec362ac3f08e7a690b3982f2b4c3))

## [1.5.2](https://github.com/RedHatInsights/insights-rbac-ui/compare/v1.5.1...v1.5.2) (2024-11-01)


### Bug Fixes

* **konflux:** add rpms check to pull requests ([#1691](https://github.com/RedHatInsights/insights-rbac-ui/issues/1691)) ([981c7ce](https://github.com/RedHatInsights/insights-rbac-ui/commit/981c7ceae4bc46143f703f666484e2cea4338469))

## [1.5.1](https://github.com/RedHatInsights/insights-rbac-ui/compare/v1.5.0...v1.5.1) (2024-10-29)


### Bug Fixes

* **permission:** unable to load more than 2 pages in permissions ([#1688](https://github.com/RedHatInsights/insights-rbac-ui/issues/1688)) ([2d08c25](https://github.com/RedHatInsights/insights-rbac-ui/commit/2d08c25204ab35488a01c1c05cb51c942f244510))

# [1.5.0](https://github.com/RedHatInsights/insights-rbac-ui/compare/v1.4.0...v1.5.0) (2024-10-25)


Expand Down
8 changes: 8 additions & 0 deletions cypress/e2e/users-and-user-groups.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,12 @@ describe('Users and User Groups page', () => {
cy.get('[data-ouia-component-id="iam-users-table-add-user-button"]').click();
cy.get('[data-ouia-component-id="add-user-group-modal"]').should('be.visible');
});

it('can view user details when a user is clicked', () => {
cy.get('[data-ouia-component-id="iam-users-table-table-tr-0"]').click();
cy.get('[data-ouia-component-id="user-details-drawer"]').should('be.visible');
cy.get('[data-ouia-component-id="user-details-drawer"]').contains(mockUsers.data[0].first_name).should('exist');
cy.get('[data-ouia-component-id="user-details-drawer"]').contains(mockUsers.data[0].last_name).should('exist');
cy.get('[data-ouia-component-id="user-details-drawer"]').contains(mockUsers.data[0].email).should('exist');
});
});
2 changes: 1 addition & 1 deletion deploy/frontend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ parameters:
- name: IMAGE_TAG
required: true
- name: IMAGE
value: quay.io/cloudservices/rbac-frontend
value: quay.io/redhat-services-prod/rh-platform-experien-tenant/insights-rbac-ui
16 changes: 16 additions & 0 deletions src/Messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,11 @@ export default defineMessages({
description: 'Overview Hero third list item',
defaultMessage: `Assign users to these groups, allowing them to inherit the permissions associated with their group's roles`,
},
workspace: {
id: 'workspace',
description: 'Workspace singular label',
defaultMessage: 'Workspace',
},
workspaces: {
id: 'workspaces',
description: 'Workspaces heading',
Expand Down Expand Up @@ -2173,6 +2178,17 @@ export default defineMessages({
defaultMessage:
'Select a user group to add <b>{numUsers} {plural}</b> to. These are all the user groups in your account. To manage user groups, go to user groups.',
},
assignedRoles: {
id: 'assignedRoles',
description: 'User details assigned roles label',
defaultMessage: 'Assigned roles',
},
assignedRolesDescription: {
id: 'assignedRolesDescription',
description: 'User details roles info popover description',
defaultMessage:
'User groups are granted roles that contain a set of permissions. Roles are limited to the workspace in which they were assigned.',
},
assignedUserGroupsTooltipHeader: {
id: 'assignedUserGroupsTooltipHeader',
description: 'header for assigned user groups tooltip',
Expand Down
3 changes: 1 addition & 2 deletions src/redux/reducers/user-reducer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { FETCH_USERS, UPDATE_USERS_FILTERS } from '../action-types';
import { defaultSettings, PaginationDefaultI } from '../../helpers/shared/pagination';
import { UserProps } from '../../smart-components/user/user-table-helpers';

export interface User {
email: string;
Expand All @@ -25,7 +24,7 @@ export interface UserStore {
meta: PaginationDefaultI;
filters: UserFilters;
pagination: PaginationDefaultI & { redirected?: boolean };
data?: UserProps[];
data?: User[];
};
}

Expand Down
118 changes: 118 additions & 0 deletions src/smart-components/access-management/UserDetailsDrawer.tsx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be better to break this file into multiple ones to not have multiple components there

Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import {
Drawer,
DrawerActions,
DrawerCloseButton,
DrawerContent,
DrawerContentBody,
DrawerHead,
DrawerPanelContent,
Icon,
Popover,
Tab,
TabTitleText,
Tabs,
Text,
TextContent,
Title,
} from '@patternfly/react-core';
import React, { useEffect } from 'react';
import { User } from '../../redux/reducers/user-reducer';
import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons';
import { useIntl } from 'react-intl';
import messages from '../../Messages';
import UserDetailsGroupsView from './UserDetailsGroupsView';
import UserDetailsRolesView from './UserDetailsRolesView';
import { EventTypes, useDataViewEventsContext } from '@patternfly/react-data-view';

interface UserDetailsProps {
focusedUser?: User;
drawerRef: React.RefObject<HTMLDivElement>;
onClose: () => void;
ouiaId: string;
}

const UserDetailsDrawerContent: React.FunctionComponent<UserDetailsProps> = ({ focusedUser, drawerRef, onClose, ouiaId }) => {
const [activeTabKey, setActiveTabKey] = React.useState<string | number>(0);
const intl = useIntl();

return (
<DrawerPanelContent>
<DrawerHead>
<Title headingLevel="h2">
<span tabIndex={focusedUser ? 0 : -1} ref={drawerRef}>{`${focusedUser?.first_name} ${focusedUser?.last_name}`}</span>
</Title>
<TextContent>
<Text>{focusedUser?.email}</Text>
</TextContent>
<DrawerActions>
<DrawerCloseButton onClick={onClose} />
</DrawerActions>
</DrawerHead>
<Tabs isFilled activeKey={activeTabKey} onSelect={(_, tabIndex) => setActiveTabKey(tabIndex)}>
<Tab eventKey={0} title={intl.formatMessage(messages.userGroups)}>
{focusedUser && <UserDetailsGroupsView ouiaId={`${ouiaId}-user-groups-view`} userId={focusedUser.username} />}
</Tab>
<Tab
eventKey={1}
title={
<TabTitleText>
{intl.formatMessage(messages.assignedRoles)}
<Popover
triggerAction="hover"
position="top-end"
headerContent={intl.formatMessage(messages.assignedRoles)}
bodyContent={intl.formatMessage(messages.assignedRolesDescription)}
>
<Icon className="pf-v5-u-pl-sm" isInline>
<OutlinedQuestionCircleIcon />
</Icon>
</Popover>
</TabTitleText>
}
>
{focusedUser && <UserDetailsRolesView userId={focusedUser.username} ouiaId={`${ouiaId}-assigned-users-view`} />}
</Tab>
</Tabs>
</DrawerPanelContent>
);
};

interface DetailDrawerProps {
focusedUser?: User;
setFocusedUser: (user: User | undefined) => void;
children: React.ReactNode;
ouiaId: string;
}

const UserDetailsDrawer: React.FunctionComponent<DetailDrawerProps> = ({ focusedUser, setFocusedUser, children, ouiaId }) => {
const drawerRef = React.useRef<HTMLDivElement>(null);
const context = useDataViewEventsContext();

useEffect(() => {
const unsubscribe = context.subscribe(EventTypes.rowClick, (user: User | undefined) => {
setFocusedUser(user);
drawerRef.current?.focus();
});

return () => unsubscribe();
}, [drawerRef]);

return (
<Drawer isExpanded={Boolean(focusedUser)} data-ouia-component-id={ouiaId}>
<DrawerContent
panelContent={
<UserDetailsDrawerContent
ouiaId={`${ouiaId}-panel-content`}
drawerRef={drawerRef}
focusedUser={focusedUser}
onClose={() => setFocusedUser(undefined)}
/>
}
>
<DrawerContentBody hasPadding>{children}</DrawerContentBody>
</DrawerContent>
</Drawer>
);
};

export default UserDetailsDrawer;
43 changes: 43 additions & 0 deletions src/smart-components/access-management/UserDetailsGroupsView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { DataView, DataViewTable } from '@patternfly/react-data-view';
import React, { useCallback, useEffect } from 'react';
import { useIntl } from 'react-intl';
import { useDispatch, useSelector } from 'react-redux';
import { mappedProps } from '../../helpers/shared/helpers';
import { fetchGroups } from '../../redux/actions/group-actions';
import { RBACStore } from '../../redux/store';
import messages from '../../Messages';

interface UserGroupsViewProps {
userId: string;
ouiaId: string;
}

const UserDetailsGroupsView: React.FunctionComponent<UserGroupsViewProps> = ({ userId, ouiaId }) => {
const dispatch = useDispatch();
const intl = useIntl();
const columns: string[] = [intl.formatMessage(messages.userGroup), intl.formatMessage(messages.users)];

const groups = useSelector((state: RBACStore) => state.groupReducer?.groups?.data || []);

const fetchData = useCallback(() => {
dispatch(fetchGroups({ ...mappedProps({ username: userId }), usesMetaInURL: true, system: false }));
}, [dispatch, userId]);

useEffect(() => {
fetchData();
}, [fetchData]);

const rows = groups.map((group: any) => ({
row: [group.name, group.principalCount || '?'], // TODO: update once API provides principalCount [RHCLOUD-35963]
}));

return (
<div className="pf-v5-u-pt-md">
<DataView ouiaId={ouiaId}>
<DataViewTable variant="compact" aria-label="UserGroupsView" ouiaId={`${ouiaId}-table`} columns={columns} rows={rows} />
</DataView>
</div>
);
};

export default UserDetailsGroupsView;
Loading