Skip to content

Commit 18641e1

Browse files
committed
add loading indicator while bulk keyword changes are being processed
1 parent dd73947 commit 18641e1

4 files changed

Lines changed: 72 additions & 21 deletions

File tree

frontend/packages/volto-keywordmanager/src/components/DeleteModal.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
1-
import { Button, Modal } from '@plone/components';
1+
import { Button, Modal, Spinner } from '@plone/components';
22
import Icon from '@plone/volto/components/theme/Icon/Icon';
33
import { FormattedMessage } from 'react-intl';
44
import clearSVG from '@plone/volto/icons/clear.svg';
55
import { Dialog } from '@plone/components';
6+
import { useIntl, defineMessages } from 'react-intl';
67

78
interface DeleteModalProps {
9+
isLoading: boolean;
810
selectionCount: number;
911
selectedKeys: string | Set<string>;
1012
keywords: { items?: { name: string }[] };
1113
onConfirm: (keys: string[]) => void;
1214
}
1315

16+
const messages = defineMessages({
17+
loading: {
18+
id: 'loading',
19+
defaultMessage: 'Loading',
20+
},
21+
});
22+
1423
const DeleteModal = ({
24+
isLoading,
1525
selectionCount,
1626
selectedKeys,
1727
keywords,
1828
onConfirm,
1929
}: DeleteModalProps) => {
30+
const intl = useIntl();
31+
2032
return (
2133
<Modal className="delete-modal">
2234
<Dialog>
@@ -56,7 +68,11 @@ const DeleteModal = ({
5668
close();
5769
}}
5870
>
59-
<FormattedMessage id="Delete" defaultMessage="Delete" />
71+
{isLoading ? (
72+
<Spinner aria-label={intl.formatMessage(messages.loading)} />
73+
) : (
74+
<FormattedMessage id="Delete" defaultMessage="Delete" />
75+
)}
6076
</Button>
6177
</div>
6278
</>

frontend/packages/volto-keywordmanager/src/components/KeywordManager.tsx

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ const KeywordManager = (props) => {
7474
// Sorting
7575
const [sortOn, setSortOn] = useState<string>('');
7676
const [sortOrder, setSortOrder] = useState<string>('');
77+
const [isLoading, setIsLoading] = useState<boolean>(false);
7778

7879
const options = useMemo(
7980
() => ({
@@ -113,19 +114,29 @@ const KeywordManager = (props) => {
113114
if (typeof kw == 'string') {
114115
kw = [kw];
115116
}
116-
await dispatch(deleteKeywords({ items: kw, indexName: keywordIndex }));
117-
await dispatch(getKeywords(options));
117+
setIsLoading(true);
118+
try {
119+
await dispatch(deleteKeywords({ items: kw, indexName: keywordIndex }));
120+
} finally {
121+
setIsLoading(false);
122+
}
123+
dispatch(getKeywords(options));
118124
};
119125

120126
const handleUpdateKeywords = async (kw: string, olds: string[]) => {
121-
await dispatch(
122-
updateKeywords({
123-
new_keyword: kw,
124-
old_keywords: olds,
125-
indexName: keywordIndex,
126-
}),
127-
);
128-
await dispatch(getKeywords(options));
127+
setIsLoading(true);
128+
try {
129+
await dispatch(
130+
updateKeywords({
131+
new_keyword: kw,
132+
old_keywords: olds,
133+
indexName: keywordIndex,
134+
}),
135+
);
136+
} finally {
137+
setIsLoading(false);
138+
}
139+
dispatch(getKeywords(options));
129140
};
130141

131142
if (keywords?.error?.status) {
@@ -200,6 +211,7 @@ const KeywordManager = (props) => {
200211
<Icon name={replaceSVG} size="20px" />
201212
</Button>
202213
<RenameModal
214+
isLoading={isLoading}
203215
selectionCount={selectionCount}
204216
selectedKeys={selectedKeys}
205217
keywords={keywords}
@@ -217,6 +229,7 @@ const KeywordManager = (props) => {
217229
<Icon name={trashSVG} size="20px" />
218230
</Button>
219231
<DeleteModal
232+
isLoading={isLoading}
220233
selectionCount={selectionCount}
221234
selectedKeys={selectedKeys}
222235
keywords={keywords}

frontend/packages/volto-keywordmanager/src/components/KeywordView.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import { getVocabulary } from '@plone/volto/actions/vocabularies/vocabularies';
1818
import backSVG from '@plone/volto/icons/back.svg';
1919
import trashSVG from '@plone/volto/icons/delete.svg';
2020
import searchSVG from '@plone/volto/icons/zoom.svg';
21-
import circleDismissSVG from '@plone/volto/icons/circle-dismiss.svg';
2221
import UniversalLink from '@plone/volto/components/manage/UniversalLink/UniversalLink';
2322
import KeywordList from './KeywordList';
2423

@@ -84,6 +83,7 @@ const KeywordView = (props) => {
8483
const [selectedTypes, setSelectedTypes] = useState<[]>([]);
8584
const [selectedStates, setSelectedStates] = useState<[]>([]);
8685
const [search, setSearch] = useState<string>('');
86+
const [isLoading, setIsLoading] = useState<boolean>(false);
8787

8888
const options = useMemo(
8989
() => ({
@@ -124,10 +124,15 @@ const KeywordView = (props) => {
124124
if (typeof kw == 'string') {
125125
kw = [kw];
126126
}
127-
await dispatch(
128-
deleteKeywords({ items: kw, path: id, indexName: keywordIndex }),
129-
);
130-
await dispatch(searchContent('/', options, 'keywords'));
127+
setIsLoading(true);
128+
try {
129+
await dispatch(
130+
deleteKeywords({ items: kw, path: id, indexName: keywordIndex }),
131+
);
132+
} finally {
133+
setIsLoading(false);
134+
}
135+
dispatch(searchContent('/', options, 'keywords'));
131136
};
132137

133138
return (
@@ -178,6 +183,7 @@ const KeywordView = (props) => {
178183
<Icon name={trashSVG} size="20px" />
179184
</Button>
180185
<DeleteModal
186+
isLoading={isLoading}
181187
selectionCount={selectionCount}
182188
selectedKeys={selectedKeys}
183189
keywords={keywords}

frontend/packages/volto-keywordmanager/src/components/RenameModal.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,32 @@ import { useState } from 'react';
1111
import { FormattedMessage } from 'react-intl';
1212
import clearSVG from '@plone/volto/icons/clear.svg';
1313
import { Dialog } from '@plone/components';
14+
import { Spinner } from '@plone/components';
15+
import { useIntl, defineMessages } from 'react-intl';
1416

1517
interface RenameModalProps {
18+
isLoading: boolean;
1619
selectionCount: number;
1720
selectedKeys: string | Set<string>;
1821
keywords: { items?: { name: string }[] };
1922
onConfirm: (newKeyword: string, oldKeywords: string[]) => void;
2023
}
2124

25+
const messages = defineMessages({
26+
loading: {
27+
id: 'loading',
28+
defaultMessage: 'Loading',
29+
},
30+
});
31+
2232
const RenameModal = ({
33+
isLoading,
2334
selectionCount,
2435
selectedKeys,
2536
keywords,
2637
onConfirm,
2738
}: RenameModalProps) => {
39+
const intl = useIntl();
2840
const [selectedRadio, setSelectedRadio] = useState<string>('select');
2941
const [name, setName] = useState<string | null>(null);
3042

@@ -116,10 +128,14 @@ const RenameModal = ({
116128
close();
117129
}}
118130
>
119-
<FormattedMessage
120-
id="Rename & Merge"
121-
defaultMessage="Rename & Merge"
122-
/>
131+
{isLoading ? (
132+
<Spinner aria-label={intl.formatMessage(messages.loading)} />
133+
) : (
134+
<FormattedMessage
135+
id="Rename & Merge"
136+
defaultMessage="Rename & Merge"
137+
/>
138+
)}
123139
</Button>
124140
</div>
125141
</>

0 commit comments

Comments
 (0)