From 33be829a67cca670373edc56f355646a59d8e5ae Mon Sep 17 00:00:00 2001 From: bulby97 Date: Wed, 11 Aug 2021 11:09:30 +0200 Subject: [PATCH 01/17] List roles new design --- .../src/components/Roles/RoleList/RoleRow.js | 66 ++--- .../admin/src/pages/Roles/ListPage/index.js | 230 ++++++++++-------- .../core/admin/admin/src/translations/en.json | 6 + 3 files changed, 174 insertions(+), 128 deletions(-) diff --git a/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js b/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js index 0939aff4a1..2d68241d30 100644 --- a/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js +++ b/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js @@ -1,48 +1,50 @@ -import React from 'react'; +import { Box, Row, Td, Text, Tr, IconButton } from '@strapi/parts'; import PropTypes from 'prop-types'; -import { CustomRow } from '@buffetjs/styles'; -import { IconLinks, Text } from '@buffetjs/core'; +import React from 'react'; import { useIntl } from 'react-intl'; - import RoleDescription from './RoleDescription'; -const RoleRow = ({ role, onClick, links, prefix }) => { +const RoleRow = ({ name, description, usersCount, icons }) => { const { formatMessage } = useIntl(); - const number = role.usersCount; - const text = formatMessage( - { id: `Roles.RoleRow.user-count.${number > 1 ? 'plural' : 'singular'}` }, - { number } + + const usersCountText = formatMessage( + { id: `Roles.RoleRow.user-count.${usersCount > 1 ? 'plural' : 'singular'}` }, + { number: usersCount } ); return ( - - {prefix && {prefix}} - - {role.name} - - - {role.description} - - - {text} - - - - - + + + {name} + + + {description} + + + {usersCountText} + + + + {icons.map((icon, i) => + icon ? ( + + + + ) : null + )} + + + ); }; -RoleRow.defaultProps = { - onClick: null, - prefix: null, -}; +RoleRow.defaultProps = {}; RoleRow.propTypes = { - links: PropTypes.array.isRequired, - onClick: PropTypes.func, - prefix: PropTypes.node, - role: PropTypes.object.isRequired, + name: PropTypes.string.isRequired, + description: PropTypes.string.isRequired, + usersCount: PropTypes.number.isRequired, + icons: PropTypes.array.isRequired, }; export default RoleRow; diff --git a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js index 79786b832a..c2e7cb103b 100644 --- a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js +++ b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js @@ -1,41 +1,50 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { List, Header } from '@buffetjs/custom'; -import { Button } from '@buffetjs/core'; -import { Duplicate, Pencil, Plus } from '@buffetjs/icons'; +import { useQuery, useTracking } from '@strapi/helper-plugin'; +import { AddIcon, DeleteIcon, EditIcon, Duplicate } from '@strapi/icons'; +import { + Button, + ContentLayout, + HeaderLayout, + Table, + TableLabel, + Tbody, + TFooter, + Th, + Thead, + Tr, + VisuallyHidden, +} from '@strapi/parts'; import matchSorter from 'match-sorter'; +import React, { useCallback, useState } from 'react'; import { useIntl } from 'react-intl'; -import { useHistory } from 'react-router-dom'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { ListButton, useTracking, useQuery, useRBAC } from '@strapi/helper-plugin'; -import adminPermissions from '../../../permissions'; +import { useHistory } from 'react-router'; +import { EmptyRole, RoleRow } from '../../../components/Roles'; import PageTitle from '../../../components/SettingsPageTitle'; -import { EmptyRole, RoleListWrapper, RoleRow } from '../../../components/Roles'; -import { useRolesList, useSettingsHeaderSearchContext } from '../../../hooks'; import UpgradePlanModal from '../../../components/UpgradePlanModal'; -import BaselineAlignment from './BaselineAlignment'; +import { useRolesList } from '../../../hooks'; +// import adminPermissions from '../../../permissions'; const RoleListPage = () => { const { formatMessage } = useIntl(); - const { push } = useHistory(); const [isOpen, setIsOpen] = useState(false); const { trackUsage } = useTracking(); const { roles, isLoading } = useRolesList(); - const { toggleHeaderSearch } = useSettingsHeaderSearchContext(); - const { - allowedActions: { canUpdate }, - } = useRBAC(adminPermissions.settings.roles); + // const { toggleHeaderSearch } = useSettingsHeaderSearchContext(); + const { push } = useHistory(); + // const { + // allowedActions: { canUpdate }, + // } = useRBAC(adminPermissions.settings.roles); const query = useQuery(); const _q = decodeURIComponent(query.get('_q') || ''); const results = matchSorter(roles, _q, { keys: ['name', 'description'] }); - useEffect(() => { - toggleHeaderSearch({ id: 'Settings.permissions.menu.link.roles.label' }); + // useEffect(() => { + // toggleHeaderSearch({ id: 'Settings.permissions.menu.link.roles.label' }); - return () => { - toggleHeaderSearch(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + // return () => { + // toggleHeaderSearch(); + // }; + // // eslint-disable-next-line react-hooks/exhaustive-deps + // }, []); const handleGoTo = useCallback( id => { @@ -60,87 +69,116 @@ const RoleListPage = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const headerActions = [ - { - label: formatMessage({ - id: 'Settings.roles.list.button.add', - defaultMessage: 'Add new role', - }), - onClick: handleToggleModalForCreatingRole, - color: 'primary', - type: 'button', - icon: true, - }, - ]; + const rowCount = results.length; + const colCount = results.length ? Object.keys(results[0]).length : 0; - const resultsCount = results.length; + const getIcons = useCallback( + id => [ + { + onClick: handleToggle, + label: formatMessage({ id: 'app.utils.duplicate', defaultMessage: 'Duplicate' }), + icon: , + }, + { + onClick: () => handleGoTo(id), + label: formatMessage({ id: 'app.utils.edit', defaultMessage: 'Edit' }), + icon: , + }, + { + onClick: handleToggle, + label: formatMessage({ id: 'app.utils.delete', defaultMessage: 'Delete' }), + icon: , + }, + ], + [formatMessage, handleToggle, handleGoTo] + ); return ( <> -
- - - 1 ? '.plural' : '.singular'}`, - }, - { number: resultsCount } - )} - items={results} - isLoading={isLoading} - customRowComponent={role => ( - handleGoTo(role.id)} - canUpdate={canUpdate} - links={[ - { - icon: , - onClick: handleToggle, - }, - { - icon: canUpdate ? : null, - onClick: () => { - handleGoTo(role.id); - }, - }, - { - icon: , - onClick: handleToggle, - }, - ]} - role={role} - /> - )} - /> - {!resultsCount && !isLoading && } - - + )} + title={formatMessage({ + id: 'Settings.roles.title', + defaultMessage: 'roles', + })} + subtitle={formatMessage({ + id: 'Settings.roles.list.description', + defaultMessage: 'List of roles', + })} + as="h2" + /> + + }> + {formatMessage({ + id: 'Settings.roles.list.button.add', + defaultMessage: 'Add new role', + })} + + )} + > + + + + + + + + + + {results.map(role => ( + + ))} + +
+ + {formatMessage({ + id: 'Settings.roles.list.header.name', + defaultMessage: 'Name', + })} + + + + {formatMessage({ + id: 'Settings.roles.list.header.description', + defaultMessage: 'Description', + })} + + + + {formatMessage({ + id: 'Settings.roles.list.header.users', + defaultMessage: 'Users', + })} + + + + {formatMessage({ + id: 'Settings.roles.list.header.actions', + defaultMessage: 'Actions', + })} + +
+ {!rowCount && !isLoading && } +
); diff --git a/packages/core/admin/admin/src/translations/en.json b/packages/core/admin/admin/src/translations/en.json index 23ba3901e7..f3f5ee341d 100644 --- a/packages/core/admin/admin/src/translations/en.json +++ b/packages/core/admin/admin/src/translations/en.json @@ -130,6 +130,10 @@ "Settings.roles.list.description": "List of roles", "Settings.roles.list.title.plural": "{number} roles", "Settings.roles.list.title.singular": "{number} role", + "Settings.roles.list.header.name": "Name", + "Settings.roles.list.header.description": "Description", + "Settings.roles.list.header.users": "Users", + "Settings.roles.list.header.actions": "Actions", "Settings.roles.title": "Roles", "Settings.roles.title.singular": "role", "Settings.sso.description": "Configure the settings for the Single Sign-On feature.", @@ -286,6 +290,8 @@ "app.utils.add-filter": "Add filter", "app.utils.defaultMessage": " ", "app.utils.delete": "Delete", + "app.utils.duplicate": "Duplicate", + "app.utils.edit": "Edit", "app.utils.errors.file-too-big.message": "The file is too big", "app.utils.filters": "Filters", "app.utils.placeholder.defaultMessage": " ", From 33c6e8674674cd1a5b3c0e0bd8e1cedd8150b816 Mon Sep 17 00:00:00 2001 From: bulby97 Date: Wed, 11 Aug 2021 14:26:16 +0200 Subject: [PATCH 02/17] Remove commented lines --- .../admin/admin/src/pages/Roles/ListPage/index.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js index c2e7cb103b..76856a9529 100644 --- a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js +++ b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js @@ -21,31 +21,17 @@ import { EmptyRole, RoleRow } from '../../../components/Roles'; import PageTitle from '../../../components/SettingsPageTitle'; import UpgradePlanModal from '../../../components/UpgradePlanModal'; import { useRolesList } from '../../../hooks'; -// import adminPermissions from '../../../permissions'; const RoleListPage = () => { const { formatMessage } = useIntl(); const [isOpen, setIsOpen] = useState(false); const { trackUsage } = useTracking(); const { roles, isLoading } = useRolesList(); - // const { toggleHeaderSearch } = useSettingsHeaderSearchContext(); const { push } = useHistory(); - // const { - // allowedActions: { canUpdate }, - // } = useRBAC(adminPermissions.settings.roles); const query = useQuery(); const _q = decodeURIComponent(query.get('_q') || ''); const results = matchSorter(roles, _q, { keys: ['name', 'description'] }); - // useEffect(() => { - // toggleHeaderSearch({ id: 'Settings.permissions.menu.link.roles.label' }); - - // return () => { - // toggleHeaderSearch(); - // }; - // // eslint-disable-next-line react-hooks/exhaustive-deps - // }, []); - const handleGoTo = useCallback( id => { push(`/settings/roles/${id}`); From d3db779cc4e04313aaabcdda45f0702d9094ff28 Mon Sep 17 00:00:00 2001 From: bulby97 Date: Thu, 12 Aug 2021 09:30:48 +0200 Subject: [PATCH 03/17] Add new design for ee role list --- .../src/components/Roles/RoleList/RoleRow.js | 17 +- .../admin/src/hooks/useRolesList/index.js | 6 +- .../admin/src/pages/Roles/ListPage/index.js | 67 +-- .../ee/admin/pages/Roles/ListPage/RoleRow.js | 99 ---- .../ee/admin/pages/Roles/ListPage/index.js | 422 ++++++++++++------ .../ee/admin/pages/Roles/ListPage/reducer.js | 9 + 6 files changed, 357 insertions(+), 263 deletions(-) delete mode 100644 packages/core/admin/ee/admin/pages/Roles/ListPage/RoleRow.js diff --git a/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js b/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js index 2d68241d30..7d20e7ce3b 100644 --- a/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js +++ b/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js @@ -1,10 +1,10 @@ -import { Box, Row, Td, Text, Tr, IconButton } from '@strapi/parts'; +import { Box, Row, Td, Text, Tr, IconButton, BaseCheckbox } from '@strapi/parts'; import PropTypes from 'prop-types'; import React from 'react'; import { useIntl } from 'react-intl'; import RoleDescription from './RoleDescription'; -const RoleRow = ({ name, description, usersCount, icons }) => { +const RoleRow = ({ onToggle, id, name, description, usersCount, isChecked, icons }) => { const { formatMessage } = useIntl(); const usersCountText = formatMessage( @@ -14,6 +14,11 @@ const RoleRow = ({ name, description, usersCount, icons }) => { return ( + {!!onToggle && ( + + onToggle(id)} value={isChecked} /> + + )} {name} @@ -38,13 +43,19 @@ const RoleRow = ({ name, description, usersCount, icons }) => { ); }; -RoleRow.defaultProps = {}; +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 d3bc0ce504..5ace36a7e8 100644 --- a/packages/core/admin/admin/src/hooks/useRolesList/index.js +++ b/packages/core/admin/admin/src/hooks/useRolesList/index.js @@ -1,4 +1,4 @@ -import { useEffect, useReducer } from 'react'; +import { useEffect, useReducer, useCallback } from 'react'; import { request, useNotification } from '@strapi/helper-plugin'; import { get } from 'lodash'; import init from './init'; @@ -17,7 +17,7 @@ const useRolesList = (shouldFetchData = true) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [shouldFetchData]); - const fetchRolesList = async () => { + const fetchRolesList = useCallback(async () => { try { dispatch({ type: 'GET_DATA', @@ -43,7 +43,7 @@ const useRolesList = (shouldFetchData = true) => { }); } } - }; + }, [toggleNotification]); return { roles, isLoading, getData: fetchRolesList }; }; diff --git a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js index 76856a9529..b7102544e7 100644 --- a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js +++ b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js @@ -22,51 +22,47 @@ import PageTitle from '../../../components/SettingsPageTitle'; import UpgradePlanModal from '../../../components/UpgradePlanModal'; import { useRolesList } from '../../../hooks'; -const RoleListPage = () => { - const { formatMessage } = useIntl(); - const [isOpen, setIsOpen] = useState(false); - const { trackUsage } = useTracking(); +const useResults = () => { const { roles, isLoading } = useRolesList(); - const { push } = useHistory(); + const query = useQuery(); const _q = decodeURIComponent(query.get('_q') || ''); const results = matchSorter(roles, _q, { keys: ['name', 'description'] }); + return { isLoading, results }; +}; + +const useFuncs = () => { + const { formatMessage } = useIntl(); + const [isModalOpen, setIsModalOpen] = useState(false); + const { trackUsage } = useTracking(); + const { push } = useHistory(); + const handleGoTo = useCallback( id => { push(`/settings/roles/${id}`); }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [] + [push] ); - const handleToggle = useCallback(e => { - e.preventDefault(); - e.stopPropagation(); - setIsOpen(prev => !prev); + const handleToggle = useCallback(() => { + setIsModalOpen(prev => !prev); }, []); - const handleToggleModalForCreatingRole = useCallback(e => { - e.preventDefault(); - e.stopPropagation(); + const handleToggleModalForCreatingRole = useCallback(() => { trackUsage('didShowRBACUpgradeModal'); - - setIsOpen(true); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const rowCount = results.length; - const colCount = results.length ? Object.keys(results[0]).length : 0; + setIsModalOpen(true); + }, [trackUsage]); const getIcons = useCallback( - id => [ + role => [ { onClick: handleToggle, label: formatMessage({ id: 'app.utils.duplicate', defaultMessage: 'Duplicate' }), icon: , }, { - onClick: () => handleGoTo(id), + onClick: () => handleGoTo(role.id), label: formatMessage({ id: 'app.utils.edit', defaultMessage: 'Edit' }), icon: , }, @@ -79,6 +75,25 @@ const RoleListPage = () => { [formatMessage, handleToggle, handleGoTo] ); + return { + isModalOpen, + handleToggleModalForCreatingRole, + handleToggle, + getIcons, + }; +}; + +const RoleListPage = () => { + const { formatMessage } = useIntl(); + + const { results, isLoading } = useResults(); + const { isModalOpen, handleToggle, handleToggleModalForCreatingRole, getIcons } = useFuncs(); + + const rowCount = results.length; + const colCount = results.length ? Object.keys(results[0]).length : 0; + + // ! TODO - Add the search input + return ( <> @@ -151,21 +166,21 @@ const RoleListPage = () => { - {results.map(role => ( + {results?.map(role => ( ))} {!rowCount && !isLoading && } - + ); }; diff --git a/packages/core/admin/ee/admin/pages/Roles/ListPage/RoleRow.js b/packages/core/admin/ee/admin/pages/Roles/ListPage/RoleRow.js deleted file mode 100644 index 296ab8ea99..0000000000 --- a/packages/core/admin/ee/admin/pages/Roles/ListPage/RoleRow.js +++ /dev/null @@ -1,99 +0,0 @@ -import React, { useCallback } from 'react'; -import PropTypes from 'prop-types'; -import { useHistory } from 'react-router-dom'; -import { useNotification } from '@strapi/helper-plugin'; -import { Pencil, Duplicate } from '@buffetjs/icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { RoleRow as RoleRowBase } from '../../../../../admin/src/components/Roles'; -import Checkbox from './CustomCheckbox'; - -const RoleRow = ({ - canCreate, - canDelete, - canUpdate, - role, - onRoleToggle, - onRoleDuplicate, - onRoleRemove, - selectedRoles, -}) => { - const { push } = useHistory(); - const toggleNotification = useNotification(); - - const handleRoleSelection = e => { - e.stopPropagation(); - - onRoleToggle(role.id); - }; - - const handleClickDelete = e => { - e.preventDefault(); - e.stopPropagation(); - - if (role.usersCount) { - toggleNotification({ - type: 'info', - message: { id: 'Roles.ListPage.notification.delete-not-allowed' }, - }); - } else { - onRoleRemove(role.id); - } - }; - - const handleGoTo = useCallback(() => { - push(`/settings/roles/${role.id}`); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [role.id]); - - const prefix = canDelete ? ( - selectedRoleId === role.id) !== -1} - onClick={handleRoleSelection} - name="role-checkbox" - /> - ) : null; - - return ( - : null, - onClick: e => { - e.preventDefault(); - e.stopPropagation(); - onRoleDuplicate(role.id); - }, - }, - { - icon: canUpdate ? : null, - onClick: handleGoTo, - }, - { - icon: canDelete ? : null, - onClick: handleClickDelete, - }, - ]} - /> - ); -}; - -RoleRow.defaultProps = { - selectedRoles: [], -}; - -RoleRow.propTypes = { - canCreate: PropTypes.bool.isRequired, - canDelete: PropTypes.bool.isRequired, - canUpdate: PropTypes.bool.isRequired, - onRoleToggle: PropTypes.func.isRequired, - onRoleDuplicate: PropTypes.func.isRequired, - onRoleRemove: PropTypes.func.isRequired, - role: PropTypes.object.isRequired, - selectedRoles: PropTypes.arrayOf(PropTypes.number), -}; - -export default RoleRow; diff --git a/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js b/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js index b898685a9f..0025af4662 100644 --- a/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js +++ b/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js @@ -1,68 +1,75 @@ -import React, { useEffect, useReducer, useRef, useState } from 'react'; -import { useHistory } from 'react-router-dom'; -import { Button } from '@buffetjs/core'; -import { List, Header } from '@buffetjs/custom'; -import { Plus } from '@buffetjs/icons'; -import matchSorter from 'match-sorter'; -import { get } from 'lodash'; import { - useQuery, - ListButton, + LoadingIndicatorPage, PopUpWarning, request, - useRBAC, useNotification, - LoadingIndicatorPage, + useQuery, + useRBAC, } from '@strapi/helper-plugin'; +import { AddIcon, DeleteIcon, Duplicate, EditIcon } from '@strapi/icons'; +import { + Button, + ContentLayout, + HeaderLayout, + Table, + Tbody, + TFooter, + Thead, + Th, + Tr, + TableLabel, + VisuallyHidden, + BaseCheckbox, +} from '@strapi/parts'; +import { get } from 'lodash'; +import matchSorter from 'match-sorter'; +import React, { useCallback, useEffect, useReducer, useState } from 'react'; import { useIntl } from 'react-intl'; -import adminPermissions from '../../../../../admin/src/permissions'; +import { useHistory } from 'react-router-dom'; +import { EmptyRole, RoleRow as BaseRoleRow } from '../../../../../admin/src/components/Roles'; import PageTitle from '../../../../../admin/src/components/SettingsPageTitle'; -import useSettingsHeaderSearchContext from '../../../../../admin/src/hooks/useSettingsHeaderSearchContext'; -import { EmptyRole, RoleListWrapper } from '../../../../../admin/src/components/Roles'; import { useRolesList } from '../../../../../admin/src/hooks'; -import RoleRow from './RoleRow'; -import BaselineAlignment from './BaselineAlignment'; +import adminPermissions from '../../../../../admin/src/permissions'; import reducer, { initialState } from './reducer'; -const RoleListPage = () => { - const toggleNotification = useNotification(); - const [isWarningDeleteAllOpened, setIsWarningDeleteAllOpenend] = useState(false); - const { formatMessage } = useIntl(); - const { push } = useHistory(); - const [{ selectedRoles, showModalConfirmButtonLoading, shouldRefetchData }, dispath] = useReducer( - reducer, - initialState - ); +const useResults = () => { const { isLoading: isLoadingForPermissions, allowedActions: { canCreate, canDelete, canRead, canUpdate }, } = useRBAC(adminPermissions.settings.roles); const { getData, roles, isLoading } = useRolesList(false); - const getDataRef = useRef(getData); - const { toggleHeaderSearch } = useSettingsHeaderSearchContext(); const query = useQuery(); const _q = decodeURIComponent(query.get('_q') || ''); const results = matchSorter(roles, _q, { keys: ['name', 'description'] }); - useEffect(() => { - // Show the search bar only if the user is allowed to read - if (canRead) { - toggleHeaderSearch({ id: 'Settings.permissions.menu.link.roles.label' }); - } - - return () => { - if (canRead) { - toggleHeaderSearch(); - } - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [canRead]); - useEffect(() => { if (!isLoadingForPermissions && canRead) { - getDataRef.current(); + getData(); } - }, [isLoadingForPermissions, canRead]); + }, [isLoadingForPermissions, canRead, getData]); + + return { + isLoadingForPermissions, + canCreate, + canDelete, + canRead, + canUpdate, + isLoading, + getData, + results, + roles, + }; +}; + +const useFuncs = ({ getData, canCreate, canDelete, canUpdate, roles, results }) => { + const { formatMessage } = useIntl(); + const toggleNotification = useNotification(); + const [isWarningDeleteAllOpened, setIsWarningDeleteAllOpenend] = useState(false); + const { push } = useHistory(); + const [ + { selectedRoles, showModalConfirmButtonLoading, shouldRefetchData }, + dispatch, + ] = useReducer(reducer, initialState); const handleClosedModal = () => { if (shouldRefetchData) { @@ -70,14 +77,14 @@ const RoleListPage = () => { } // Empty the selected ids when the modal closes - dispath({ + dispatch({ type: 'RESET_DATA_TO_DELETE', }); }; const handleConfirmDeleteData = async () => { try { - dispath({ + dispatch({ type: 'ON_REMOVE_ROLES', }); const filteredRoles = selectedRoles.filter(currentId => { @@ -103,7 +110,7 @@ const RoleListPage = () => { // Empty the selectedRolesId and set the shouldRefetchData to true so the // list is updated when closing the modal - dispath({ + dispatch({ type: 'ON_REMOVE_ROLES_SUCCEEDED', }); } @@ -128,48 +135,164 @@ const RoleListPage = () => { } }; - const handleDuplicateRole = id => { - push(`/settings/roles/duplicate/${id}`); - }; + const onRoleDuplicate = useCallback( + id => { + push(`/settings/roles/duplicate/${id}`); + }, + [push] + ); const handleNewRoleClick = () => push('/settings/roles/new'); - const handleRemoveRole = roleId => { - dispath({ + const onRoleRemove = useCallback(roleId => { + dispatch({ type: 'SET_ROLE_TO_DELETE', id: roleId, }); handleToggleModal(); - }; + }, []); - const handleRoleToggle = roleId => { - dispath({ + const onRoleToggle = roleId => { + dispatch({ type: 'ON_SELECTION', id: roleId, }); }; + const onAllRolesToggle = () => + dispatch({ + type: 'TOGGLE_ALL', + ids: results.map(r => r.id), + }); + const handleToggleModal = () => setIsWarningDeleteAllOpenend(prev => !prev); - /* eslint-disable indent */ - const headerActions = canCreate - ? [ - { - label: formatMessage({ - id: 'Settings.roles.list.button.add', - defaultMessage: 'Add new role', - }), - onClick: handleNewRoleClick, - color: 'primary', - type: 'button', - icon: true, - }, - ] - : []; - /* eslint-enable indent */ + const handleGoTo = useCallback( + id => { + push(`/settings/roles/${id}`); + }, + [push] + ); - const resultsCount = results.length; + const handleClickDelete = useCallback( + (e, role) => { + e.preventDefault(); + e.stopPropagation(); + + if (role.usersCount) { + toggleNotification({ + type: 'info', + message: { id: 'Roles.ListPage.notification.delete-not-allowed' }, + }); + } else { + onRoleRemove(role.id); + } + }, + [toggleNotification, onRoleRemove] + ); + + const handleClickDuplicate = useCallback( + (e, role) => { + e.preventDefault(); + e.stopPropagation(); + onRoleDuplicate(role.id); + }, + [onRoleDuplicate] + ); + + const getIcons = useCallback( + role => [ + ...(canCreate + ? [ + { + onClick: e => handleClickDuplicate(e, role), + label: formatMessage({ id: 'app.utils.duplicate', defaultMessage: 'Duplicate' }), + icon: , + }, + ] + : []), + ...(canUpdate + ? [ + { + onClick: () => handleGoTo(role.id), + label: formatMessage({ id: 'app.utils.edit', defaultMessage: 'Edit' }), + icon: , + }, + ] + : []), + ...(canDelete + ? [ + { + onClick: e => handleClickDelete(e, role), + label: formatMessage({ id: 'app.utils.delete', defaultMessage: 'Delete' }), + icon: , + }, + ] + : []), + ], + [ + formatMessage, + handleClickDelete, + handleClickDuplicate, + handleGoTo, + canCreate, + canUpdate, + canDelete, + ] + ); + + return { + handleClosedModal, + handleConfirmDeleteData, + handleNewRoleClick, + onRoleToggle, + onAllRolesToggle, + getIcons, + selectedRoles, + isWarningDeleteAllOpened, + showModalConfirmButtonLoading, + handleToggleModal, + }; +}; + +const RoleListPage = () => { + const { formatMessage } = useIntl(); + const { + isLoadingForPermissions, + canCreate, + canRead, + canDelete, + canUpdate, + isLoading, + getData, + results, + roles, + } = useResults(); + + const { + handleClosedModal, + handleConfirmDeleteData, + handleNewRoleClick, + onRoleToggle, + onAllRolesToggle, + getIcons, + selectedRoles, + isWarningDeleteAllOpened, + showModalConfirmButtonLoading, + handleToggleModal, + } = useFuncs({ getData, canCreate, canDelete, canUpdate, roles, results }); + + // ! TODO - Show the search bar only if the user is allowed to read - add the search input + // canRead + + const rowCount = results.length; + const colCount = results.length ? Object.keys(results[0]).length : 0; + + const isAllEntriesIndeterminate = selectedRoles.length + ? selectedRoles.length !== rowCount + : false; + const isAllChecked = selectedRoles.length ? selectedRoles.length === rowCount : false; if (isLoadingForPermissions) { return ; @@ -178,73 +301,108 @@ const RoleListPage = () => { return ( <> -
}> + {formatMessage({ + id: 'Settings.roles.list.button.add', + defaultMessage: 'Add new role', + })} + + ) : null + } + title={formatMessage({ + id: 'Settings.roles.title', + defaultMessage: 'roles', + })} + subtitle={formatMessage({ id: 'Settings.roles.list.description', defaultMessage: 'List of roles', })} - actions={headerActions} - isLoading={isLoading} + as="h2" /> - {canRead && ( - - 1 ? '.plural' : '.singular'}`, - defaultMessage: `{number} ${resultsCount > 1 ? 'roles' : 'role'}`, - }, - { number: resultsCount } - )} - isLoading={isLoading} - /* eslint-disable indent */ - button={ - canDelete - ? { - color: 'delete', - disabled: selectedRoles.length === 0, - label: formatMessage({ id: 'app.utils.delete', defaultMessage: 'Delete' }), - onClick: handleToggleModal, - type: 'button', - } - : null + + }> + {formatMessage({ + id: 'Settings.roles.list.button.add', + defaultMessage: 'Add new role', + })} + + ) : null } - /* eslint-enable indent */ - items={results} - customRowComponent={role => ( - - )} - /> - {!resultsCount && !isLoading && } - {canCreate && ( - - + + {!!onRoleToggle && ( + + )} + + + + + + + + {results?.map(role => ( + selectedRoleId === role.id) !== -1 + } + name={role.name} + description={role.description} + usersCount={role.usersCount} + icons={getIcons(role)} + /> + ))} + +
+ + + + {formatMessage({ + id: 'Settings.roles.list.header.name', + defaultMessage: 'Name', + })} + + + + {formatMessage({ + id: 'Settings.roles.list.header.description', + defaultMessage: 'Description', + })} + + + + {formatMessage({ + id: 'Settings.roles.list.header.users', + defaultMessage: 'Users', + })} + + + + {formatMessage({ + id: 'Settings.roles.list.header.actions', + defaultMessage: 'Actions', + })} + +
+ {!rowCount && !isLoading && } +
)} } 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; From e368c5de072625bf4e766280ef020a4548fe2689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Montlouis-Calixte=20St=C3=A9phane?= Date: Thu, 12 Aug 2021 12:23:48 +0200 Subject: [PATCH 04/17] Update packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js Co-authored-by: Marvin Frachet --- .../core/admin/admin/src/components/Roles/RoleList/RoleRow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js b/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js index 7d20e7ce3b..1ac3533db8 100644 --- a/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js +++ b/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js @@ -32,7 +32,7 @@ const RoleRow = ({ onToggle, id, name, description, usersCount, isChecked, icons {icons.map((icon, i) => icon ? ( - + ) : null From 34879e71a0c0d8ae1a27059f76f95ee25a59c854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Montlouis-Calixte=20St=C3=A9phane?= Date: Thu, 12 Aug 2021 12:23:54 +0200 Subject: [PATCH 05/17] Update packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js Co-authored-by: Marvin Frachet --- .../core/admin/admin/src/components/Roles/RoleList/RoleRow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js b/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js index 1ac3533db8..d8f53c0a66 100644 --- a/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js +++ b/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js @@ -14,7 +14,7 @@ const RoleRow = ({ onToggle, id, name, description, usersCount, isChecked, icons return ( - {!!onToggle && ( + {Boolean(onToggle) && ( onToggle(id)} value={isChecked} /> From 779f991ab5026c3cd3ad5c5aa4822e90ad27d667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Montlouis-Calixte=20St=C3=A9phane?= Date: Thu, 12 Aug 2021 12:48:55 +0200 Subject: [PATCH 06/17] Update packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js Co-authored-by: Marvin Frachet --- .../core/admin/admin/src/components/Roles/RoleList/RoleRow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js b/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js index d8f53c0a66..2457317a78 100644 --- a/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js +++ b/packages/core/admin/admin/src/components/Roles/RoleList/RoleRow.js @@ -16,7 +16,7 @@ const RoleRow = ({ onToggle, id, name, description, usersCount, isChecked, icons {Boolean(onToggle) && ( - onToggle(id)} value={isChecked} /> + onToggle(id)} value={isChecked} aria-label={`Select ${name} for bulk actions`} /> )} From b90d2389838a4ad7692c258dabec69204a81dfb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Montlouis-Calixte=20St=C3=A9phane?= Date: Thu, 12 Aug 2021 12:57:30 +0200 Subject: [PATCH 07/17] Update packages/core/admin/admin/src/pages/Roles/ListPage/index.js Co-authored-by: Marvin Frachet --- packages/core/admin/admin/src/pages/Roles/ListPage/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js index b7102544e7..926624318c 100644 --- a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js +++ b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js @@ -114,7 +114,6 @@ const RoleListPage = () => { id: 'Settings.roles.list.description', defaultMessage: 'List of roles', })} - as="h2" /> Date: Thu, 12 Aug 2021 13:00:03 +0200 Subject: [PATCH 08/17] Update packages/core/admin/admin/src/pages/Roles/ListPage/index.js Co-authored-by: Marvin Frachet --- packages/core/admin/admin/src/pages/Roles/ListPage/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js index 926624318c..46cc3988b2 100644 --- a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js +++ b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js @@ -89,7 +89,7 @@ const RoleListPage = () => { const { results, isLoading } = useResults(); const { isModalOpen, handleToggle, handleToggleModalForCreatingRole, getIcons } = useFuncs(); - const rowCount = results.length; + const rowCount = results.length + 1; const colCount = results.length ? Object.keys(results[0]).length : 0; // ! TODO - Add the search input From 724c5d27e80192a50ff70768c1d2a6ca5c0d0ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Montlouis-Calixte=20St=C3=A9phane?= Date: Thu, 12 Aug 2021 13:00:59 +0200 Subject: [PATCH 09/17] Update packages/core/admin/admin/src/pages/Roles/ListPage/index.js Co-authored-by: Marvin Frachet --- packages/core/admin/admin/src/pages/Roles/ListPage/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js index 46cc3988b2..9e1f605012 100644 --- a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js +++ b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js @@ -90,7 +90,7 @@ const RoleListPage = () => { const { isModalOpen, handleToggle, handleToggleModalForCreatingRole, getIcons } = useFuncs(); const rowCount = results.length + 1; - const colCount = results.length ? Object.keys(results[0]).length : 0; + const colCount = 5 // ! TODO - Add the search input From af6553acd9c6c083426362cf1ff736fb97233024 Mon Sep 17 00:00:00 2001 From: bulby97 Date: Thu, 12 Aug 2021 13:34:35 +0200 Subject: [PATCH 10/17] Review fixes --- .../admin/src/pages/Roles/ListPage/index.js | 31 +++++++++++-------- .../ee/admin/pages/Roles/CreatePage/index.js | 18 +++++++++-- .../ee/admin/pages/Roles/ListPage/index.js | 22 ++++++------- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js index 9e1f605012..2a834a5251 100644 --- a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js +++ b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js @@ -22,17 +22,17 @@ import PageTitle from '../../../components/SettingsPageTitle'; import UpgradePlanModal from '../../../components/UpgradePlanModal'; import { useRolesList } from '../../../hooks'; -const useResults = () => { +const useSortedRoles = () => { const { roles, isLoading } = useRolesList(); const query = useQuery(); const _q = decodeURIComponent(query.get('_q') || ''); - const results = matchSorter(roles, _q, { keys: ['name', 'description'] }); + const sortedRoles = matchSorter(roles, _q, { keys: ['name', 'description'] }); - return { isLoading, results }; + return { isLoading, sortedRoles }; }; -const useFuncs = () => { +const useRoleActions = () => { const { formatMessage } = useIntl(); const [isModalOpen, setIsModalOpen] = useState(false); const { trackUsage } = useTracking(); @@ -86,11 +86,16 @@ const useFuncs = () => { const RoleListPage = () => { const { formatMessage } = useIntl(); - const { results, isLoading } = useResults(); - const { isModalOpen, handleToggle, handleToggleModalForCreatingRole, getIcons } = useFuncs(); + const { sortedRoles, isLoading } = useSortedRoles(); + const { + isModalOpen, + handleToggle, + handleToggleModalForCreatingRole, + getIcons, + } = useRoleActions(); - const rowCount = results.length + 1; - const colCount = 5 + const rowCount = sortedRoles.length + 1; + const colCount = 5; // ! TODO - Add the search input @@ -98,14 +103,14 @@ const RoleListPage = () => { <> }> {formatMessage({ id: 'Settings.roles.list.button.add', defaultMessage: 'Add new role', })} - )} + } title={formatMessage({ id: 'Settings.roles.title', defaultMessage: 'roles', @@ -119,14 +124,14 @@ const RoleListPage = () => {
}> {formatMessage({ id: 'Settings.roles.list.button.add', defaultMessage: 'Add new role', })} - )} + } > @@ -165,7 +170,7 @@ const RoleListPage = () => { - {results?.map(role => ( + {sortedRoles?.map(role => ( { > {({ handleSubmit, values, errors, handleReset, handleChange, handleBlur }) => (
- + <> + }>Add an entry} + secondaryAction={( + + )} + title="Other CT" + subtitle="36 entries found" + as="h2" + />
{ /> )} - + )} diff --git a/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js b/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js index 0025af4662..1e5a1610e8 100644 --- a/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js +++ b/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js @@ -32,7 +32,7 @@ import { useRolesList } from '../../../../../admin/src/hooks'; import adminPermissions from '../../../../../admin/src/permissions'; import reducer, { initialState } from './reducer'; -const useResults = () => { +const useSortedRoles = () => { const { isLoading: isLoadingForPermissions, allowedActions: { canCreate, canDelete, canRead, canUpdate }, @@ -40,7 +40,7 @@ const useResults = () => { const { getData, roles, isLoading } = useRolesList(false); const query = useQuery(); const _q = decodeURIComponent(query.get('_q') || ''); - const results = matchSorter(roles, _q, { keys: ['name', 'description'] }); + const sortedRoles = matchSorter(roles, _q, { keys: ['name', 'description'] }); useEffect(() => { if (!isLoadingForPermissions && canRead) { @@ -56,12 +56,12 @@ const useResults = () => { canUpdate, isLoading, getData, - results, + sortedRoles, roles, }; }; -const useFuncs = ({ getData, canCreate, canDelete, canUpdate, roles, results }) => { +const useRoleActions = ({ getData, canCreate, canDelete, canUpdate, roles, sortedRoles }) => { const { formatMessage } = useIntl(); const toggleNotification = useNotification(); const [isWarningDeleteAllOpened, setIsWarningDeleteAllOpenend] = useState(false); @@ -163,7 +163,7 @@ const useFuncs = ({ getData, canCreate, canDelete, canUpdate, roles, results }) const onAllRolesToggle = () => dispatch({ type: 'TOGGLE_ALL', - ids: results.map(r => r.id), + ids: sortedRoles.map(r => r.id), }); const handleToggleModal = () => setIsWarningDeleteAllOpenend(prev => !prev); @@ -266,9 +266,9 @@ const RoleListPage = () => { canUpdate, isLoading, getData, - results, + sortedRoles, roles, - } = useResults(); + } = useSortedRoles(); const { handleClosedModal, @@ -281,13 +281,13 @@ const RoleListPage = () => { isWarningDeleteAllOpened, showModalConfirmButtonLoading, handleToggleModal, - } = useFuncs({ getData, canCreate, canDelete, canUpdate, roles, results }); + } = useRoleActions({ getData, canCreate, canDelete, canUpdate, roles, sortedRoles }); // ! TODO - Show the search bar only if the user is allowed to read - add the search input // canRead - const rowCount = results.length; - const colCount = results.length ? Object.keys(results[0]).length : 0; + const rowCount = sortedRoles.length; + const colCount = sortedRoles.length ? Object.keys(sortedRoles[0]).length : 0; const isAllEntriesIndeterminate = selectedRoles.length ? selectedRoles.length !== rowCount @@ -385,7 +385,7 @@ const RoleListPage = () => {
- {results?.map(role => ( + {sortedRoles?.map(role => ( Date: Thu, 12 Aug 2021 14:12:08 +0200 Subject: [PATCH 11/17] Add Main tag --- .../admin/admin/src/pages/Roles/ListPage/index.js | 14 ++++++++------ .../admin/ee/admin/pages/Roles/ListPage/index.js | 6 ++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js index 2a834a5251..bdf4743e71 100644 --- a/packages/core/admin/admin/src/pages/Roles/ListPage/index.js +++ b/packages/core/admin/admin/src/pages/Roles/ListPage/index.js @@ -12,6 +12,7 @@ import { Thead, Tr, VisuallyHidden, + Main, } from '@strapi/parts'; import matchSorter from 'match-sorter'; import React, { useCallback, useState } from 'react'; @@ -100,17 +101,18 @@ const RoleListPage = () => { // ! TODO - Add the search input return ( - <> +
}> {formatMessage({ id: 'Settings.roles.list.button.add', defaultMessage: 'Add new role', })} - } + )} title={formatMessage({ id: 'Settings.roles.title', defaultMessage: 'roles', @@ -124,14 +126,14 @@ const RoleListPage = () => {
}> {formatMessage({ id: 'Settings.roles.list.button.add', defaultMessage: 'Add new role', })} - } + )} > @@ -185,7 +187,7 @@ const RoleListPage = () => { {!rowCount && !isLoading && } - + ); }; diff --git a/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js b/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js index 1e5a1610e8..e63736d0e1 100644 --- a/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js +++ b/packages/core/admin/ee/admin/pages/Roles/ListPage/index.js @@ -20,6 +20,7 @@ import { TableLabel, VisuallyHidden, BaseCheckbox, + Main, } from '@strapi/parts'; import { get } from 'lodash'; import matchSorter from 'match-sorter'; @@ -299,9 +300,10 @@ const RoleListPage = () => { } return ( - <> +
}> @@ -411,7 +413,7 @@ const RoleListPage = () => { toggleModal={handleToggleModal} isConfirmButtonLoading={showModalConfirmButtonLoading} /> - +
); }; From 304c98b41e9ab202c3ff449f940f11f49b041d26 Mon Sep 17 00:00:00 2001 From: mfrachet Date: Tue, 10 Aug 2021 15:27:57 +0200 Subject: [PATCH 12/17] Preparing the i18n settings list view --- .eslintrc.front.js | 1 + .../core/admin/admin/src/pages/Admin/index.js | 1 - packages/core/admin/package.json | 8 +- .../email/admin/src/pages/Settings/index.js | 1 - .../src/hooks/useFocusWhenNavigate/index.js | 19 ++ packages/core/helper-plugin/lib/src/index.js | 1 + .../src/components/LocaleList/LocaleTable.js | 176 ++++++++++++++++++ .../admin/src/components/LocaleList/index.js | 116 ++++++------ .../admin/src/components/LocaleRow/index.js | 77 -------- .../pages/SettingsPage/LocaleSettingsPage.js | 51 +---- .../i18n/admin/src/translations/en.json | 3 + yarn.lock | 18 +- 12 files changed, 277 insertions(+), 195 deletions(-) create mode 100644 packages/core/helper-plugin/lib/src/hooks/useFocusWhenNavigate/index.js create mode 100644 packages/plugins/i18n/admin/src/components/LocaleList/LocaleTable.js delete mode 100644 packages/plugins/i18n/admin/src/components/LocaleRow/index.js diff --git a/.eslintrc.front.js b/.eslintrc.front.js index d0a6fb5f83..34565411f9 100644 --- a/.eslintrc.front.js +++ b/.eslintrc.front.js @@ -118,5 +118,6 @@ module.exports = { 'react/state-in-constructor': 0, 'react/static-property-placement': 0, 'react/display-name': 0, + 'react/jsx-wrap-multilines': 0, }, }; diff --git a/packages/core/admin/admin/src/pages/Admin/index.js b/packages/core/admin/admin/src/pages/Admin/index.js index 8f9d8a8d5b..5bee443605 100644 --- a/packages/core/admin/admin/src/pages/Admin/index.js +++ b/packages/core/admin/admin/src/pages/Admin/index.js @@ -81,7 +81,6 @@ const Admin = () => { { + useEffect(() => { + const mainElement = document.querySelector(selector); + + if (mainElement) { + mainElement.focus(); + window.scrollTo({ top: 0 }); + } else { + console.warn( + `[useFocusWhenNavigate] The page does not contain the selector "${selector}" and can't be focused.` + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, dependencies); +}; + +export default useFocusWhenNavigate; diff --git a/packages/core/helper-plugin/lib/src/index.js b/packages/core/helper-plugin/lib/src/index.js index b6f1cbf329..86c5324316 100644 --- a/packages/core/helper-plugin/lib/src/index.js +++ b/packages/core/helper-plugin/lib/src/index.js @@ -129,6 +129,7 @@ export { default as useAutoReloadOverlayBlocker } from './hooks/useAutoReloadOve export { default as useRBACProvider } from './hooks/useRBACProvider'; export { default as useRBAC } from './hooks/useRBAC'; export { default as usePersistentState } from './hooks/usePersistentState'; +export { default as useFocusWhenNavigate } from './hooks/useFocusWhenNavigate'; // Providers export { default as LibraryProvider } from './providers/LibraryProvider'; diff --git a/packages/plugins/i18n/admin/src/components/LocaleList/LocaleTable.js b/packages/plugins/i18n/admin/src/components/LocaleList/LocaleTable.js new file mode 100644 index 0000000000..78537d8d6c --- /dev/null +++ b/packages/plugins/i18n/admin/src/components/LocaleList/LocaleTable.js @@ -0,0 +1,176 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { + Table, + Thead, + Tr, + Th, + Td, + Tbody, + Text, + TableLabel, + VisuallyHidden, + Stack, + IconButton, +} from '@strapi/parts'; +import EditIcon from '@strapi/icons/EditIcon'; +import DeleteIcon from '@strapi/icons/DeleteIcon'; +import DropdownIcon from '@strapi/icons/FilterDropdown'; +import styled from 'styled-components'; +import { useIntl } from 'react-intl'; +import { getTrad } from '../../utils'; + +const ActionIconWrapper = styled.span` + svg { + transform: ${({ reverse }) => (reverse ? `rotateX(180deg)` : undefined)}; + } +`; + +const SortingKeys = { + id: 'id', + name: 'name', + default: 'isDefault', +}; + +const SortingOrder = { + asc: 'asc', + desc: 'desc', +}; + +const LocaleTable = ({ locales, onDeleteLocale, onEditLocale }) => { + const { formatMessage } = useIntl(); + const [sortingKey, setSortingKey] = useState(SortingKeys.id); + const [sortingOrder, setSortingOrder] = useState(SortingOrder.asc); + + const sortedLocales = [...locales].sort((a, b) => { + if (sortingOrder === SortingOrder.desc) { + return a[sortingKey] < b[sortingKey]; + } + + return a[sortingKey] > b[sortingKey]; + }); + + const handleSorting = key => { + if (key === sortingKey) { + setSortingOrder(prev => (prev === SortingOrder.asc ? SortingOrder.desc : SortingOrder.asc)); + } else { + setSortingKey(key); + setSortingOrder(SortingOrder.asc); + } + }; + + const isReversedArrow = key => sortingKey === key && sortingOrder === SortingOrder.desc; + + return ( +
+ + + + + + + + + + {sortedLocales.map(locale => ( + + + + + + + ))} + +
+ + + } + noBorder + onClick={() => handleSorting(SortingKeys.id)} + /> + } + > + ID + + + + } + noBorder + onClick={() => handleSorting(SortingKeys.name)} + /> + } + > + Display name + + + + } + noBorder + onClick={() => handleSorting(SortingKeys.default)} + /> + } + > + Default + + Actions +
+ {locale.id} + + {locale.name} + + + {locale.isDefault + ? formatMessage({ id: getTrad('Settings.locales.row.default-locale') }) + : null} + + + + {onEditLocale && ( + onEditLocale(locale)} + label={formatMessage({ id: getTrad('Settings.list.actions.edit') })} + icon={} + noBorder + /> + )} + {onDeleteLocale && !locale.isDefault && ( + onDeleteLocale(locale)} + label={formatMessage({ id: getTrad('Settings.list.actions.delete') })} + icon={} + noBorder + /> + )} + +
+ ); +}; + +LocaleTable.defaultProps = { + locales: [], + onDeleteLocale: undefined, + onEditLocale: undefined, +}; + +LocaleTable.propTypes = { + locales: PropTypes.array, + onDeleteLocale: PropTypes.func, + onEditLocale: PropTypes.func, +}; + +export default LocaleTable; diff --git a/packages/plugins/i18n/admin/src/components/LocaleList/index.js b/packages/plugins/i18n/admin/src/components/LocaleList/index.js index 8226a56b92..4c6c02848a 100644 --- a/packages/plugins/i18n/admin/src/components/LocaleList/index.js +++ b/packages/plugins/i18n/admin/src/components/LocaleList/index.js @@ -1,89 +1,85 @@ import React, { useState } from 'react'; import { useIntl } from 'react-intl'; -import { EmptyState, ListButton } from '@strapi/helper-plugin'; -import { List } from '@buffetjs/custom'; -import { Button } from '@buffetjs/core'; -import { Plus } from '@buffetjs/icons'; import PropTypes from 'prop-types'; +import { ContentLayout, EmptyStateLayout, Button, Main, HeaderLayout } from '@strapi/parts'; +import { useFocusWhenNavigate } from '@strapi/helper-plugin'; +import AddIcon from '@strapi/icons/AddIcon'; +import EmptyStateDocument from '@strapi/icons/EmptyStateDocument'; import useLocales from '../../hooks/useLocales'; -import LocaleRow from '../LocaleRow'; import { getTrad } from '../../utils'; import ModalEdit from '../ModalEdit'; import ModalDelete from '../ModalDelete'; import ModalCreate from '../ModalCreate'; +import LocaleTable from './LocaleTable'; const LocaleList = ({ canUpdateLocale, canDeleteLocale, onToggleCreateModal, isCreating }) => { const [localeToDelete, setLocaleToDelete] = useState(); const [localeToEdit, setLocaleToEdit] = useState(); - const { locales, isLoading } = useLocales(); + const { locales } = useLocales(); const { formatMessage } = useIntl(); + useFocusWhenNavigate(); + // Delete actions const closeModalToDelete = () => setLocaleToDelete(undefined); const handleDeleteLocale = canDeleteLocale ? setLocaleToDelete : undefined; // Edit actions - const closeModalToEdit = () => { - setLocaleToEdit(undefined); - }; + const closeModalToEdit = () => setLocaleToEdit(undefined); const handleEditLocale = canUpdateLocale ? setLocaleToEdit : undefined; - if (isLoading || (locales && locales.length > 0)) { - const listTitle = isLoading - ? null - : formatMessage( - { - id: getTrad( - `Settings.locales.list.title${locales.length > 1 ? '.plural' : '.singular'}` - ), - }, - { number: locales.length } - ); - - return ( - <> - ( - - )} - /> - - - - - - ); - } - return ( - <> - + } onClick={onToggleCreateModal}> + {formatMessage({ id: getTrad('Settings.list.actions.add') })} + + } + title={formatMessage({ id: getTrad('plugin.name') })} + subtitle={formatMessage({ id: getTrad('Settings.list.description') })} /> + + {locales?.length === 0 ? ( + <> + + } + content={formatMessage({ id: getTrad('Settings.list.empty.title') })} + action={ + onToggleCreateModal ? ( + + ) : null + } + /> + + + ) : null} - {onToggleCreateModal && ( - - - ) : null - } - /> - - - ) : null} - - {locales.length > 0 ? ( + {locales?.length > 0 ? ( - ) : null} + ) : ( + + } + content={formatMessage({ id: getTrad('Settings.list.empty.title') })} + action={ + onToggleCreateModal ? ( + + ) : null + } + /> + + )}
Date: Mon, 16 Aug 2021 16:41:04 +0200 Subject: [PATCH 17/17] Update packages/core/admin/ee/admin/pages/Roles/CreatePage/index.js Co-authored-by: Marvin Frachet --- packages/core/admin/ee/admin/pages/Roles/CreatePage/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/ee/admin/pages/Roles/CreatePage/index.js b/packages/core/admin/ee/admin/pages/Roles/CreatePage/index.js index ef0953f95d..89e64a9a17 100644 --- a/packages/core/admin/ee/admin/pages/Roles/CreatePage/index.js +++ b/packages/core/admin/ee/admin/pages/Roles/CreatePage/index.js @@ -153,7 +153,7 @@ const CreatePage = () => { )} title="Other CT" subtitle="36 entries found" - as="h2" + as="h1" />