mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 02:44:55 +00:00
Merge pull request #10893 from strapi/ds-migration/roles-list-header
[v4] migrate roles page header
This commit is contained in:
commit
13fb791c67
@ -1,195 +1,62 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { List, Header } from '@buffetjs/custom';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import React from 'react';
|
||||
import { Button, HeaderLayout, Layout, Main } from '@strapi/parts';
|
||||
import { AddIcon } from '@strapi/icons';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { useTracking, SettingsPageTitle, CheckPermissions } from '@strapi/helper-plugin';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import {
|
||||
useRBAC,
|
||||
PopUpWarning,
|
||||
request,
|
||||
useTracking,
|
||||
useNotification,
|
||||
useOverlayBlocker,
|
||||
} from '@strapi/helper-plugin';
|
||||
|
||||
import permissions from '../../../permissions';
|
||||
import { EmptyRole, RoleListWrapper, RoleRow } from '../../../components/Roles';
|
||||
import { useRolesList } from '../../../hooks';
|
||||
import BaselineAlignment from './BaselineAlignment';
|
||||
import pluginId from '../../../pluginId';
|
||||
import { getTrad } from '../../../utils';
|
||||
import pluginId from '../../../pluginId';
|
||||
import permissions from '../../../permissions';
|
||||
|
||||
const RoleListPage = () => {
|
||||
const { formatMessage } = useIntl();
|
||||
const { trackUsage } = useTracking();
|
||||
const { formatMessage } = useIntl();
|
||||
const { push } = useHistory();
|
||||
const toggleNotification = useNotification();
|
||||
const { lockApp, unlockApp } = useOverlayBlocker();
|
||||
const [modalToDelete, setModalDelete] = useState();
|
||||
const [shouldRefetchData, setShouldRefetchData] = useState(false);
|
||||
const [showModalConfirmButtonLoading, setModalButtonLoading] = useState(false);
|
||||
|
||||
const updatePermissions = useMemo(() => {
|
||||
return {
|
||||
update: permissions.updateRole,
|
||||
create: permissions.createRole,
|
||||
delete: permissions.deleteRole,
|
||||
read: permissions.readRoles,
|
||||
};
|
||||
}, []);
|
||||
const {
|
||||
isLoading: isLoadingForPermissions,
|
||||
allowedActions: { canCreate, canUpdate, canDelete, canRead },
|
||||
} = useRBAC(updatePermissions);
|
||||
const shouldFetchData = !isLoadingForPermissions && canRead;
|
||||
|
||||
const { roles, getData, isLoading } = useRolesList(shouldFetchData);
|
||||
|
||||
const handleGoTo = id => {
|
||||
if (canUpdate) {
|
||||
push(`/settings/${pluginId}/roles/${id}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
lockApp();
|
||||
|
||||
setModalButtonLoading(true);
|
||||
|
||||
Promise.resolve(
|
||||
request(`/${pluginId}/roles/${modalToDelete}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
setShouldRefetchData(true);
|
||||
toggleNotification({
|
||||
type: 'success',
|
||||
message: { id: getTrad('Settings.roles.deleted') },
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
toggleNotification({
|
||||
type: 'warning',
|
||||
message: { id: 'notification.error' },
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setModalDelete(null);
|
||||
unlockApp();
|
||||
});
|
||||
};
|
||||
|
||||
const handleClosedModalDelete = () => {
|
||||
if (shouldRefetchData) {
|
||||
getData();
|
||||
}
|
||||
setModalButtonLoading(false);
|
||||
setShouldRefetchData(false);
|
||||
};
|
||||
|
||||
const handleNewRoleClick = () => {
|
||||
trackUsage('willCreateRole');
|
||||
push(`/settings/${pluginId}/roles/new`);
|
||||
};
|
||||
|
||||
/* eslint-disable indent */
|
||||
const headerActions = canCreate
|
||||
? [
|
||||
{
|
||||
label: formatMessage({
|
||||
id: getTrad('List.button.roles'),
|
||||
defaultMessage: 'Add new role',
|
||||
}),
|
||||
onClick: handleNewRoleClick,
|
||||
color: 'primary',
|
||||
type: 'button',
|
||||
icon: true,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
/* eslint-enable indent */
|
||||
|
||||
const checkCanDeleteRole = useCallback(
|
||||
role => {
|
||||
return canDelete && !['public', 'authenticated'].includes(role.type);
|
||||
},
|
||||
[canDelete]
|
||||
);
|
||||
|
||||
const getLinks = role => {
|
||||
const links = [];
|
||||
|
||||
if (canUpdate) {
|
||||
links.push({
|
||||
icon: <FontAwesomeIcon icon="pencil-alt" />,
|
||||
onClick: () => handleGoTo(role.id),
|
||||
});
|
||||
}
|
||||
if (checkCanDeleteRole(role)) {
|
||||
links.push({
|
||||
icon: <FontAwesomeIcon icon="trash-alt" />,
|
||||
onClick: e => {
|
||||
e.preventDefault();
|
||||
setModalDelete(role.id);
|
||||
e.stopPropagation();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return links;
|
||||
};
|
||||
const pageTitle = formatMessage({
|
||||
id: getTrad('HeaderNav.link.roles'),
|
||||
defaultMessage: 'Roles',
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet title={formatMessage({ id: getTrad('page.title') })} />
|
||||
|
||||
<Header
|
||||
icon
|
||||
title={{
|
||||
label: formatMessage({
|
||||
id: 'Settings.roles.title',
|
||||
defaultMessage: 'Roles & Permissions',
|
||||
}),
|
||||
}}
|
||||
content={formatMessage({
|
||||
id: 'Settings.roles.list.description',
|
||||
defaultMessage: 'Define the roles and permissions for your users.',
|
||||
<Layout>
|
||||
<SettingsPageTitle name={pageTitle} />
|
||||
<Main
|
||||
labelledBy={formatMessage({
|
||||
id: getTrad('HeaderNav.link.roles'),
|
||||
defaultMessage: 'Roles',
|
||||
})}
|
||||
actions={headerActions}
|
||||
// Show a loader in the header while requesting data
|
||||
isLoading={isLoading || isLoadingForPermissions}
|
||||
/>
|
||||
|
||||
<BaselineAlignment />
|
||||
{canRead && (
|
||||
<RoleListWrapper>
|
||||
<List
|
||||
title={formatMessage(
|
||||
{
|
||||
id: `Settings.roles.list.title${roles.length > 1 ? '.plural' : '.singular'}`,
|
||||
},
|
||||
{ number: roles.length }
|
||||
)}
|
||||
items={roles}
|
||||
isLoading={isLoading || isLoadingForPermissions}
|
||||
customRowComponent={role => (
|
||||
<RoleRow onClick={() => handleGoTo(role.id)} links={getLinks(role)} role={role} />
|
||||
)}
|
||||
/>
|
||||
{!roles && !isLoading && !isLoadingForPermissions && <EmptyRole />}
|
||||
<PopUpWarning
|
||||
isOpen={Boolean(modalToDelete)}
|
||||
onConfirm={handleDelete}
|
||||
onClosed={handleClosedModalDelete}
|
||||
toggleModal={() => setModalDelete(null)}
|
||||
isConfirmButtonLoading={showModalConfirmButtonLoading}
|
||||
/>
|
||||
</RoleListWrapper>
|
||||
)}
|
||||
</>
|
||||
>
|
||||
<HeaderLayout
|
||||
as="h1"
|
||||
id="roles"
|
||||
title={formatMessage({
|
||||
id: 'Settings.roles.title',
|
||||
defaultMessage: 'Roles',
|
||||
})}
|
||||
subtitle={formatMessage({
|
||||
id: 'Settings.roles.list.description',
|
||||
defaultMessage: 'List of roles',
|
||||
})}
|
||||
primaryAction={
|
||||
<CheckPermissions permissions={permissions.createRole}>
|
||||
<Button onClick={handleNewRoleClick} startIcon={<AddIcon />}>
|
||||
{formatMessage({
|
||||
id: getTrad('List.button.roles'),
|
||||
defaultMessage: 'Add new role',
|
||||
})}
|
||||
</Button>
|
||||
</CheckPermissions>
|
||||
}
|
||||
/>
|
||||
</Main>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1,160 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { ThemeProvider, lightTheme } from '@strapi/parts';
|
||||
|
||||
import RoleListPage from '../index';
|
||||
|
||||
jest.mock('@strapi/helper-plugin', () => ({
|
||||
...jest.requireActual('@strapi/helper-plugin'),
|
||||
useTracking: jest.fn(() => ({ trackUsage: jest.fn() })),
|
||||
useNotification: jest.fn(),
|
||||
CheckPermissions: jest.fn(() => <div />),
|
||||
}));
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useHistory: () => ({
|
||||
push: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
const App = (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{ en: {} }} textComponent="span">
|
||||
<RoleListPage />
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
describe('Admin | containers | RoleListPage', () => {
|
||||
it('renders and matches the snapshot', () => {
|
||||
const {
|
||||
container: { firstChild },
|
||||
} = render(App);
|
||||
|
||||
expect(firstChild).toMatchInlineSnapshot(`
|
||||
.c7 {
|
||||
font-weight: 600;
|
||||
font-size: 2rem;
|
||||
line-height: 1.25;
|
||||
color: #32324d;
|
||||
}
|
||||
|
||||
.c8 {
|
||||
font-weight: 400;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.43;
|
||||
color: #666687;
|
||||
}
|
||||
|
||||
.c9 {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
padding-bottom: 56px;
|
||||
}
|
||||
|
||||
.c4 {
|
||||
background: #f6f6f9;
|
||||
padding-top: 56px;
|
||||
padding-right: 56px;
|
||||
padding-bottom: 56px;
|
||||
padding-left: 56px;
|
||||
}
|
||||
|
||||
.c5 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: row;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-webkit-box-pack: justify;
|
||||
-webkit-justify-content: space-between;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
-webkit-align-items: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.c6 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: row;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-webkit-align-items: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.c3 {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
<div
|
||||
class="c0"
|
||||
>
|
||||
<div
|
||||
class="c1 c2"
|
||||
>
|
||||
<main
|
||||
aria-labelledby="Roles"
|
||||
class="c3"
|
||||
id="main-content"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
style="height: 0px;"
|
||||
>
|
||||
<div
|
||||
class="c4"
|
||||
data-strapi-header="true"
|
||||
>
|
||||
<div
|
||||
class="c5"
|
||||
>
|
||||
<div
|
||||
class="c6"
|
||||
>
|
||||
<h1
|
||||
class="c7"
|
||||
id="roles"
|
||||
>
|
||||
Roles
|
||||
</h1>
|
||||
</div>
|
||||
<div />
|
||||
</div>
|
||||
<p
|
||||
class="c8 c9"
|
||||
>
|
||||
List of roles
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user