Skip to content

Commit

Permalink
feat: improve users and user groups routing
Browse files Browse the repository at this point in the history
  • Loading branch information
fhlavac committed Jan 2, 2025
1 parent 6cef2fd commit e7878ae
Show file tree
Hide file tree
Showing 17 changed files with 292 additions and 199 deletions.
26 changes: 22 additions & 4 deletions src/Routing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,34 @@ const AddGroupServiceAccounts = lazy(() => import('./smart-components/group/serv
const RemoveServiceAccountFromGroup = lazy(() => import('./smart-components/group/service-account/remove-group-service-accounts'));
const QuickstartsTest = lazy(() => import('./smart-components/quickstarts/quickstarts-test'));

const UsersAndUserGroups = lazy(() => import('./smart-components/access-management/users-and-user-groups'));
const UsersAndUserGroups = lazy(() => import('./smart-components/access-management/users-and-user-groups/users-and-user-groups'));
const UsersView = lazy(() => import('./smart-components/access-management/users-and-user-groups/users/UsersView'));
const UserGroupsView = lazy(() => import('./smart-components/access-management/users-and-user-groups/user-groups/UserGroupsView'));

const getRoutes = ({ enableServiceAccounts, isITLess, isWorkspacesFlag, isCommonAuthModel }: Record<string, boolean>) => [
{
path: pathnames['users-and-user-groups'].path,
element: UsersAndUserGroups,
childRoutes: [
isCommonAuthModel && {
path: pathnames['invite-group-users'].path,
element: InviteUsersModalCommonAuth,
{
path: pathnames['users-new'].path,
element: UsersView,
childRoutes: [
isCommonAuthModel && {
path: pathnames['invite-group-users'].path,
element: InviteUsersModalCommonAuth,
},
],
},
{
path: pathnames['user-groups'].path,
element: UserGroupsView,
childRoutes: [
{
path: pathnames['create-user-group'].path,
element: AddGroupWizard,
},
],
},
],
},
Expand Down
106 changes: 0 additions & 106 deletions src/smart-components/access-management/users-and-user-groups.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import React, { useEffect, useCallback, useMemo, useState, Fragment } from 'react';
import React, { useEffect, useCallback, useMemo, useState, Fragment, Suspense } from 'react';
import { formatDistanceToNow } from 'date-fns';
import { FormattedMessage, useIntl } from 'react-intl';
import { useSelector, useDispatch } from 'react-redux';
import { Outlet, useSearchParams } from 'react-router-dom';
import { useDataViewSelection, useDataViewPagination } from '@patternfly/react-data-view/dist/dynamic/Hooks';
import { BulkSelect, BulkSelectValue } from '@patternfly/react-component-groups/dist/dynamic/BulkSelect';
import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView';
import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar';
import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
import { ButtonVariant, EmptyState, EmptyStateBody, EmptyStateHeader, EmptyStateIcon, Pagination, Tooltip } from '@patternfly/react-core';
import { ActionsColumn } from '@patternfly/react-table';
import { mappedProps } from '../../helpers/shared/helpers';
import { RBACStore } from '../../redux/store';
import { useSearchParams } from 'react-router-dom';
import { fetchGroups, removeGroups } from '../../redux/actions/group-actions';
import { formatDistanceToNow } from 'date-fns';
import { FormattedMessage, useIntl } from 'react-intl';
import messages from '../../Messages';
import { Group } from '../../redux/reducers/group-reducer';
import { DataViewTrObject, DataViewState, EventTypes, useDataViewEventsContext } from '@patternfly/react-data-view';
import { SearchIcon } from '@patternfly/react-icons';
import { ActionsColumn } from '@patternfly/react-table';
import { ResponsiveAction, ResponsiveActions, SkeletonTableBody, SkeletonTableHead, WarningModal } from '@patternfly/react-component-groups';
import AddGroupWizard from '../group/add-group/add-group-wizard';
import { mappedProps } from '../../../../helpers/shared/helpers';
import { RBACStore } from '../../../../redux/store';
import { fetchGroups, removeGroups } from '../../../../redux/actions/group-actions';
import { Group } from '../../../../redux/reducers/group-reducer';
import useAppNavigate from '../../../../hooks/useAppNavigate';
import pathnames from '../../../../utilities/pathnames';
import messages from '../../../../Messages';

const COLUMNS: string[] = ['User group name', 'Description', 'Users', 'Service accounts', 'Roles', 'Workspaces', 'Last modified'];

Expand Down Expand Up @@ -67,11 +68,12 @@ const UserGroupsTable: React.FunctionComponent<UserGroupsTableProps> = ({
focusedGroup,
}) => {
const [isDeleteModalOpen, setIsDeleteModalOpen] = React.useState(false);
const [isAddGroupWizardOpen, setIsAddGroupWizardOpen] = React.useState(false);
const [currentGroups, setCurrentGroups] = React.useState<Group[]>([]);
const dispatch = useDispatch();
const [activeState, setActiveState] = useState<DataViewState | undefined>(DataViewState.loading);
const intl = useIntl();
const navigate = useAppNavigate();
const search = useSearchParams();
const { trigger } = useDataViewEventsContext();

const handleDeleteModalToggle = (groups: Group[]) => {
Expand Down Expand Up @@ -228,11 +230,6 @@ const UserGroupsTable: React.FunctionComponent<UserGroupsTableProps> = ({

return (
<Fragment>
{isAddGroupWizardOpen && (
<div data-ouia-component-id="add-group-wizard">
<AddGroupWizard pagination={{ limit: 20 }} filters={{}} enableRoles={false} setIsWizardOpen={setIsAddGroupWizardOpen} />
</div>
)}
{isDeleteModalOpen && (
<WarningModal
ouiaId={`${ouiaId}-remove-user-modal`}
Expand Down Expand Up @@ -274,7 +271,7 @@ const UserGroupsTable: React.FunctionComponent<UserGroupsTableProps> = ({
}
actions={
<ResponsiveActions breakpoint="lg" ouiaId={`${ouiaId}-actions-dropdown`}>
<ResponsiveAction isPinned isPersistent onClick={() => setIsAddGroupWizardOpen(true)}>
<ResponsiveAction isPinned isPersistent onClick={() => navigate(pathnames['create-user-group'].link)}>
{intl.formatMessage(messages.createUserGroup)}
</ResponsiveAction>
<ResponsiveAction
Expand Down Expand Up @@ -306,6 +303,26 @@ const UserGroupsTable: React.FunctionComponent<UserGroupsTableProps> = ({
/>
<DataViewToolbar ouiaId={`${ouiaId}-footer-toolbar`} pagination={paginationComponent} />
</DataView>
<Suspense>
<Outlet
context={{
[pathnames['create-user-group'].path]: {
afterSubmit: () => {
navigate({ pathname: pathnames['user-groups'].link });
},
onCancel: () =>
navigate({
pathname: pathnames['user-groups'].link,
search: search.toString(),
}),
enableRoles: false,
pagination: { limit: perPage },
filters: {},
postMethod: fetchData,
},
}}
/>
</Suspense>
</Fragment>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { DataViewEventsProvider } from '@patternfly/react-data-view';
import React from 'react';
import GroupDetailsDrawer from './user-group-detail/GroupDetailsDrawer';
import { TabContent } from '@patternfly/react-core';
import UserGroupsTable from './UserGroupsTable';
import { Group } from '../../../../redux/reducers/group-reducer';

interface UserGroupsViewProps {
groupsRef?: React.Ref<HTMLElement>;
}

const UserGroupsView: React.FunctionComponent<UserGroupsViewProps> = ({ groupsRef }) => {
const [focusedGroup, setFocusedGroup] = React.useState<Group | undefined>(undefined);

return (
<DataViewEventsProvider>
<GroupDetailsDrawer ouiaId="groups-details-drawer" focusedGroup={focusedGroup} setFocusedGroup={setFocusedGroup}>
<TabContent eventKey={1} id="groupsTab" ref={groupsRef} aria-label="Groups tab">
<UserGroupsTable focusedGroup={focusedGroup} />
</TabContent>
</GroupDetailsDrawer>
</DataViewEventsProvider>
);
};

export default UserGroupsView;
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import {
import React, { useEffect } from 'react';
import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons';
import { useIntl } from 'react-intl';
import messages from '../../Messages';
import { Group } from '../../redux/reducers/group-reducer';
import messages from '../../../../../Messages';
import { Group } from '../../../../../redux/reducers/group-reducer';
import GroupDetailsRolesView from './GroupDetailsRolesView';
import GroupDetailsServiceAccountsView from './GroupDetailsServiceAccountsView';
import GroupDetailsUsersView from './GroupDetailsUsersView';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { DataView, DataViewTable } from '@patternfly/react-data-view';
import React, { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { RBACStore } from '../../redux/store';
import messages from '../../Messages';
import { RBACStore } from '../../../../../redux/store';
import messages from '../../../../../Messages';
import { useIntl } from 'react-intl';
import { fetchRolesForGroup } from '../../redux/actions/group-actions';
import { fetchRolesForGroup } from '../../../../../redux/actions/group-actions';

interface GroupRolesViewProps {
groupId: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { DataView, DataViewTable } from '@patternfly/react-data-view';
import { useIntl } from 'react-intl';
import React, { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { RBACStore } from '../../redux/store';
import messages from '../../Messages';
import { useIntl } from 'react-intl';
import { fetchServiceAccountsForGroup } from '../../redux/actions/group-actions';
import { RBACStore } from '../../../../../redux/store';
import messages from '../../../../../Messages';
import { fetchServiceAccountsForGroup } from '../../../../../redux/actions/group-actions';

interface GroupDetailsServiceAccountsViewProps {
groupId: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { DataView, DataViewTable } from '@patternfly/react-data-view';
import React, { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { RBACStore } from '../../redux/store';
import messages from '../../Messages';
import { RBACStore } from '../../../../../redux/store';
import messages from '../../../../../Messages';
import { useIntl } from 'react-intl';
import { fetchMembersForGroup } from '../../redux/actions/group-actions';
import { fetchMembersForGroup } from '../../../../../redux/actions/group-actions';

interface GroupDetailsUsersViewProps {
groupId: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React, { useEffect, useMemo } from 'react';
import { useIntl } from 'react-intl';
import { Outlet, useLocation } from 'react-router-dom';
import { PageSection, PageSectionVariants, Tab, Tabs } from '@patternfly/react-core';
import ContentHeader from '@patternfly/react-component-groups/dist/dynamic/ContentHeader';
import useAppNavigate from '../../../hooks/useAppNavigate';
import pathnames from '../../../utilities/pathnames';
import Messages from '../../../Messages';

const UsersAndUserGroups: React.FunctionComponent = () => {
const intl = useIntl();
const usersRef = React.createRef<HTMLElement>();
const groupsRef = React.createRef<HTMLElement>();

const navigate = useAppNavigate('/iam/access-management');
const location = useLocation();
const activeTabIndex = useMemo(() => Number(location.pathname.endsWith(pathnames['user-groups'].link)), [location.pathname]);

useEffect(() => {
location.pathname.endsWith(pathnames['users-and-user-groups'].link) && navigate(pathnames['users-new'].link, { replace: true });
}, [location.pathname, navigate]);

const handleTabSelect = (_: React.MouseEvent<HTMLElement, MouseEvent>, key: string | number) => {
activeTabIndex !== Number(key) &&
navigate((activeTabIndex ? pathnames['users-new'] : pathnames['user-groups']).link, {
replace: true,
});
};

return (
<React.Fragment>
<ContentHeader title={intl.formatMessage(Messages.usersAndUserGroups)} subtitle={intl.formatMessage(Messages.usersAndUserGroupsDescription)} />
<PageSection type="tabs" variant={PageSectionVariants.light} isWidthLimited>
<Tabs
activeKey={activeTabIndex}
onSelect={handleTabSelect}
inset={{
default: 'insetNone',
md: 'insetSm',
xl: 'insetLg',
}}
role="region"
>
<Tab eventKey={0} title={intl.formatMessage(Messages.users)} tabContentId="usersTab" tabContentRef={usersRef} ouiaId="users-tab-button" />
<Tab
eventKey={1}
title={intl.formatMessage(Messages.userGroups)}
tabContentId="groupsTab"
tabContentRef={groupsRef}
ouiaId="user-groups-tab-button"
/>
</Tabs>
</PageSection>
<PageSection padding={{ default: 'noPadding' }}>
<Outlet
context={{
[pathnames['users-new'].path]: {
usersRef,
},
[pathnames['user-groups'].path]: {
groupsRef,
},
}}
/>
</PageSection>
</React.Fragment>
);
};

export default UsersAndUserGroups;
Loading

0 comments on commit e7878ae

Please sign in to comment.