diff --git a/packages/core/admin/admin/src/components/Roles/RoleRow/index.js b/packages/core/admin/admin/src/components/Roles/RoleRow/index.js index 9bf509be55..d5044716f4 100644 --- a/packages/core/admin/admin/src/components/Roles/RoleRow/index.js +++ b/packages/core/admin/admin/src/components/Roles/RoleRow/index.js @@ -1,9 +1,9 @@ -import { Box, Row, Td, Text, Tr, IconButton, BaseCheckbox } from '@strapi/parts'; +import { Box, Row, Td, Text, Tr, IconButton } from '@strapi/parts'; import PropTypes from 'prop-types'; import React from 'react'; import { useIntl } from 'react-intl'; -const RoleRow = ({ onToggle, id, name, description, usersCount, isChecked, icons }) => { +const RoleRow = ({ id, name, description, usersCount, icons }) => { const { formatMessage } = useIntl(); const usersCountText = formatMessage( @@ -16,19 +16,6 @@ const RoleRow = ({ onToggle, id, name, description, usersCount, isChecked, icons return ( - {Boolean(onToggle) && ( - - onToggle(id)} - value={isChecked} - aria-label={formatMessage( - { id: `Roles.RoleRow.select-all`, defaultMessage: 'Select {name} for bulk actions' }, - { name } - )} - /> - - )} {name} @@ -53,19 +40,12 @@ const RoleRow = ({ onToggle, id, name, description, usersCount, isChecked, icons ); }; -RoleRow.defaultProps = { - onToggle: undefined, - isChecked: undefined, -}; - RoleRow.propTypes = { id: PropTypes.number.isRequired, name: PropTypes.string.isRequired, description: PropTypes.string.isRequired, usersCount: PropTypes.number.isRequired, icons: PropTypes.array.isRequired, - onToggle: PropTypes.func, - isChecked: PropTypes.bool, }; export default RoleRow; diff --git a/packages/core/admin/admin/src/hooks/useRolesList/index.js b/packages/core/admin/admin/src/hooks/useRolesList/index.js index 5ace36a7e8..a96cc913fd 100644 --- a/packages/core/admin/admin/src/hooks/useRolesList/index.js +++ b/packages/core/admin/admin/src/hooks/useRolesList/index.js @@ -1,6 +1,7 @@ import { useEffect, useReducer, useCallback } from 'react'; -import { request, useNotification } from '@strapi/helper-plugin'; -import { get } from 'lodash'; +import { useNotification } from '@strapi/helper-plugin'; +import get from 'lodash/get'; +import { axiosInstance } from '../../core/utils'; import init from './init'; import reducer, { initialState } from './reducer'; @@ -23,7 +24,9 @@ const useRolesList = (shouldFetchData = true) => { type: 'GET_DATA', }); - const { data } = await request('/admin/roles', { method: 'GET' }); + const { + data: { data }, + } = await axiosInstance.get('/admin/roles'); dispatch({ type: 'GET_DATA_SUCCEEDED', diff --git a/packages/core/admin/admin/src/pages/Users/ListPage/index.js b/packages/core/admin/admin/src/pages/Users/ListPage/index.js index 29e6801671..547f1c4fac 100644 --- a/packages/core/admin/admin/src/pages/Users/ListPage/index.js +++ b/packages/core/admin/admin/src/pages/Users/ListPage/index.js @@ -1,8 +1,9 @@ import React from 'react'; import { CustomContentLayout, - useRBAC, + Search, SettingsPageTitle, + useRBAC, useNotification, useFocusWhenNavigate, } from '@strapi/helper-plugin'; @@ -15,7 +16,6 @@ import get from 'lodash/get'; import adminPermissions from '../../../permissions'; import DynamicTable from './DynamicTable'; import Filters from './Filters'; -import Search from './Search'; import PaginationFooter from './PaginationFooter'; import { deleteData, fetchData } from './utils/api'; import displayedFilters from './utils/displayedFilters'; diff --git a/packages/core/admin/admin/src/pages/Users/ListPage/tests/index.test.js b/packages/core/admin/admin/src/pages/Users/ListPage/tests/index.test.js index 132fa139d4..a62b8723f6 100644 --- a/packages/core/admin/admin/src/pages/Users/ListPage/tests/index.test.js +++ b/packages/core/admin/admin/src/pages/Users/ListPage/tests/index.test.js @@ -699,46 +699,6 @@ describe('ADMIN | Pages | USERS | ListPage', () => { height: 0.25rem; } - .c13 { - padding-right: 56px; - padding-left: 56px; - } - - .c36 tr:last-of-type { - border-bottom: none; - } - - .c37 { - border-bottom: 1px solid #eaeaef; - } - - .c37 td, - .c37 th { - padding: 16px; - } - - .c37 td:first-of-type, - .c37 th:first-of-type { - padding: 0 4px; - } - - .c38 { - vertical-align: middle; - text-align: left; - color: #666687; - outline-offset: -4px; - } - - .c38 input { - vertical-align: sub; - } - - .c34 { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - .c15 { display: -webkit-box; display: -webkit-flex; @@ -788,6 +748,46 @@ describe('ADMIN | Pages | USERS | ListPage', () => { fill: #666687; } + .c13 { + padding-right: 56px; + padding-left: 56px; + } + + .c36 tr:last-of-type { + border-bottom: none; + } + + .c37 { + border-bottom: 1px solid #eaeaef; + } + + .c37 td, + .c37 th { + padding: 16px; + } + + .c37 td:first-of-type, + .c37 th:first-of-type { + padding: 0 4px; + } + + .c38 { + vertical-align: middle; + text-align: left; + color: #666687; + outline-offset: -4px; + } + + .c38 input { + vertical-align: sub; + } + + .c34 { + -webkit-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg); + } +
{ } = useRBAC(adminPermissions.settings.roles); const { getData, roles, isLoading } = useRolesList(false); - const query = useQuery(); - const _q = decodeURIComponent(query.get('_q') || ''); + const [{ query }] = useQueryParams(); + const _q = query?._q || ''; const sortedRoles = matchSorter(roles, _q, { keys: ['name', 'description'] }); useEffect(() => { @@ -63,62 +64,33 @@ const useSortedRoles = () => { }; }; -const useRoleActions = ({ getData, canCreate, canDelete, canUpdate, roles, sortedRoles }) => { +const useRoleActions = ({ getData, canCreate, canDelete, canUpdate }) => { const { formatMessage } = useIntl(); const toggleNotification = useNotification(); const [isWarningDeleteAllOpened, setIsWarningDeleteAllOpenend] = useState(false); const { push } = useHistory(); - const [ - { selectedRoles, showModalConfirmButtonLoading, shouldRefetchData }, - dispatch, - ] = useReducer(reducer, initialState); + const [{ selectedRoles, showModalConfirmButtonLoading, roleToDelete }, dispatch] = useReducer( + reducer, + initialState + ); - const handleClosedModal = () => { - if (shouldRefetchData) { - getData(); - } - - // Empty the selected ids when the modal closes - dispatch({ - type: 'RESET_DATA_TO_DELETE', - }); - }; - - const handleConfirmDeleteData = async () => { + const handleDeleteData = async () => { try { dispatch({ type: 'ON_REMOVE_ROLES', }); - const filteredRoles = selectedRoles.filter(currentId => { - const currentRole = roles.find(role => role.id === currentId); - return currentRole.usersCount === 0; + await axiosInstance.post('/admin/roles/batch-delete', { + ids: [roleToDelete], }); - if (selectedRoles.length !== filteredRoles.length) { - toggleNotification({ - type: 'info', - message: { id: 'Roles.ListPage.notification.delete-all-not-allowed' }, - }); - } + await getData(); - if (filteredRoles.length) { - await request('/admin/roles/batch-delete', { - method: 'POST', - body: { - ids: filteredRoles, - }, - }); - - // Empty the selectedRolesId and set the shouldRefetchData to true so the - // list is updated when closing the modal - dispatch({ - type: 'ON_REMOVE_ROLES_SUCCEEDED', - }); - } + dispatch({ + type: 'RESET_DATA_TO_DELETE', + }); } catch (err) { - console.error(err); const errorIds = get(err, ['response', 'payload', 'data', 'ids'], null); if (errorIds && Array.isArray(errorIds)) { @@ -133,9 +105,8 @@ const useRoleActions = ({ getData, canCreate, canDelete, canUpdate, roles, sorte message: { id: 'notification.error' }, }); } - } finally { - handleToggleModal(); } + handleToggleModal(); }; const onRoleDuplicate = useCallback( @@ -156,19 +127,6 @@ const useRoleActions = ({ getData, canCreate, canDelete, canUpdate, roles, sorte handleToggleModal(); }, []); - const onRoleToggle = roleId => { - dispatch({ - type: 'ON_SELECTION', - id: roleId, - }); - }; - - const onAllRolesToggle = () => - dispatch({ - type: 'TOGGLE_ALL', - ids: sortedRoles.map(r => r.id), - }); - const handleToggleModal = () => setIsWarningDeleteAllOpenend(prev => !prev); const handleGoTo = useCallback( @@ -246,16 +204,13 @@ const useRoleActions = ({ getData, canCreate, canDelete, canUpdate, roles, sorte ); return { - handleClosedModal, - handleConfirmDeleteData, handleNewRoleClick, - onRoleToggle, - onAllRolesToggle, getIcons, selectedRoles, isWarningDeleteAllOpened, showModalConfirmButtonLoading, handleToggleModal, + handleDeleteData, }; }; @@ -271,21 +226,16 @@ const RoleListPage = () => { isLoading, getData, sortedRoles, - roles, } = useSortedRoles(); const { - handleClosedModal, - handleConfirmDeleteData, handleNewRoleClick, - onRoleToggle, - onAllRolesToggle, getIcons, - selectedRoles, isWarningDeleteAllOpened, showModalConfirmButtonLoading, handleToggleModal, - } = useRoleActions({ getData, canCreate, canDelete, canUpdate, roles, sortedRoles }); + handleDeleteData, + } = useRoleActions({ getData, canCreate, canDelete, canUpdate }); // ! TODO - Show the search bar only if the user is allowed to read - add the search input // canRead @@ -293,11 +243,6 @@ const RoleListPage = () => { const rowCount = sortedRoles.length + 1; const colCount = 6; - const isAllEntriesIndeterminate = selectedRoles.length - ? selectedRoles.length !== rowCount - : false; - const isAllChecked = selectedRoles.length ? selectedRoles.length === rowCount : false; - if (isLoadingForPermissions) { return ; } @@ -327,6 +272,7 @@ const RoleListPage = () => { })} as="h2" /> + {canRead && } />} {canRead && ( { > - {!!onRoleToggle && ( - - )}
- - {formatMessage({ @@ -394,10 +330,6 @@ const RoleListPage = () => { selectedRoleId === role.id) !== -1 - } name={role.name} description={role.description} usersCount={role.usersCount} @@ -409,12 +341,11 @@ const RoleListPage = () => { {!rowCount && !isLoading && } )} - ); diff --git a/packages/core/admin/ee/admin/pages/Roles/ListPage/reducer.js b/packages/core/admin/ee/admin/pages/Roles/ListPage/reducer.js index 8b7e07b04c..5295d92757 100644 --- a/packages/core/admin/ee/admin/pages/Roles/ListPage/reducer.js +++ b/packages/core/admin/ee/admin/pages/Roles/ListPage/reducer.js @@ -2,7 +2,7 @@ import produce from 'immer'; export const initialState = { - selectedRoles: [], + roleToDelete: null, showModalConfirmButtonLoading: false, shouldRefetchData: false, }; @@ -10,57 +10,26 @@ export const initialState = { const reducer = (state, action) => produce(state, draftState => { switch (action.type) { - case 'ON_SELECTION': { - const { id } = action; - const roleIndex = state.selectedRoles.findIndex(roleId => roleId === id); - - if (roleIndex === -1) { - draftState.selectedRoles.push(id); - } else { - draftState.selectedRoles = state.selectedRoles.filter(roleId => roleId !== id); - } - break; - } - case 'TOGGLE_ALL': { - if (state.selectedRoles.length) { - draftState.selectedRoles = []; - } else { - const { ids } = action; - draftState.selectedRoles = ids; - } - break; - } case 'ON_REMOVE_ROLES': { draftState.showModalConfirmButtonLoading = true; break; } case 'ON_REMOVE_ROLES_SUCCEEDED': { draftState.shouldRefetchData = true; + draftState.roleToDelete = null; break; } case 'RESET_DATA_TO_DELETE': { draftState.shouldRefetchData = false; - draftState.selectedRoles = []; + draftState.roleToDelete = null; draftState.showModalConfirmButtonLoading = false; break; } case 'SET_ROLE_TO_DELETE': { - draftState.selectedRoles = [action.id]; + draftState.roleToDelete = action.id; break; } - // Leaving this code for the moment - // case 'ON_DUPLICATION': { - // const { id } = action; - // draftState.roles = state.roles.reduce((acc, c) => { - // if (c.id === id) { - // return acc.concat([c, { ...c, id: state.roles.length + 1 }]); - // } - - // return [...acc, c]; - // }, []); - // break; - // } default: return draftState; } diff --git a/packages/core/admin/ee/admin/pages/Roles/ListPage/tests/reducer.test.js b/packages/core/admin/ee/admin/pages/Roles/ListPage/tests/reducer.test.js index e72815f5ab..f16c8d422e 100644 --- a/packages/core/admin/ee/admin/pages/Roles/ListPage/tests/reducer.test.js +++ b/packages/core/admin/ee/admin/pages/Roles/ListPage/tests/reducer.test.js @@ -11,54 +11,18 @@ describe('ADMIN | ee | CONTAINERS | ROLES | ListPage | reducer', () => { }); }); - describe('ON_SELECTION', () => { - it('should add the selected role correctly', () => { - const action = { - type: 'ON_SELECTION', - id: 2, - }; - const initialState = { - selectedRoles: [], - shouldRefetchData: false, - }; - const expected = { - selectedRoles: [2], - shouldRefetchData: false, - }; - - expect(reducer(initialState, action)).toEqual(expected); - }); - - it('should remove the selected role correctly', () => { - const action = { - type: 'ON_SELECTION', - id: 2, - }; - const initialState = { - selectedRoles: [1, 2], - shouldRefetchData: false, - }; - const expected = { - selectedRoles: [1], - shouldRefetchData: false, - }; - - expect(reducer(initialState, action)).toEqual(expected); - }); - }); - describe('ON_REMOVE_ROLES', () => { it('should set the showModalConfirmButtonLoading to true', () => { const action = { type: 'ON_REMOVE_ROLES', }; const initialState = { - selectedRoles: [], + roleToDelete: 1, shouldRefetchData: false, showModalConfirmButtonLoading: false, }; const expected = { - selectedRoles: [], + roleToDelete: 1, shouldRefetchData: false, showModalConfirmButtonLoading: true, }; @@ -73,12 +37,12 @@ describe('ADMIN | ee | CONTAINERS | ROLES | ListPage | reducer', () => { type: 'ON_REMOVE_ROLES_SUCCEEDED', }; const initialState = { - selectedRoles: [], + roleToDelete: 1, shouldRefetchData: false, showModalConfirmButtonLoading: true, }; const expected = { - selectedRoles: [], + roleToDelete: null, shouldRefetchData: true, showModalConfirmButtonLoading: true, }; @@ -87,26 +51,6 @@ describe('ADMIN | ee | CONTAINERS | ROLES | ListPage | reducer', () => { }); }); - describe('RESET_DATA_TO_DELETE', () => { - it('should empty the selected role array and set the shouldRefetchData to false', () => { - const action = { - type: 'RESET_DATA_TO_DELETE', - }; - const initialState = { - selectedRoles: [1, 2, 4], - shouldRefetchData: true, - showModalConfirmButtonLoading: true, - }; - const expected = { - selectedRoles: [], - shouldRefetchData: false, - showModalConfirmButtonLoading: false, - }; - - expect(reducer(initialState, action)).toEqual(expected); - }); - }); - describe('SET_ROLE_TO_DELETE', () => { it('should set the selected roles property correctly', () => { const action = { @@ -114,11 +58,11 @@ describe('ADMIN | ee | CONTAINERS | ROLES | ListPage | reducer', () => { id: 6, }; const initialState = { - selectedRoles: [1, 2, 4], + roleToDelete: null, shouldRefetchData: false, }; const expected = { - selectedRoles: [6], + roleToDelete: 6, shouldRefetchData: false, }; diff --git a/packages/core/admin/admin/src/pages/Users/ListPage/Search/index.js b/packages/core/helper-plugin/lib/src/components/Search/index.js similarity index 90% rename from packages/core/admin/admin/src/pages/Users/ListPage/Search/index.js rename to packages/core/helper-plugin/lib/src/components/Search/index.js index eafce8e68e..7c57241b80 100644 --- a/packages/core/admin/admin/src/pages/Users/ListPage/Search/index.js +++ b/packages/core/helper-plugin/lib/src/components/Search/index.js @@ -1,13 +1,13 @@ import React, { useEffect, useState } from 'react'; -import { useQueryParams } from '@strapi/helper-plugin'; import { SearchIcon } from '@strapi/icons'; import { IconButton } from '@strapi/parts/IconButton'; import { TextInput } from '@strapi/parts/TextInput'; +import useQueryParams from '../../hooks/useQueryParams'; const Search = () => { const [isOpen, setIsOpen] = useState(false); const [{ query }, setQuery] = useQueryParams(); - const [value, setValue] = useState(query._q || ''); + const [value, setValue] = useState(query?._q || ''); useEffect(() => { const handler = setTimeout(() => { diff --git a/packages/core/helper-plugin/lib/src/index.js b/packages/core/helper-plugin/lib/src/index.js index 2a6e9c3076..8a57bc9f39 100644 --- a/packages/core/helper-plugin/lib/src/index.js +++ b/packages/core/helper-plugin/lib/src/index.js @@ -179,6 +179,7 @@ export { default as EmptyBodyTable } from './components/EmptyBodyTable'; export * from './components/InjectionZone'; export { default as LoadingIndicatorPage } from './components/LoadingIndicatorPage'; export { default as SettingsPageTitle } from './components/SettingsPageTitle'; +export { default as Search } from './components/Search'; export { default as Status } from './components/Status'; // New icons