Skip to content

Commit 3846412

Browse files
committed
Properly handle endpoint errors
1 parent 0e53469 commit 3846412

File tree

2 files changed

+155
-153
lines changed

2 files changed

+155
-153
lines changed

packages/volto/src/components/manage/Controlpanels/BlockType.tsx

Lines changed: 89 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@ import { getParentUrl, flattenToAppURL } from '@plone/volto/helpers/Url/Url';
55
import debounce from 'lodash/debounce';
66
import { useClient } from '@plone/volto/hooks';
77
import config from '@plone/volto/registry';
8-
import { useState, useEffect } from 'react';
8+
import { useEffect } from 'react';
99
import { createPortal } from 'react-dom';
1010
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
1111
import { useDispatch, useSelector } from 'react-redux';
1212
import { Link, useParams } from 'react-router-dom';
13-
import Unauthorized from '@plone/volto/components/theme/Unauthorized/Unauthorized';
14-
import { listRoles } from '@plone/volto/actions/roles/roles';
13+
import Error from '@plone/volto/components/theme/Error/Error';
1514

1615
import backSVG from '@plone/volto/icons/back.svg';
1716
import searchSVG from '@plone/volto/icons/zoom.svg';
@@ -35,11 +34,9 @@ type RouteProps = {
3534

3635
const BlockTypeControlpanel = (props: RouteProps) => {
3736
const { location } = props;
38-
const [isUserManager, setIsUserManager] = useState(false);
3937
const params = useParams<{ id: string }>();
4038
const id = params.id;
4139
const intl = useIntl();
42-
const roles = useSelector((state) => state.roles.roles);
4340
const blockTypes = useSelector((state) => state.blockTypes);
4441
const isClient = useClient();
4542
const dispatch = useDispatch();
@@ -48,95 +45,102 @@ const BlockTypeControlpanel = (props: RouteProps) => {
4845
config.blocks.blocksConfig[id as keyof typeof config.blocks.blocksConfig];
4946

5047
useEffect(() => {
51-
dispatch(listRoles());
52-
}, []);
53-
54-
useEffect(() => {
55-
if (roles.length > 0 && roles.map((role) => role.id === 'Manager')) {
56-
setIsUserManager(true);
57-
dispatch(getBlockTypes(id));
58-
}
59-
}, [roles, id]);
48+
dispatch(getBlockTypes(id));
49+
}, [dispatch, id]);
6050

6151
const onChangeSearch = (value: string) => {
6252
dispatch(getBlockTypes(id, value));
6353
};
6454

6555
const debouncedSearch = debounce(onChangeSearch, 600);
6656

67-
return isUserManager ? (
68-
<div id="page-block_type" className="ui container controlpanel-block_type">
69-
<h1>
70-
<FormattedMessage
71-
id="block-type"
72-
defaultMessage="{title}"
73-
values={{ title: block.title }}
74-
/>
75-
</h1>
76-
<form className="search" onSubmit={(e) => e.preventDefault()}>
77-
<input
78-
type="text"
79-
placeholder="Search"
80-
onChange={(e) => debouncedSearch(e.target.value)}
81-
/>
82-
<Icon
83-
name={searchSVG}
84-
size="16px"
85-
title={intl.formatMessage(messages.search)}
86-
/>
87-
</form>
88-
{blockTypes.items?.length > 0 ? (
89-
<table className="table">
90-
<thead className="table-header">
91-
<tr className="table-row">
92-
<th className="table-heading">
93-
<FormattedMessage id="Title" defaultMessage="Title" />
94-
</th>
95-
<th className="table-heading">
96-
<FormattedMessage id="Occurrence" defaultMessage="Occurrence" />
97-
</th>
98-
</tr>
99-
</thead>
100-
<tbody className="table-body">
101-
{blockTypes.items.map((item) => (
102-
<tr key={item['@id']} className="table-row">
103-
<td className="table-cell">
104-
<a href={item['@id']}>{item.title}</a>
105-
<span>{flattenToAppURL(item['@id']) || '/'}</span>
106-
</td>
107-
<td className="table-cell">{item.count}</td>
57+
if (blockTypes.loading) {
58+
return <div>Loading...</div>;
59+
}
60+
61+
if (blockTypes?.error?.status) {
62+
return <Error error={blockTypes.error} />;
63+
}
64+
65+
return (
66+
blockTypes.loaded && (
67+
<div
68+
id="page-block_type"
69+
className="ui container controlpanel-block_type"
70+
>
71+
<h1>
72+
<FormattedMessage
73+
id="block-type"
74+
defaultMessage="{title}"
75+
values={{ title: block.title }}
76+
/>
77+
</h1>
78+
<form className="search" onSubmit={(e) => e.preventDefault()}>
79+
<input
80+
type="text"
81+
placeholder="Search"
82+
onChange={(e) => debouncedSearch(e.target.value)}
83+
/>
84+
<Icon
85+
name={searchSVG}
86+
size="16px"
87+
title={intl.formatMessage(messages.search)}
88+
/>
89+
</form>
90+
{blockTypes.items?.length > 0 ? (
91+
<table className="table">
92+
<thead className="table-header">
93+
<tr className="table-row">
94+
<th className="table-heading">
95+
<FormattedMessage id="Title" defaultMessage="Title" />
96+
</th>
97+
<th className="table-heading">
98+
<FormattedMessage
99+
id="Occurrence"
100+
defaultMessage="Occurrence"
101+
/>
102+
</th>
108103
</tr>
109-
))}
110-
</tbody>
111-
</table>
112-
) : (
113-
<FormattedMessage
114-
id="no-blocks-found"
115-
defaultMessage="No items found for type: {type}."
116-
values={{ type: id }}
117-
/>
118-
)}
119-
{isClient &&
120-
createPortal(
121-
<Toolbar
122-
pathname={pathname}
123-
hideDefaultViewButtons
124-
inner={
125-
<Link to={getParentUrl(pathname)} className="item">
126-
<Icon
127-
name={backSVG}
128-
className="contents circled"
129-
size="30px"
130-
title={intl.formatMessage(messages.back)}
131-
/>
132-
</Link>
133-
}
134-
/>,
135-
document.getElementById('toolbar') as HTMLElement,
104+
</thead>
105+
<tbody className="table-body">
106+
{blockTypes.items.map((item) => (
107+
<tr key={item['@id']} className="table-row">
108+
<td className="table-cell">
109+
<a href={item['@id']}>{item.title}</a>
110+
<span>{flattenToAppURL(item['@id']) || '/'}</span>
111+
</td>
112+
<td className="table-cell">{item.count}</td>
113+
</tr>
114+
))}
115+
</tbody>
116+
</table>
117+
) : (
118+
<FormattedMessage
119+
id="no-blocks-found"
120+
defaultMessage="No items found for type: {type}."
121+
values={{ type: id }}
122+
/>
136123
)}
137-
</div>
138-
) : (
139-
<Unauthorized pathname={pathname} staticContext={props.staticContext} />
124+
{isClient &&
125+
createPortal(
126+
<Toolbar
127+
pathname={pathname}
128+
hideDefaultViewButtons
129+
inner={
130+
<Link to={getParentUrl(pathname)} className="item">
131+
<Icon
132+
name={backSVG}
133+
className="contents circled"
134+
size="30px"
135+
title={intl.formatMessage(messages.back)}
136+
/>
137+
</Link>
138+
}
139+
/>,
140+
document.getElementById('toolbar') as HTMLElement,
141+
)}
142+
</div>
143+
)
140144
);
141145
};
142146

packages/volto/src/components/manage/Controlpanels/BlockTypes.tsx

Lines changed: 66 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ import config from '@plone/volto/registry';
66
import { createPortal } from 'react-dom';
77
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
88
import { Link } from 'react-router-dom';
9-
import { useState, useEffect } from 'react';
9+
import { useEffect } from 'react';
1010
import { getBlockTypes } from '@plone/volto/actions/blockTypes/blockTypes';
1111
import { useDispatch, useSelector } from 'react-redux';
12-
import Unauthorized from '@plone/volto/components/theme/Unauthorized/Unauthorized';
13-
import { listRoles } from '@plone/volto/actions/roles/roles';
12+
import Error from '@plone/volto/components/theme/Error/Error';
1413

1514
import backSVG from '@plone/volto/icons/back.svg';
1615
import type { Location } from 'history';
@@ -30,8 +29,6 @@ type RouteProps = {
3029
const BlockTypesControlpanel = (props: RouteProps) => {
3130
const { location } = props;
3231
const intl = useIntl();
33-
const [isUserManager, setIsUserManager] = useState(false);
34-
const roles = useSelector((state) => state.roles.roles);
3532
const blockTypes = useSelector((state) => state.blockTypes);
3633
const blocksConfig = config.blocks.blocksConfig;
3734
const dispatch = useDispatch();
@@ -40,75 +37,76 @@ const BlockTypesControlpanel = (props: RouteProps) => {
4037
const blocks = [];
4138

4239
useEffect(() => {
43-
dispatch(listRoles());
44-
}, []);
45-
46-
useEffect(() => {
47-
if (roles.length > 0 && roles.map((role) => role.id === 'Manager')) {
48-
setIsUserManager(true);
49-
dispatch(getBlockTypes());
50-
}
51-
}, [roles]);
40+
dispatch(getBlockTypes());
41+
}, [dispatch]);
5242

5343
for (const block of Object.values(blocksConfig)) {
5444
blocks.push(block);
5545
}
5646

57-
return isUserManager ? (
58-
<div
59-
id="page-block_types"
60-
className="ui container controlpanel-block_types"
61-
>
62-
<h1>
63-
<FormattedMessage id="block-types" defaultMessage="Block Types" />
64-
</h1>
65-
<table className="table">
66-
<thead className="table-header">
67-
<tr className="table-row">
68-
<th className="table-heading">
69-
<FormattedMessage id="Type" defaultMessage="Type" />
70-
</th>
71-
<th className="table-heading">
72-
<FormattedMessage id="Occurrence" defaultMessage="Occurrence" />
73-
</th>
74-
</tr>
75-
</thead>
76-
<tbody className="table-body">
77-
{blocks.map((block) => (
78-
<tr key={block.id} className="table-row">
79-
<td className="table-cell">
80-
<a href={`${pathname}/${block.id}`} className="table-link">
81-
{block.title}
82-
</a>
83-
</td>
84-
<td className="table-cell">
85-
{blockTypes.items?.[block.id] || 0}
86-
</td>
47+
if (blockTypes.loading) {
48+
return <div>Loading...</div>;
49+
}
50+
51+
if (blockTypes?.error?.status) {
52+
return <Error error={blockTypes.error} />;
53+
}
54+
55+
return (
56+
blockTypes.loaded && (
57+
<div
58+
id="page-block_types"
59+
className="ui container controlpanel-block_types"
60+
>
61+
<h1>
62+
<FormattedMessage id="block-types" defaultMessage="Block Types" />
63+
</h1>
64+
<table className="table">
65+
<thead className="table-header">
66+
<tr className="table-row">
67+
<th className="table-heading">
68+
<FormattedMessage id="Type" defaultMessage="Type" />
69+
</th>
70+
<th className="table-heading">
71+
<FormattedMessage id="Occurrence" defaultMessage="Occurrence" />
72+
</th>
8773
</tr>
88-
))}
89-
</tbody>
90-
</table>
91-
{isClient &&
92-
createPortal(
93-
<Toolbar
94-
pathname={pathname}
95-
hideDefaultViewButtons
96-
inner={
97-
<Link to={getParentUrl(pathname)} className="item">
98-
<Icon
99-
name={backSVG}
100-
className="contents circled"
101-
size="30px"
102-
title={intl.formatMessage(messages.back)}
103-
/>
104-
</Link>
105-
}
106-
/>,
107-
document.getElementById('toolbar') as HTMLElement,
108-
)}
109-
</div>
110-
) : (
111-
<Unauthorized pathname={pathname} staticContext={props.staticContext} />
74+
</thead>
75+
<tbody className="table-body">
76+
{blocks.map((block) => (
77+
<tr key={block.id} className="table-row">
78+
<td className="table-cell">
79+
<a href={`${pathname}/${block.id}`} className="table-link">
80+
{block.title}
81+
</a>
82+
</td>
83+
<td className="table-cell">
84+
{blockTypes.items?.[block.id] || 0}
85+
</td>
86+
</tr>
87+
))}
88+
</tbody>
89+
</table>
90+
{isClient &&
91+
createPortal(
92+
<Toolbar
93+
pathname={pathname}
94+
hideDefaultViewButtons
95+
inner={
96+
<Link to={getParentUrl(pathname)} className="item">
97+
<Icon
98+
name={backSVG}
99+
className="contents circled"
100+
size="30px"
101+
title={intl.formatMessage(messages.back)}
102+
/>
103+
</Link>
104+
}
105+
/>,
106+
document.getElementById('toolbar') as HTMLElement,
107+
)}
108+
</div>
109+
)
112110
);
113111
};
114112

0 commit comments

Comments
 (0)