diff --git a/src/Messages.js b/src/Messages.js index 79459ab93..76a3f0caf 100644 --- a/src/Messages.js +++ b/src/Messages.js @@ -2456,6 +2456,16 @@ export default defineMessages({ description: 'Empty state subtitle role bindings', defaultMessage: 'This filter criteria matches no role assignments.{br}Try changing your filter input.', }, + rolesEmptyStateTitle: { + id: 'rolesEmptyStateTitle', + description: 'Empty state title Roles', + defaultMessage: 'No roles found', + }, + rolesEmptyStateSubtitle: { + id: 'rolesEmptyStateSubtitle', + description: 'Empty state subtitle Roles', + defaultMessage: 'This filter criteria matches no roles.{br}Try changing your filter input.', + }, userGroupsEmptyStateTitle: { id: 'userGroupsEmptyStateTitle', description: 'Empty state title User groups', diff --git a/src/smart-components/role/RolesTable.tsx b/src/smart-components/role/RolesTable.tsx index c0d2faa30..39b106cbe 100644 --- a/src/smart-components/role/RolesTable.tsx +++ b/src/smart-components/role/RolesTable.tsx @@ -2,13 +2,24 @@ import React, { useEffect, useCallback, useState, useRef, useMemo } from 'react' import { useSelector, useDispatch } from 'react-redux'; 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 { DataView, DataViewState } 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 { DataViewEventsProvider, EventTypes, useDataViewEventsContext } from '@patternfly/react-data-view/dist/dynamic/DataViewEventsContext'; import { useDataViewSort } from '@patternfly/react-data-view/dist/dynamic/Hooks'; -import { ButtonVariant, Drawer, DrawerContent, DrawerContentBody, PageSection, Pagination } from '@patternfly/react-core'; -import { ActionsColumn, ThProps } from '@patternfly/react-table'; +import { + ButtonVariant, + Drawer, + DrawerContent, + DrawerContentBody, + EmptyState, + EmptyStateBody, + EmptyStateHeader, + EmptyStateIcon, + PageSection, + Pagination, +} from '@patternfly/react-core'; +import { ActionsColumn, TableVariant, ThProps } from '@patternfly/react-table'; import ContentHeader from '@patternfly/react-component-groups/dist/esm/ContentHeader'; import { fetchRolesWithPolicies, removeRole } from '../../redux/actions/role-actions'; import { FormattedMessage, useIntl } from 'react-intl'; @@ -18,9 +29,33 @@ import { Role } from '../../redux/reducers/role-reducer'; import { RBACStore } from '../../redux/store'; import { useSearchParams } from 'react-router-dom'; import RolesDetails from './RolesTableDetails'; -import { ResponsiveAction, ResponsiveActions, WarningModal } from '@patternfly/react-component-groups'; +import { + ResponsiveAction, + ResponsiveActions, + SkeletonTable, + SkeletonTableBody, + SkeletonTableHead, + WarningModal, +} from '@patternfly/react-component-groups'; import { DataViewTextFilter, DataViewTh, DataViewTr, DataViewTrObject, useDataViewFilters } from '@patternfly/react-data-view'; import { PER_PAGE_OPTIONS } from '../../helpers/shared/pagination'; +import { SearchIcon } from '@patternfly/react-icons'; + +const EmptyTable: React.FunctionComponent<{ titleText: string }> = ({ titleText }) => { + return ( + + } /> + + , + }} + /> + + + ); +}; interface RoleFilters { display_name: string; @@ -35,9 +70,10 @@ interface RolesTableProps { const RolesTable: React.FunctionComponent = ({ selectedRole }) => { const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [currentRoles, setCurrentRoles] = useState([]); - const { roles, totalCount } = useSelector((state: RBACStore) => ({ + const { roles, totalCount, isLoading } = useSelector((state: RBACStore) => ({ roles: state.roleReducer.roles.data || [], totalCount: state.roleReducer.roles.meta.count, + isLoading: state.roleReducer.isLoading, })); const { trigger } = useDataViewEventsContext(); @@ -60,6 +96,7 @@ const RolesTable: React.FunctionComponent = ({ selectedRole }) const dispatch = useDispatch(); const [searchParams, setSearchParams] = useSearchParams(); + const [activeState, setActiveState] = useState(DataViewState.loading); const { filters, onSetFilters, clearAllFilters } = useDataViewFilters({ initialFilters: { display_name: '' }, searchParams, @@ -92,6 +129,14 @@ const RolesTable: React.FunctionComponent = ({ selectedRole }) }); }, [fetchData, page, perPage, sortBy, direction]); + useEffect(() => { + if (isLoading) { + setActiveState(DataViewState.loading); + } else { + totalCount === 0 ? setActiveState(DataViewState.empty) : setActiveState(undefined); + } + }, [totalCount, isLoading]); + useEffect(() => { debouncedFetch( () => @@ -208,7 +253,7 @@ const RolesTable: React.FunctionComponent = ({ selectedRole }) /> )} - + = ({ selectedRole }) /> } /> - + {isLoading ? ( + + ) : ( + }} + bodyStates={{ + loading: , + empty: , + }} + /> + )}