@strapi/admin 4.9.1 → 4.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/admin/src/components/LanguageProvider/index.js +1 -1
  2. package/admin/src/components/LocalesProvider/index.js +2 -3
  3. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +3 -6
  4. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +9 -5
  5. package/admin/src/content-manager/components/InputUID/index.js +1 -1
  6. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +4 -8
  7. package/admin/src/content-manager/pages/App/LeftMenu/index.js +56 -35
  8. package/admin/src/content-manager/pages/App/actions.js +19 -7
  9. package/admin/src/content-manager/pages/App/constants.js +3 -3
  10. package/admin/src/content-manager/pages/App/index.js +3 -2
  11. package/admin/src/content-manager/pages/App/reducer.js +7 -6
  12. package/admin/src/content-manager/pages/App/selectors.js +3 -0
  13. package/admin/src/content-manager/pages/App/{useModels.js → useContentManagerInitData.js} +29 -28
  14. package/admin/src/content-manager/pages/App/utils/generateModelsLinks.js +2 -2
  15. package/admin/src/content-manager/pages/App/utils/getContentTypeLinks.js +17 -15
  16. package/admin/src/content-manager/pages/EditSettingsView/components/ModalForm.js +4 -5
  17. package/admin/src/content-manager/pages/EditSettingsView/index.js +4 -0
  18. package/admin/src/content-manager/pages/EditSettingsView/reducer.js +6 -7
  19. package/admin/src/content-manager/pages/EditSettingsView/utils/layout.js +1 -30
  20. package/admin/src/content-manager/pages/EditView/DeleteLink/index.js +5 -11
  21. package/admin/src/content-manager/pages/EditView/index.js +2 -7
  22. package/admin/src/content-manager/pages/ListSettingsView/index.js +1 -0
  23. package/admin/src/hooks/useFetchMarketplacePlugins/utils/api.js +7 -8
  24. package/admin/src/hooks/useFetchMarketplaceProviders/utils/api.js +5 -0
  25. package/admin/src/pages/HomePage/SocialLinks.js +1 -1
  26. package/admin/src/pages/MarketplacePage/components/EmptyNpmPackageSearch/index.js +1 -1
  27. package/admin/src/pages/MarketplacePage/components/NpmPackagesGrid/index.js +42 -9
  28. package/admin/src/pages/MarketplacePage/components/NpmPackagesPagination/index.js +26 -0
  29. package/admin/src/pages/MarketplacePage/components/OfflineLayout/index.js +45 -0
  30. package/admin/src/pages/MarketplacePage/index.js +80 -175
  31. package/admin/src/pages/MarketplacePage/utils/useMarketplaceData.js +85 -0
  32. package/admin/src/pages/SettingsPage/pages/Roles/ListPage/components/RoleRow/index.js +12 -4
  33. package/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js +21 -2
  34. package/admin/src/translations/en.json +51 -50
  35. package/admin/src/translations/ru.json +51 -19
  36. package/build/{Admin-authenticatedApp.a168aa38.chunk.js → Admin-authenticatedApp.217db666.chunk.js} +5 -5
  37. package/build/{Admin_homePage.da2181fe.chunk.js → Admin_homePage.f9309c6d.chunk.js} +1 -1
  38. package/build/Admin_marketplace.56bc1008.chunk.js +31 -0
  39. package/build/{Admin_settingsPage.cb63220f.chunk.js → Admin_settingsPage.1dbfc9ce.chunk.js} +1 -1
  40. package/build/{admin-app.8cde5b22.chunk.js → admin-app.558af642.chunk.js} +6 -6
  41. package/build/admin-roles-list.329c1f63.chunk.js +31 -0
  42. package/build/audit-logs-settings-page.19d90bda.chunk.js +76 -0
  43. package/build/content-manager.d1565bfc.chunk.js +1132 -0
  44. package/build/content-type-builder-translation-en-json.6c8e69ab.chunk.js +1 -0
  45. package/build/content-type-builder.9d780e7f.chunk.js +126 -0
  46. package/build/en-json.cf600231.chunk.js +1 -0
  47. package/build/index.html +1 -1
  48. package/build/main.ef8db4a2.js +2280 -0
  49. package/build/ru-json.e0662702.chunk.js +1 -0
  50. package/build/{runtime~main.03cdff31.js → runtime~main.3a92d953.js} +2 -2
  51. package/build/users-roles-settings-page.2f85dcec.chunk.js +30 -0
  52. package/ee/admin/hooks/useLicenseLimitNotification/index.js +1 -1
  53. package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/hooks/useAuditLogsData.js +6 -3
  54. package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/index.js +15 -5
  55. package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/utils/getDisplayedFilters.js +52 -45
  56. package/package.json +10 -10
  57. package/webpack.config.js +5 -1
  58. package/admin/src/content-manager/pages/App/LeftMenu/utils/index.js +0 -1
  59. package/admin/src/content-manager/pages/App/LeftMenu/utils/matchByTitle.js +0 -24
  60. package/build/9347.058ddb22.chunk.js +0 -1
  61. package/build/Admin_marketplace.99e25059.chunk.js +0 -31
  62. package/build/admin-roles-list.97e198f9.chunk.js +0 -31
  63. package/build/audit-logs-settings-page.ca9a3c46.chunk.js +0 -76
  64. package/build/content-manager.ba088a66.chunk.js +0 -1132
  65. package/build/content-type-builder-translation-en-json.e577d595.chunk.js +0 -1
  66. package/build/content-type-builder.fef159db.chunk.js +0 -126
  67. package/build/en-json.b052667a.chunk.js +0 -1
  68. package/build/main.0df5f5ca.js +0 -2280
  69. package/build/ru-json.6a01cea6.chunk.js +0 -1
  70. package/build/users-roles-settings-page.9359e4d1.chunk.js +0 -30
  71. /package/admin/src/{content-manager/components/InputUID/useDebounce.js → hooks/useDebounce/index.js} +0 -0
