@strapi/admin 4.12.2 → 4.12.5

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 (42) hide show
  1. package/admin/src/content-manager/components/Wysiwyg/WysiwygNav.js +7 -30
  2. package/admin/src/content-manager/hooks/useSyncRbac/index.js +10 -2
  3. package/admin/src/content-manager/pages/CollectionTypeRecursivePath/index.js +1 -1
  4. package/admin/src/content-manager/pages/EditViewLayoutManager/index.js +2 -2
  5. package/admin/src/content-manager/pages/ListSettingsView/index.js +16 -41
  6. package/admin/src/content-manager/pages/ListView/index.js +27 -1
  7. package/admin/src/content-manager/pages/ListViewLayoutManager/index.js +2 -2
  8. package/admin/src/pages/MarketplacePage/index.js +0 -1
  9. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +24 -31
  10. package/admin/src/pages/SettingsPage/pages/TransferTokens/ListView/index.js +11 -6
  11. package/admin/src/pages/SettingsPage/pages/Users/EditPage/index.js +2 -0
  12. package/admin/src/pages/SettingsPage/pages/Users/ListPage/index.js +1 -0
  13. package/admin/src/translations/zh-Hans.json +918 -902
  14. package/build/4546.7a3c0d03.chunk.js +1 -0
  15. package/build/{9806.3392505e.chunk.js → 9806.5d5a0e8d.chunk.js} +16 -16
  16. package/build/{Admin-authenticatedApp.3c585a0d.chunk.js → Admin-authenticatedApp.01fc56de.chunk.js} +1 -1
  17. package/build/{Admin_marketplace.dde9c148.chunk.js → Admin_marketplace.c94239f6.chunk.js} +1 -1
  18. package/build/admin-edit-users.acfd4128.chunk.js +10 -0
  19. package/build/{admin-users.123aa08e.chunk.js → admin-users.00e20017.chunk.js} +1 -1
  20. package/build/api-tokens-list-page.0af7d431.chunk.js +16 -0
  21. package/build/audit-logs-settings-page.0f73ccf8.chunk.js +1 -0
  22. package/build/content-manager.b40f79c0.chunk.js +1099 -0
  23. package/build/{content-type-builder.40534de5.chunk.js → content-type-builder.cd999f6e.chunk.js} +2 -2
  24. package/build/i18n-translation-ru-json.a3dbc125.chunk.js +1 -0
  25. package/build/index.html +1 -1
  26. package/build/main.9dbe4579.js +2859 -0
  27. package/build/{runtime~main.2902859a.js → runtime~main.46a609e9.js} +1 -1
  28. package/build/transfer-tokens-list-page.d6986b03.chunk.js +16 -0
  29. package/build/users-permissions-translation-zh-Hans-json.8d82c809.chunk.js +1 -0
  30. package/build/{users-roles-settings-page.3f9f063e.chunk.js → users-roles-settings-page.9d9a1eff.chunk.js} +1 -1
  31. package/build/zh-Hans-json.97efd015.chunk.js +1 -0
  32. package/package.json +10 -10
  33. package/build/4546.cfafae68.chunk.js +0 -1
  34. package/build/admin-edit-users.79eeb125.chunk.js +0 -10
  35. package/build/api-tokens-list-page.505bf7e0.chunk.js +0 -16
  36. package/build/audit-logs-settings-page.4b422831.chunk.js +0 -1
  37. package/build/content-manager.2af15f57.chunk.js +0 -1099
  38. package/build/i18n-translation-ru-json.401bc498.chunk.js +0 -1
  39. package/build/main.f13fc96c.js +0 -2856
  40. package/build/transfer-tokens-list-page.22147d2c.chunk.js +0 -16
  41. package/build/users-permissions-translation-zh-Hans-json.6ab714ee.chunk.js +0 -1
  42. package/build/zh-Hans-json.937b395b.chunk.js +0 -1