@@ -1,13 +1,10 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
3
  import { Helmet } from 'react-helmet';
4
- import matchSorter from 'match-sorter';
5
4
  import {
6
- AnErrorOccurred,
7
5
  CheckPagePermissions,
8
6
  useFocusWhenNavigate,
9
7
  useTracking,
10
- LoadingIndicatorPage,
11
8
  useNotification,
12
9
  useAppInfos,
13
10
  useQueryParams,
@@ -18,8 +15,6 @@ import {
18
15
  Main,
19
16
  Searchbar,
20
17
  Box,
21
- useNotifyAT,
22
- Typography,
23
18
  Flex,
24
19
  Tabs,
25
20
  Tab,
@@ -28,39 +23,25 @@ import {
28
23
  TabPanel,
29
24
  } from '@strapi/design-system';
30
25
 
31
- import EmptyNpmPackageSearch from './components/EmptyNpmPackageSearch';
32
26
  import PageHeader from './components/PageHeader';
33
- import useFetchMarketplaceProviders from '../../hooks/useFetchMarketplaceProviders';
34
- import useFetchMarketplacePlugins from '../../hooks/useFetchMarketplacePlugins';
35
27
  import adminPermissions from '../../permissions';
36
- import offlineCloud from '../../assets/images/icon_offline-cloud.svg';
37
28
  import useNavigatorOnLine from '../../hooks/useNavigatorOnLine';
38
29
  import MissingPluginBanner from './components/MissingPluginBanner';
39
30
  import NpmPackagesGrid from './components/NpmPackagesGrid';
40
31
  import SortSelect from './components/SortSelect';
41
32
  import NpmPackagesFilters from './components/NpmPackagesFilters';
42
-
43
- const matchSearch = (npmPackages, search) => {
44
- return matchSorter(npmPackages, search, {
45
- keys: [
46
- {
47
- threshold: matchSorter.rankings.WORD_STARTS_WITH,
48
- key: 'attributes.name',
49
- },
50
- { threshold: matchSorter.rankings.WORD_STARTS_WITH, key: 'attributes.description' },
51
- ],
52
- baseSort: (a, b) => (a.index < b.index ? -1 : 1),
53
- });
54
- };
33
+ import NpmPackagesPagination from './components/NpmPackagesPagination';
34
+ import useDebounce from '../../hooks/useDebounce';
35
+ import OfflineLayout from './components/OfflineLayout';
36
+ import useMarketplaceData from './utils/useMarketplaceData';
55
37
 
56
38
  const MarketPlacePage = () => {
57
39
  const { formatMessage } = useIntl();
58
40
  const { trackUsage } = useTracking();
59
- const { notifyStatus } = useNotifyAT();
60
41
  const trackUsageRef = useRef(trackUsage);
61
42
  const toggleNotification = useNotification();
62
- const [searchQuery, setSearchQuery] = useState('');
63
43
  const [{ query }, setQuery] = useQueryParams();
44
+ const debouncedSearch = useDebounce(query?.search, 500) || '';
64
45
 
65
46
  const { autoReload: isInDevelopmentMode, dependencies, useYarn, strapiVersion } = useAppInfos();
66
47
  const isOnline = useNavigatorOnLine();
@@ -74,33 +55,6 @@ const MarketPlacePage = () => {
74
55
 
75
56
  useFocusWhenNavigate();
76
57
 
77
- const marketplaceTitle = formatMessage({
78
- id: 'global.marketplace',
79
- defaultMessage: 'Marketplace',
80
- });
81
-
82
- const notifyMarketplaceLoad = () => {
83
- notifyStatus(
84
- formatMessage(
85
- {
86
- id: 'app.utils.notify.data-loaded',
87
- defaultMessage: 'The {target} has loaded',
88
- },
89
- { target: marketplaceTitle }
90
- )
91
- );
92
- };
93
-
94
- const { status: marketplaceProvidersStatus, data: marketplaceProvidersResponse } =
95
- useFetchMarketplaceProviders(notifyMarketplaceLoad, tabQuery.provider);
96
-
97
- const { status: marketplacePluginsStatus, data: marketplacePluginsResponse } =
98
- useFetchMarketplacePlugins(notifyMarketplaceLoad, tabQuery.plugin);
99
-
100
- const isLoading = [marketplacePluginsStatus, marketplaceProvidersStatus].includes('loading');
101
-
102
- const hasFailed = [marketplacePluginsStatus, marketplaceProvidersStatus].includes('error');
103
-
104
58
  useEffect(() => {
105
59
  trackUsageRef.current('didGoToMarketplace');
106
60
  }, []);
@@ -118,94 +72,50 @@ const MarketPlacePage = () => {
118
72
  }
119
73
  }, [toggleNotification, isInDevelopmentMode]);
120
74
 
121
- if (!isOnline) {
122
- return (
123
- <Layout>
124
- <Main>
125
- <PageHeader isOnline={isOnline} />
126
- <Flex
127
- width="100%"
128
- direction="column"
129
- alignItems="center"
130
- justifyContent="center"
131
- style={{ paddingTop: '120px' }}
132
- >
133
- <Box paddingBottom={2}>
134
- <Typography textColor="neutral700" variant="alpha">
135
- {formatMessage({
136
- id: 'admin.pages.MarketPlacePage.offline.title',
137
- defaultMessage: 'You are offline',
138
- })}
139
- </Typography>
140
- </Box>
141
- <Box paddingBottom={6}>
142
- <Typography textColor="neutral700" variant="epsilon">
143
- {formatMessage({
144
- id: 'admin.pages.MarketPlacePage.offline.subtitle',
145
- defaultMessage:
146
- 'You need to be connected to the Internet to access Strapi Market.',
147
- })}
148
- </Typography>
149
- </Box>
150
- <img src={offlineCloud} alt="offline" style={{ width: '88px', height: '88px' }} />
151
- </Flex>
152
- </Main>
153
- </Layout>
154
- );
155
- }
156
-
157
- if (hasFailed) {
158
- return (
159
- <Layout>
160
- <ContentLayout>
161
- <Box paddingTop={8}>
162
- <AnErrorOccurred />
163
- </Box>
164
- </ContentLayout>
165
- </Layout>
166
- );
167
- }
75
+ const {
76
+ pluginsResponse,
77
+ providersResponse,
78
+ pluginsStatus,
79
+ providersStatus,
80
+ possibleCollections,
81
+ possibleCategories,
82
+ pagination,
83
+ } = useMarketplaceData({ npmPackageType, debouncedSearch, query, tabQuery });
168
84
 
169
- if (isLoading) {
170
- return (
171
- <Layout>
172
- <Main aria-busy>
173
- <LoadingIndicatorPage />
174
- </Main>
175
- </Layout>
176
- );
85
+ if (!isOnline) {
86
+ return <OfflineLayout />;
177
87
  }
178
88
 
179
- // Search for plugins and providers that match the search query
180
- const pluginSearchResults = matchSearch(marketplacePluginsResponse.data, searchQuery);
181
- const providerSearchResults = matchSearch(marketplaceProvidersResponse.data, searchQuery);
182
- const emptySearchMessage = formatMessage(
183
- {
184
- id: 'admin.pages.MarketPlacePage.search.empty',
185
- defaultMessage: 'No result for "{target}"',
186
- },
187
- { target: searchQuery }
188
- );
189
-
190
89
  const handleTabChange = (selected) => {
191
90
  const selectedTab = selected === 0 ? 'plugin' : 'provider';
192
91
  const hasTabQuery = tabQuery[selectedTab] && Object.keys(tabQuery[selectedTab]).length;
193
92
 
194
93
  if (hasTabQuery) {
195
- setQuery({ ...tabQuery[selectedTab], npmPackageType: selectedTab });
94
+ setQuery({
95
+ // Keep filters and search
96
+ ...tabQuery[selectedTab],
97
+ search: query?.search || '',
98
+ // Set tab and reset page
99
+ npmPackageType: selectedTab,
100
+ page: 1,
101
+ });
196
102
  } else {
197
103
  setQuery({
104
+ // Set tab
198
105
  npmPackageType: selectedTab,
199
106
  // Clear filters
200
107
  collections: [],
201
108
  categories: [],
202
109
  sort: 'name:asc',
110
+ page: 1,
111
+ // Keep search
112
+ search: query?.search || '',
203
113
  });
204
114
  }
205
115
  };
206
116
 
207
117
  const handleSelectChange = (update) => {
208
- setQuery(update);
118
+ setQuery({ ...update, page: 1 });
209
119
  setTabQuery((prev) => ({
210
120
  ...prev,
211
121
  [npmPackageType]: { ...prev[npmPackageType], ...update },
@@ -213,19 +123,13 @@ const MarketPlacePage = () => {
213
123
  };
214
124
 
215
125
  const handleSelectClear = (filterType) => {
216
- setQuery({ [filterType]: [] }, 'remove');
126
+ setQuery({ [filterType]: [], page: null }, 'remove');
217
127
  setTabQuery((prev) => ({ ...prev, [npmPackageType]: {} }));
218
128
  };
219
129
 
220
130
  // Check if plugins and providers are installed already
221
131
  const installedPackageNames = Object.keys(dependencies);
222
132
 
223
- const possibleCollections =
224
- npmPackageType === 'plugin'
225
- ? marketplacePluginsResponse.meta.collections
226
- : marketplaceProvidersResponse.meta.collections;
227
- const possibleCategories = marketplacePluginsResponse.meta.categories;
228
-
229
133
  return (
230
134
  <Layout>
231
135
  <Main>
@@ -237,27 +141,6 @@ const MarketPlacePage = () => {
237
141
  />
238
142
  <PageHeader isOnline={isOnline} npmPackageType={npmPackageType} />
239
143
  <ContentLayout>
240
- <Box width="25%" paddingBottom={4}>
241
- <Searchbar
242
- name="searchbar"
243
- onClear={() => setSearchQuery('')}
244
- value={searchQuery}
245
- onChange={(e) => setSearchQuery(e.target.value)}
246
- clearLabel={formatMessage({
247
- id: 'admin.pages.MarketPlacePage.search.clear',
248
- defaultMessage: 'Clear the search',
249
- })}
250
- placeholder={formatMessage({
251
- id: 'admin.pages.MarketPlacePage.search.placeholder',
252
- defaultMessage: 'Search',
253
- })}
254
- >
255
- {formatMessage({
256
- id: 'admin.pages.MarketPlacePage.search.placeholder',
257
- defaultMessage: 'Search',
258
- })}
259
- </Searchbar>
260
- </Box>
261
144
  <TabGroup
262
145
  label={formatMessage({
263
146
  id: 'admin.pages.MarketPlacePage.tab-group.label',
@@ -268,24 +151,49 @@ const MarketPlacePage = () => {
268
151
  initialSelectedTabIndex={['plugin', 'provider'].indexOf(npmPackageType)}
269
152
  onTabChange={handleTabChange}
270
153
  >
271
- <Box paddingBottom={4}>
154
+ <Flex justifyContent="space-between" paddingBottom={4}>
272
155
  <Tabs>
273
156
  <Tab>
274
157
  {formatMessage({
275
158
  id: 'admin.pages.MarketPlacePage.plugins',
276
159
  defaultMessage: 'Plugins',
277
160
  })}{' '}
278
- ({pluginSearchResults.length})
161
+ {pluginsStatus === 'success'
162
+ ? `(${pluginsResponse.meta.pagination.total})`
163
+ : '...'}
279
164
  </Tab>
280
165
  <Tab>
281
166
  {formatMessage({
282
167
  id: 'admin.pages.MarketPlacePage.providers',
283
168
  defaultMessage: 'Providers',
284
169
  })}{' '}
285
- ({providerSearchResults.length})
170
+ {providersStatus === 'success'
171
+ ? `(${providersResponse.meta.pagination.total})`
172
+ : '...'}
286
173
  </Tab>
287
174
  </Tabs>
288
- </Box>
175
+ <Box width="25%">
176
+ <Searchbar
177
+ name="searchbar"
178
+ onClear={() => setQuery({ search: '', page: 1 })}
179
+ value={query?.search}
180
+ onChange={(e) => setQuery({ search: e.target.value, page: 1 })}
181
+ clearLabel={formatMessage({
182
+ id: 'admin.pages.MarketPlacePage.search.clear',
183
+ defaultMessage: 'Clear the search',
184
+ })}
185
+ placeholder={formatMessage({
186
+ id: 'admin.pages.MarketPlacePage.search.placeholder',
187
+ defaultMessage: 'Search',
188
+ })}
189
+ >
190
+ {formatMessage({
191
+ id: 'admin.pages.MarketPlacePage.search.placeholder',
192
+ defaultMessage: 'Search',
193
+ })}
194
+ </Searchbar>
195
+ </Box>
196
+ </Flex>
289
197
  <Flex paddingBottom={4} gap={2}>
290
198
  <SortSelect
291
199
  sortQuery={query?.sort || 'name:asc'}
@@ -304,36 +212,33 @@ const MarketPlacePage = () => {
304
212
  <TabPanels>
305
213
  {/* Plugins panel */}
306
214
  <TabPanel>
307
- {searchQuery.length > 0 && !pluginSearchResults.length ? (
308
- <EmptyNpmPackageSearch content={emptySearchMessage} />
309
- ) : (
310
- <NpmPackagesGrid
311
- npmPackages={pluginSearchResults}
312
- installedPackageNames={installedPackageNames}
313
- useYarn={useYarn}
314
- isInDevelopmentMode={isInDevelopmentMode}
315
- npmPackageType="plugin"
316
- strapiAppVersion={strapiVersion}
317
- />
318
- )}
215
+ <NpmPackagesGrid
216
+ npmPackages={pluginsResponse?.data}
217
+ status={pluginsStatus}
218
+ installedPackageNames={installedPackageNames}
219
+ useYarn={useYarn}
220
+ isInDevelopmentMode={isInDevelopmentMode}
221
+ npmPackageType="plugin"
222
+ strapiAppVersion={strapiVersion}
223
+ debouncedSearch={debouncedSearch}
224
+ />
319
225
  </TabPanel>
320
226
  {/* Providers panel */}
321
227
  <TabPanel>
322
- {searchQuery.length > 0 && !providerSearchResults.length ? (
323
- <EmptyNpmPackageSearch content={emptySearchMessage} />
324
- ) : (
325
- <NpmPackagesGrid
326
- npmPackages={providerSearchResults}
327
- installedPackageNames={installedPackageNames}
328
- useYarn={useYarn}
329
- isInDevelopmentMode={isInDevelopmentMode}
330
- npmPackageType="provider"
331
- />
332
- )}
228
+ <NpmPackagesGrid
229
+ npmPackages={providersResponse?.data}
230
+ status={providersStatus}
231
+ installedPackageNames={installedPackageNames}
232
+ useYarn={useYarn}
233
+ isInDevelopmentMode={isInDevelopmentMode}
234
+ npmPackageType="provider"
235
+ debouncedSearch={debouncedSearch}
236
+ />
333
237
  </TabPanel>
334
238
  </TabPanels>
335
239
  </TabGroup>
336
- <Box paddingTop={7}>
240
+ {pagination && <NpmPackagesPagination pagination={pagination} />}
241
+ <Box paddingTop={8}>
337
242
  <MissingPluginBanner />
338
243
  </Box>
339
244
  </ContentLayout>
@@ -0,0 +1,85 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import { useNotifyAT } from '@strapi/design-system';
4
+ import useFetchMarketplacePlugins from '../../../hooks/useFetchMarketplacePlugins';
5
+ import useFetchMarketplaceProviders from '../../../hooks/useFetchMarketplaceProviders';
6
+
7
+ function useMarketplaceData({ npmPackageType, debouncedSearch, query, tabQuery }) {
8
+ const { notifyStatus } = useNotifyAT();
9
+ const { formatMessage } = useIntl();
10
+ const marketplaceTitle = formatMessage({
11
+ id: 'global.marketplace',
12
+ defaultMessage: 'Marketplace',
13
+ });
14
+
15
+ const notifyMarketplaceLoad = () => {
16
+ notifyStatus(
17
+ formatMessage(
18
+ {
19
+ id: 'app.utils.notify.data-loaded',
20
+ defaultMessage: 'The {target} has loaded',
21
+ },
22
+ { target: marketplaceTitle }
23
+ )
24
+ );
25
+ };
26
+
27
+ const paginationParams = {
28
+ page: query?.page || 1,
29
+ pageSize: query?.pageSize || 24,
30
+ };
31
+
32
+ const { data: pluginsResponse, status: pluginsStatus } = useFetchMarketplacePlugins(
33
+ notifyMarketplaceLoad,
34
+ {
35
+ ...tabQuery.plugin,
36
+ pagination: paginationParams,
37
+ search: debouncedSearch,
38
+ }
39
+ );
40
+
41
+ const { data: providersResponse, status: providersStatus } = useFetchMarketplaceProviders(
42
+ notifyMarketplaceLoad,
43
+ {
44
+ ...tabQuery.provider,
45
+ pagination: paginationParams,
46
+ search: debouncedSearch,
47
+ }
48
+ );
49
+
50
+ const npmPackageTypeResponse = npmPackageType === 'plugin' ? pluginsResponse : providersResponse;
51
+ const npmPackageTypeStatus = npmPackageType === 'plugin' ? pluginsStatus : providersStatus;
52
+
53
+ const [possibleCollections, setPossibleCollections] = useState({});
54
+ const [possibleCategories, setPossibleCategories] = useState({});
55
+
56
+ // Keep possible filters up to date, but don't lose them while loading
57
+ useEffect(() => {
58
+ if (npmPackageTypeStatus === 'success') {
59
+ setPossibleCollections(npmPackageTypeResponse.meta.collections);
60
+ }
61
+
62
+ if (pluginsStatus === 'success') {
63
+ setPossibleCategories(pluginsResponse.meta.categories);
64
+ }
65
+ }, [
66
+ pluginsResponse?.meta.categories,
67
+ pluginsStatus,
68
+ npmPackageTypeResponse?.meta.collections,
69
+ npmPackageTypeStatus,
70
+ ]);
71
+
72
+ const { pagination } = npmPackageTypeStatus === 'success' ? npmPackageTypeResponse.meta : {};
73
+
74
+ return {
75
+ pluginsResponse,
76
+ providersResponse,
77
+ pluginsStatus,
78
+ providersStatus,
79
+ possibleCollections,
80
+ possibleCategories,
81
+ pagination,
82
+ };
83
+ }
84
+
85
+ export default useMarketplaceData;
@@ -4,8 +4,9 @@ import { Box, Flex, Td, Tr, Typography, IconButton } from '@strapi/design-system
4
4
  import { stopPropagation, onRowClick, pxToRem } from '@strapi/helper-plugin';
5
5
  import { useIntl } from 'react-intl';
6
6
 
7
- const RoleRow = ({ id, name, description, usersCount, icons, rowIndex }) => {
7
+ const RoleRow = ({ id, name, description, usersCount, icons, rowIndex, canUpdate }) => {
8
8
  const { formatMessage } = useIntl();
9
+ const [, editObject] = icons;
9
10
 
10
11
  const usersCountText = formatMessage(
11
12
  {
@@ -19,9 +20,11 @@ const RoleRow = ({ id, name, description, usersCount, icons, rowIndex }) => {
19
20
  <Tr
20
21
  aria-rowindex={rowIndex}
21
22
  key={id}
22
- {...onRowClick({
23
- fn: icons[1].onClick,
24
- })}
23
+ {...(canUpdate
24
+ ? onRowClick({
25
+ fn: editObject.onClick,
26
+ })
27
+ : {})}
25
28
  >
26
29
  <Td maxWidth={pxToRem(130)}>
27
30
  <Typography ellipsis textColor="neutral800">
@@ -58,6 +61,11 @@ RoleRow.propTypes = {
58
61
  usersCount: PropTypes.number.isRequired,
59
62
  icons: PropTypes.array.isRequired,
60
63
  rowIndex: PropTypes.number.isRequired,
64
+ canUpdate: PropTypes.bool,
65
+ };
66
+
67
+ RoleRow.defaultProps = {
68
+ canUpdate: false,
61
69
  };
62
70
 
63
71
  export default RoleRow;
@@ -9,6 +9,8 @@ import {
9
9
  useQueryParams,
10
10
  useRBAC,
11
11
  useFocusWhenNavigate,
12
+ useFilter,
13
+ useCollator,
12
14
  } from '@strapi/helper-plugin';
13
15
  import { Plus, Trash, Duplicate, Pencil } from '@strapi/icons';
14
16
  import {
@@ -27,7 +29,6 @@ import {
27
29
  VisuallyHidden,
28
30
  } from '@strapi/design-system';
29
31
  import get from 'lodash/get';
30
- import matchSorter from 'match-sorter';
31
32
  import { useIntl } from 'react-intl';
32
33
  import { useHistory } from 'react-router-dom';
33
34
  import { useRolesList } from '../../../../../hooks';
@@ -38,6 +39,7 @@ import reducer, { initialState } from './reducer';
38
39
 
39
40
  const useSortedRoles = () => {
40
41
  useFocusWhenNavigate();
42
+ const { locale } = useIntl();
41
43
  const {
42
44
  isLoading: isLoadingForPermissions,
43
45
  allowedActions: { canCreate, canDelete, canRead, canUpdate },
@@ -46,7 +48,23 @@ const useSortedRoles = () => {
46
48
  const { getData, roles, isLoading } = useRolesList(false);
47
49
  const [{ query }] = useQueryParams();
48
50
  const _q = query?._q || '';
49
- const sortedRoles = matchSorter(roles, _q, { keys: ['name', 'description'] });
51
+
52
+ const { includes } = useFilter(locale, {
53
+ sensitivity: 'base',
54
+ });
55
+
56
+ /**
57
+ * @type {Intl.Collator}
58
+ */
59
+ const formatter = useCollator(locale, {
60
+ sensitivity: 'base',
61
+ });
62
+
63
+ const sortedRoles = (roles || [])
64
+ .filter((role) => includes(role.name, _q) || includes(role.description, _q))
65
+ .sort(
66
+ (a, b) => formatter.compare(a.name, b.name) || formatter.compare(a.description, b.description)
67
+ );
50
68
 
51
69
  useEffect(() => {
52
70
  if (!isLoadingForPermissions && canRead) {
@@ -356,6 +374,7 @@ const RoleListPage = () => {
356
374
  usersCount={role.usersCount}
357
375
  icons={getIcons(role)}
358
376
  rowIndex={index + 2}
377
+ canUpdate={canUpdate}
359
378
  />
360
379
  ))}
361
380
  </Tbody>