@@ -64,22 +64,16 @@ const WysiwygNav = ({
64
64
  </Select>
65
65
 
66
66
  <MainButtons>
67
- <CustomIconButton disabled id="Bold" label="Bold" name="Bold" icon={<Bold />} />
68
- <CustomIconButton disabled id="Italic" label="Italic" name="Italic" icon={<Italic />} />
69
- <CustomIconButton
70
- disabled
71
- id="Underline"
72
- label="Underline"
73
- name="Underline"
74
- icon={<Underline />}
75
- />
67
+ <CustomIconButton disabled label="Bold" name="Bold" icon={<Bold />} />
68
+ <CustomIconButton disabled label="Italic" name="Italic" icon={<Italic />} />
69
+ <CustomIconButton disabled label="Underline" name="Underline" icon={<Underline />} />
76
70
  </MainButtons>
77
71
 
78
- <MoreButton disabled id="more" label="More" icon={<More />} />
72
+ <MoreButton disabled label="More" icon={<More />} />
79
73
  </StyledFlex>
80
74
 
81
75
  {!isExpandMode && (
82
- <Button onClick={onTogglePreviewMode} variant="tertiary" id="preview">
76
+ <Button onClick={onTogglePreviewMode} variant="tertiary">
83
77
  {formatMessage({
84
78
  id: 'components.Wysiwyg.ToggleMode.markdown-mode',
85
79
  defaultMessage: 'Markdown mode',
@@ -110,21 +104,18 @@ const WysiwygNav = ({
110
104
  <MainButtons>
111
105
  <CustomIconButton
112
106
  onClick={() => onActionClick('Bold', editorRef)}
113
- id="Bold"
114
107
  label="Bold"
115
108
  name="Bold"
116
109
  icon={<Bold />}
117
110
  />
118
111
  <CustomIconButton
119
112
  onClick={() => onActionClick('Italic', editorRef)}
120
- id="Italic"
121
113
  label="Italic"
122
114
  name="Italic"
123
115
  icon={<Italic />}
124
116
  />
125
117
  <CustomIconButton
126
118
  onClick={() => onActionClick('Underline', editorRef)}
127
- id="Underline"
128
119
  label="Underline"
129
120
  name="Underline"
130
121
  icon={<Underline />}
@@ -134,37 +125,27 @@ const WysiwygNav = ({
134
125
  <MoreButton
135
126
  ref={buttonMoreRef}
136
127
  onClick={handleTogglePopover}
137
- id="more"
138
128
  label="More"
139
129
  icon={<More />}
140
130
  />
141
131
  {visiblePopover && (
142
- <Popover
143
- onDismiss={handleTogglePopover}
144
- centered
145
- source={buttonMoreRef}
146
- spacing={4}
147
- id="popover"
148
- >
132
+ <Popover onDismiss={handleTogglePopover} centered source={buttonMoreRef} spacing={4}>
149
133
  <Flex>
150
134
  <IconButtonGroupMargin>
151
135
  <CustomIconButton
152
136
  onClick={() => onActionClick('Strikethrough', editorRef, handleTogglePopover)}
153
- id="Strikethrough"
154
137
  label="Strikethrough"
155
138
  name="Strikethrough"
156
139
  icon={<StrikeThrough />}
157
140
  />
158
141
  <CustomIconButton
159
142
  onClick={() => onActionClick('BulletList', editorRef, handleTogglePopover)}
160
- id="BulletList"
161
143
  label="BulletList"
162
144
  name="BulletList"
163
145
  icon={<BulletList />}
164
146
  />
165
147
  <CustomIconButton
166
148
  onClick={() => onActionClick('NumberList', editorRef, handleTogglePopover)}
167
- id="NumberList"
168
149
  label="NumberList"
169
150
  name="NumberList"
170
151
  icon={<NumberList />}
@@ -173,7 +154,6 @@ const WysiwygNav = ({
173
154
  <IconButtonGroup>
174
155
  <CustomIconButton
175
156
  onClick={() => onActionClick('Code', editorRef, handleTogglePopover)}
176
- id="Code"
177
157
  label="Code"
178
158
  name="Code"
179
159
  icon={<Code />}
@@ -183,14 +163,12 @@ const WysiwygNav = ({
183
163
  handleTogglePopover();
184
164
  onToggleMediaLib();
185
165
  }}
186
- id="Image"
187
166
  label="Image"
188
167
  name="Image"
189
168
  icon={<Image />}
190
169
  />
191
170
  <CustomLinkIconButton
192
171
  onClick={() => onActionClick('Link', editorRef, handleTogglePopover)}
193
- id="Link"
194
172
  label="Link"
195
173
  name="Link"
196
174
  // eslint-disable-next-line jsx-a11y/anchor-is-valid
@@ -198,7 +176,6 @@ const WysiwygNav = ({
198
176
  />
199
177
  <CustomIconButton
200
178
  onClick={() => onActionClick('Quote', editorRef, handleTogglePopover)}
201
- id="Quote"
202
179
  label="Quote"
203
180
  name="Quote"
204
181
  icon={<Quote />}
@@ -210,7 +187,7 @@ const WysiwygNav = ({
210
187
  </StyledFlex>
211
188
 
212
189
  {onTogglePreviewMode && (
213
- <Button onClick={onTogglePreviewMode} variant="tertiary" id="preview">
190
+ <Button onClick={onTogglePreviewMode} variant="tertiary">
214
191
  {formatMessage({
215
192
  id: 'components.Wysiwyg.ToggleMode.preview-mode',
216
193
  defaultMessage: 'Preview mode',
@@ -6,9 +6,10 @@ import { resetPermissions, setPermissions } from './actions';
6
6
  import { selectCollectionTypePermissions, selectPermissions } from './selectors';
7
7
 
8
8
  const useSyncRbac = (query, collectionTypeUID, containerName = 'listView') => {
9
+ const dispatch = useDispatch();
10
+
9
11
  const collectionTypesRelatedPermissions = useSelector(selectCollectionTypePermissions);
10
12
  const permissions = useSelector(selectPermissions);
11
- const dispatch = useDispatch();
12
13
 
13
14
  const relatedPermissions = collectionTypesRelatedPermissions[collectionTypeUID];
14
15
 
@@ -24,7 +25,14 @@ const useSyncRbac = (query, collectionTypeUID, containerName = 'listView') => {
24
25
  return () => {};
25
26
  }, [relatedPermissions, dispatch, query, containerName]);
26
27
 
27
- return permissions;
28
+ // Check if the permissions are related to the current collectionTypeUID
29
+ const isPermissionMismatch =
30
+ permissions?.some((permission) => permission.subject !== collectionTypeUID) ?? true;
31
+
32
+ return {
33
+ isValid: permissions && !isPermissionMismatch,
34
+ permissions,
35
+ };
28
36
  };
29
37
 
30
38
  export default useSyncRbac;
@@ -12,7 +12,7 @@ import { useFetchContentTypeLayout } from '../../hooks';
12
12
  import { formatLayoutToApi } from '../../utils';
13
13
  import EditSettingsView from '../EditSettingsView';
14
14
  import EditViewLayoutManager from '../EditViewLayoutManager';
15
- import ListSettingsView from '../ListSettingsView';
15
+ import { ListSettingsView } from '../ListSettingsView';
16
16
  import ListViewLayout from '../ListViewLayoutManager';
17
17
 
18
18
  import ErrorFallback from './components/ErrorFallback';
@@ -16,7 +16,7 @@ const EditViewLayoutManager = ({ layout, ...rest }) => {
16
16
  const dispatch = useDispatch();
17
17
  const [{ query }] = useQueryParams();
18
18
  const { runHookWaterfall } = useStrapiApp();
19
- const permissions = useSyncRbac(query, rest.slug, 'editView');
19
+ const { permissions, isValid: isValidPermissions } = useSyncRbac(query, rest.slug, 'editView');
20
20
 
21
21
  useEffect(() => {
22
22
  // Allow the plugins to extend the edit view layout
@@ -29,7 +29,7 @@ const EditViewLayoutManager = ({ layout, ...rest }) => {
29
29
  };
30
30
  }, [layout, dispatch, query, runHookWaterfall]);
31
31
 
32
- if (!currentLayout || !permissions) {
32
+ if (!currentLayout || !isValidPermissions) {
33
33
  return <LoadingIndicatorPage />;
34
34
  }
35
35
 
@@ -1,4 +1,4 @@
1
- import React, { useContext, useReducer, useState } from 'react';
1
+ import * as React from 'react';
2
2
 
3
3
  import {
4
4
  Button,
@@ -9,13 +9,7 @@ import {
9
9
  Layout,
10
10
  Main,
11
11
  } from '@strapi/design-system';
12
- import {
13
- ConfirmDialog,
14
- Link,
15
- useFetchClient,
16
- useNotification,
17
- useTracking,
18
- } from '@strapi/helper-plugin';
12
+ import { Link, useFetchClient, useNotification, useTracking } from '@strapi/helper-plugin';
19
13
  import { ArrowLeft, Check } from '@strapi/icons';
20
14
  import isEqual from 'lodash/isEqual';
21
15
  import upperFirst from 'lodash/upperFirst';
@@ -34,16 +28,14 @@ import { SortDisplayedFields } from './components/SortDisplayedFields';
34
28
  import { EXCLUDED_SORT_ATTRIBUTE_TYPES } from './constants';
35
29
  import reducer, { initialState } from './reducer';
36
30
 
37
- const ListSettingsView = ({ layout, slug }) => {
31
+ export const ListSettingsView = ({ layout, slug }) => {
38
32
  const { put } = useFetchClient();
39
33
  const { formatMessage } = useIntl();
40
34
  const { trackUsage } = useTracking();
41
35
  const pluginsQueryParams = usePluginsQueryParams();
42
36
  const toggleNotification = useNotification();
43
- const { refetchData } = useContext(ModelsContext);
44
- const [showWarningSubmit, setWarningSubmit] = useState(false);
45
- const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
46
- const [{ fieldToEdit, fieldForm, initialData, modifiedData }, dispatch] = useReducer(
37
+ const { refetchData } = React.useContext(ModelsContext);
38
+ const [{ fieldToEdit, fieldForm, initialData, modifiedData }, dispatch] = React.useReducer(
47
39
  reducer,
48
40
  initialState,
49
41
  () => ({
@@ -101,16 +93,6 @@ const ListSettingsView = ({ layout, slug }) => {
101
93
  }
102
94
  );
103
95
 
104
- const handleConfirm = async () => {
105
- const { layouts, settings, metadatas } = modifiedData;
106
-
107
- mutate({
108
- layouts,
109
- settings,
110
- metadatas,
111
- });
112
- };
113
-
114
96
  const handleAddField = (item) => {
115
97
  dispatch({
116
98
  type: 'ADD_FIELD',
@@ -134,9 +116,17 @@ const ListSettingsView = ({ layout, slug }) => {
134
116
  }
135
117
  };
136
118
 
137
- const handleSubmit = (e) => {
138
- e.preventDefault();
139
- toggleWarningSubmit();
119
+ const handleSubmit = (event) => {
120
+ event.preventDefault();
121
+
122
+ const { layouts, settings, metadatas } = modifiedData;
123
+
124
+ mutate({
125
+ layouts,
126
+ settings,
127
+ metadatas,
128
+ });
129
+
140
130
  trackUsage('willSaveContentTypeLayout');
141
131
  };
142
132
 
@@ -253,19 +243,6 @@ const ListSettingsView = ({ layout, slug }) => {
253
243
  />
254
244
  </Flex>
255
245
  </ContentLayout>
256
-
257
- <ConfirmDialog
258
- bodyText={{
259
- id: getTrad('popUpWarning.warning.updateAllSettings'),
260
- defaultMessage: 'This will modify all your settings',
261
- }}
262
- iconRightButton={<Check />}
263
- isConfirmButtonLoading={isSubmittingForm}
264
- isOpen={showWarningSubmit}
265
- onToggleDialog={toggleWarningSubmit}
266
- onConfirm={handleConfirm}
267
- variantRightButton="success-light"
268
- />
269
246
  </form>
270
247
 
271
248
  {isModalFormOpen && (
@@ -305,5 +282,3 @@ ListSettingsView.propTypes = {
305
282
  }).isRequired,
306
283
  slug: PropTypes.string.isRequired,
307
284
  };
308
-
309
- export default ListSettingsView;
@@ -180,6 +180,22 @@ function ListView({
180
180
  data: { results, pagination: paginationResult },
181
181
  } = await fetchClient.get(endPoint, options);
182
182
 
183
+ // If user enters a page number that doesn't exist, redirect him to the last page
184
+ if (paginationResult.page > paginationResult.pageCount && paginationResult.pageCount > 0) {
185
+ const query = {
186
+ ...params,
187
+ page: paginationResult.pageCount,
188
+ };
189
+
190
+ push({
191
+ pathname,
192
+ state: { from: pathname },
193
+ search: stringify(query),
194
+ });
195
+
196
+ return;
197
+ }
198
+
183
199
  notifyStatus(
184
200
  formatMessage(
185
201
  {
@@ -219,7 +235,17 @@ function ListView({
219
235
  });
220
236
  }
221
237
  },
222
- [formatMessage, getData, getDataSucceeded, notifyStatus, push, toggleNotification, fetchClient]
238
+ [
239
+ formatMessage,
240
+ getData,
241
+ getDataSucceeded,
242
+ notifyStatus,
243
+ push,
244
+ toggleNotification,
245
+ fetchClient,
246
+ params,
247
+ pathname,
248
+ ]
223
249
  );
224
250
 
225
251
  const handleConfirmDeleteAllData = React.useCallback(
@@ -14,7 +14,7 @@ const ListViewLayout = ({ layout, ...props }) => {
14
14
  const dispatch = useDispatch();
15
15
  const { replace } = useHistory();
16
16
  const [{ query, rawQuery }] = useQueryParams();
17
- const permissions = useSyncRbac(query, props.slug, 'listView');
17
+ const { permissions, isValid: isValidPermissions } = useSyncRbac(query, props.slug, 'listView');
18
18
  const redirectionLink = useFindRedirectionLink(props.slug);
19
19
 
20
20
  useEffect(() => {
@@ -33,7 +33,7 @@ const ListViewLayout = ({ layout, ...props }) => {
33
33
  };
34
34
  }, [dispatch]);
35
35
 
36
- if (!permissions) {
36
+ if (!isValidPermissions) {
37
37
  return null;
38
38
  }
39
39
 
@@ -70,7 +70,6 @@ const MarketPlacePage = () => {
70
70
  id: 'admin.pages.MarketPlacePage.production',
71
71
  defaultMessage: 'Manage plugins from the development environment',
72
72
  },
73
- blockTransition: true,
74
73
  });
75
74
  }
76
75
  }, [toggleNotification, isInDevelopmentMode]);
@@ -1,11 +1,12 @@
1
- import React, { useEffect, useRef } from 'react';
1
+ import * as React from 'react';
2
2
 
3
- import { Button, ContentLayout, HeaderLayout, Main } from '@strapi/design-system';
3
+ import { ContentLayout, HeaderLayout, Main } from '@strapi/design-system';
4
4
  import {
5
5
  LinkButton,
6
6
  NoContent,
7
7
  NoPermissions,
8
8
  SettingsPageTitle,
9
+ useAPIErrorHandler,
9
10
  useFetchClient,
10
11
  useFocusWhenNavigate,
11
12
  useGuidedTour,
@@ -38,16 +39,17 @@ const ApiTokenListView = () => {
38
39
  const { push } = useHistory();
39
40
  const { trackUsage } = useTracking();
40
41
  const { startSection } = useGuidedTour();
41
- const startSectionRef = useRef(startSection);
42
+ const startSectionRef = React.useRef(startSection);
42
43
  const { get, del } = useFetchClient();
44
+ const { formatAPIError } = useAPIErrorHandler();
43
45
 
44
- useEffect(() => {
46
+ React.useEffect(() => {
45
47
  if (startSectionRef.current) {
46
48
  startSectionRef.current('apiTokens');
47
49
  }
48
50
  }, []);
49
51
 
50
- useEffect(() => {
52
+ React.useEffect(() => {
51
53
  push({ search: qs.stringify({ sort: 'name:ASC' }, { encode: false }) });
52
54
  }, [push]);
53
55
 
@@ -59,16 +61,13 @@ const ApiTokenListView = () => {
59
61
  },
60
62
  }));
61
63
 
62
- const {
63
- data: apiTokens,
64
- status,
65
- isFetching,
66
- } = useQuery(
64
+ const { data: apiTokens, isLoading: isLoadingTokens } = useQuery(
67
65
  ['api-tokens'],
68
66
  async () => {
69
67
  trackUsage('willAccessTokenList', {
70
68
  tokenType: API_TOKEN_TYPE,
71
69
  });
70
+
72
71
  const {
73
72
  data: { data },
74
73
  } = await get(`/admin/api-tokens`);
@@ -78,19 +77,18 @@ const ApiTokenListView = () => {
78
77
  return data;
79
78
  },
80
79
  {
80
+ cacheTime: 0,
81
81
  enabled: canRead,
82
- onError() {
82
+ onError(error) {
83
83
  toggleNotification({
84
84
  type: 'warning',
85
- message: { id: 'notification.error', defaultMessage: 'An error occured' },
85
+ message: formatAPIError(error),
86
86
  });
87
87
  },
88
88
  }
89
89
  );
90
90
 
91
- const isLoading =
92
- canRead &&
93
- ((status !== 'success' && status !== 'error') || (status === 'success' && isFetching));
91
+ const isLoading = isLoadingTokens;
94
92
 
95
93
  const deleteMutation = useMutation(
96
94
  async (id) => {
@@ -101,25 +99,20 @@ const ApiTokenListView = () => {
101
99
  await queryClient.invalidateQueries(['api-tokens']);
102
100
  trackUsage('didDeleteToken');
103
101
  },
104
- onError(err) {
105
- if (err?.response?.data?.data) {
106
- toggleNotification({ type: 'warning', message: err.response.data.data });
107
- } else {
108
- toggleNotification({
109
- type: 'warning',
110
- message: { id: 'notification.error', defaultMessage: 'An error occured' },
111
- });
112
- }
102
+ onError(error) {
103
+ toggleNotification({ type: 'warning', message: formatAPIError(error) });
113
104
  },
114
105
  }
115
106
  );
116
107
 
117
- const shouldDisplayDynamicTable = canRead && apiTokens;
118
- const shouldDisplayNoContent = canRead && !apiTokens && !canCreate;
119
- const shouldDisplayNoContentWithCreationButton = canRead && !apiTokens && canCreate;
108
+ const hasApiTokens = apiTokens && apiTokens.length > 0;
109
+ const shouldDisplayDynamicTable = canRead && hasApiTokens;
110
+ const shouldDisplayNoContent = canRead && !hasApiTokens && !canCreate;
111
+ const shouldDisplayNoContentWithCreationButton = canRead && !hasApiTokens && canCreate;
120
112
 
121
113
  return (
122
114
  <Main aria-busy={isLoading}>
115
+ {/* TODO: this needs to be translated */}
123
116
  <SettingsPageTitle name="API Tokens" />
124
117
  <HeaderLayout
125
118
  title={formatMessage({ id: 'Settings.apiTokens.title', defaultMessage: 'API Tokens' })}
@@ -128,7 +121,7 @@ const ApiTokenListView = () => {
128
121
  defaultMessage: 'List of generated tokens to consume the API',
129
122
  })}
130
123
  primaryAction={
131
- canCreate ? (
124
+ canCreate && (
132
125
  <LinkButton
133
126
  data-testid="create-api-token-button"
134
127
  startIcon={<Plus />}
@@ -145,7 +138,7 @@ const ApiTokenListView = () => {
145
138
  defaultMessage: 'Create new API Token',
146
139
  })}
147
140
  </LinkButton>
148
- ) : undefined
141
+ )
149
142
  }
150
143
  />
151
144
  <ContentLayout>
@@ -169,12 +162,12 @@ const ApiTokenListView = () => {
169
162
  defaultMessage: 'Add your first API Token',
170
163
  }}
171
164
  action={
172
- <Button variant="secondary" startIcon={<Plus />}>
165
+ <LinkButton variant="secondary" startIcon={<Plus />} to="/settings/api-tokens/create">
173
166
  {formatMessage({
174
167
  id: 'Settings.apiTokens.addNewToken',
175
168
  defaultMessage: 'Add new API Token',
176
169
  })}
177
- </Button>
170
+ </LinkButton>
178
171
  }
179
172
  />
180
173
  )}
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
2
 
3
- import { Button, ContentLayout, HeaderLayout, Main } from '@strapi/design-system';
3
+ import { ContentLayout, HeaderLayout, Main } from '@strapi/design-system';
4
4
  import {
5
5
  LinkButton,
6
6
  NoContent,
@@ -136,9 +136,10 @@ const TransferTokenListView = () => {
136
136
  }
137
137
  );
138
138
 
139
- const shouldDisplayDynamicTable = canRead && transferTokens;
140
- const shouldDisplayNoContent = canRead && !transferTokens && !canCreate;
141
- const shouldDisplayNoContentWithCreationButton = canRead && !transferTokens && canCreate;
139
+ const hasTransferTokens = transferTokens && transferTokens?.length > 0;
140
+ const shouldDisplayDynamicTable = canRead && hasTransferTokens;
141
+ const shouldDisplayNoContent = canRead && !hasTransferTokens && !canCreate;
142
+ const shouldDisplayNoContentWithCreationButton = canRead && !hasTransferTokens && canCreate;
142
143
 
143
144
  return (
144
145
  <Main aria-busy={isLoading}>
@@ -194,12 +195,16 @@ const TransferTokenListView = () => {
194
195
  defaultMessage: 'Add your first Transfer Token',
195
196
  }}
196
197
  action={
197
- <Button variant="secondary" startIcon={<Plus />}>
198
+ <LinkButton
199
+ variant="secondary"
200
+ startIcon={<Plus />}
201
+ to="/settings/transfer-tokens/create"
202
+ >
198
203
  {formatMessage({
199
204
  id: 'Settings.transferTokens.addNewToken',
200
205
  defaultMessage: 'Add new Transfer Token',
201
206
  })}
202
- </Button>
207
+ </LinkButton>
203
208
  }
204
209
  />
205
210
  )}
@@ -71,6 +71,8 @@ const EditPage = ({ canUpdate }) => {
71
71
  } = useAdminUsers(
72
72
  { id },
73
73
  {
74
+ cacheTime: 0,
75
+
74
76
  onError(error) {
75
77
  const { status } = error.response;
76
78
 
@@ -52,6 +52,7 @@ export const UserListPageCE = () => {
52
52
  isLoading,
53
53
  refetch: refetchAdminUsers,
54
54
  } = useAdminUsers(qs.parse(search, { ignoreQueryPrefix: true }), {
55
+ cacheTime: 0,
55
56
  enabled: canRead,
56
57
  });
57
58
  const CreateAction = useEnterprise(