@strapi/admin 4.12.4 → 4.12.6

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 (187) hide show
  1. package/admin/src/StrapiApp.js +1 -1
  2. package/admin/src/components/AuthenticatedApp.js +229 -0
  3. package/admin/src/components/GuidedTour/Modal/index.js +1 -3
  4. package/admin/src/content-manager/components/Wysiwyg/WysiwygNav.js +7 -30
  5. package/admin/src/content-manager/hooks/useSyncRbac/index.js +10 -2
  6. package/admin/src/content-manager/pages/App/index.js +5 -16
  7. package/admin/src/content-manager/pages/CollectionTypeRecursivePath/index.js +1 -1
  8. package/admin/src/content-manager/pages/EditView/Information/index.js +1 -1
  9. package/admin/src/content-manager/pages/EditViewLayoutManager/index.js +2 -2
  10. package/admin/src/content-manager/pages/ListSettingsView/index.js +16 -41
  11. package/admin/src/content-manager/pages/ListView/components/BulkActionButtons/SelectedEntriesModal/index.js +2 -2
  12. package/admin/src/content-manager/pages/ListView/components/TableRows/index.js +1 -1
  13. package/admin/src/content-manager/pages/ListView/index.js +27 -1
  14. package/admin/src/content-manager/pages/ListViewLayoutManager/index.js +2 -2
  15. package/admin/src/hooks/useSettingsForm/index.js +14 -3
  16. package/admin/src/hooks/useSettingsMenu/index.js +2 -2
  17. package/admin/src/hooks/useSettingsMenu/utils/formatLinks.js +1 -3
  18. package/admin/src/hooks/useSettingsMenu/utils/sortLinks.js +1 -3
  19. package/admin/src/index.js +1 -1
  20. package/admin/src/pages/Admin/Onboarding/index.js +1 -3
  21. package/admin/src/pages/Admin/index.js +80 -74
  22. package/admin/src/pages/App/constants.js +1 -1
  23. package/admin/src/pages/App/index.js +160 -122
  24. package/admin/src/pages/AuthPage/index.js +2 -4
  25. package/admin/src/pages/HomePage/index.js +1 -3
  26. package/admin/src/pages/InstalledPluginsPage/index.js +1 -3
  27. package/admin/src/pages/{InternalErrorPage/index.js → InternalErrorPage.js} +3 -4
  28. package/admin/src/pages/MarketplacePage/index.js +0 -1
  29. package/admin/src/pages/{NotFoundPage/index.js → NotFoundPage.js} +1 -3
  30. package/admin/src/pages/ProfilePage/index.js +2 -4
  31. package/admin/src/pages/SettingsPage/components/SettingsNav/index.js +3 -3
  32. package/admin/src/pages/SettingsPage/constants.js +67 -132
  33. package/admin/src/pages/SettingsPage/index.js +31 -36
  34. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +1 -1
  35. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +24 -31
  36. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js +69 -35
  37. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/index.js +1 -1
  38. package/admin/src/pages/SettingsPage/pages/TransferTokens/ListView/index.js +11 -6
  39. package/admin/src/pages/SettingsPage/pages/Users/EditPage/index.js +4 -1
  40. package/admin/src/pages/SettingsPage/pages/Users/ListPage/DynamicTable/TableRows/index.js +1 -1
  41. package/admin/src/pages/SettingsPage/pages/Users/ListPage/index.js +1 -0
  42. package/admin/src/pages/UseCasePage.js +174 -0
  43. package/admin/src/translations/zh-Hans.json +918 -902
  44. package/admin/src/utils/createRoute.js +5 -7
  45. package/admin/src/utils/formatAPIErrors.js +1 -3
  46. package/admin/src/utils/getFullName.js +1 -1
  47. package/admin/src/utils/sortLinks.js +1 -3
  48. package/admin/src/utils/uniqueAdminHash.js +2 -9
  49. package/build/{1049.f76cb14b.chunk.js → 1049.9d69d231.chunk.js} +1 -1
  50. package/build/1504.eff012f7.chunk.js +95 -0
  51. package/build/2166.c837469a.chunk.js +1 -0
  52. package/build/2225.33287e1b.chunk.js +79 -0
  53. package/build/2237.03792b63.chunk.js +114 -0
  54. package/build/2379.401f56f3.chunk.js +1 -0
  55. package/build/2395.e6a79fbb.chunk.js +26 -0
  56. package/build/{9806.3392505e.chunk.js → 2747.d1442a90.chunk.js} +78 -70
  57. package/build/2801.31393ffe.chunk.js +1 -0
  58. package/build/3483.8517171f.chunk.js +1 -0
  59. package/build/4546.7a3c0d03.chunk.js +1 -0
  60. package/build/502.8dd074ff.chunk.js +1 -0
  61. package/build/5483.5bfbb00d.chunk.js +6 -0
  62. package/build/7464.592a9295.chunk.js +1 -0
  63. package/build/748.fd2e5afd.chunk.js +105 -0
  64. package/build/773.6381d62d.chunk.js +18 -0
  65. package/build/7826.399afe81.chunk.js +103 -0
  66. package/build/8261.2525d35c.chunk.js +7 -0
  67. package/build/8276.e519a707.chunk.js +26 -0
  68. package/build/8299.62b67c72.chunk.js +1 -0
  69. package/build/Admin-AuthPage.90d64342.chunk.js +35 -0
  70. package/build/Admin-AuthenticatedApp.379ac945.chunk.js +24 -0
  71. package/build/Admin-UseCasePage.1f757db5.chunk.js +13 -0
  72. package/build/Admin_GuidedTourModal.8ccf1fbc.chunk.js +12 -0
  73. package/build/Admin_InternalErrorPage.9de92c6d.chunk.js +9 -0
  74. package/build/Admin_NotFoundPage.21620424.chunk.js +9 -0
  75. package/build/Admin_Onboarding.dbfa32f6.chunk.js +43 -0
  76. package/build/Admin_homePage.2000cbe9.chunk.js +86 -0
  77. package/build/Admin_marketplace.ec80e29b.chunk.js +63 -0
  78. package/build/Admin_pluginsPage.0c6851f8.chunk.js +14 -0
  79. package/build/Admin_profilePage.78cd8495.chunk.js +21 -0
  80. package/build/Admin_settingsPage.1760c3ce.chunk.js +119 -0
  81. package/build/StrapiApp.221fac30.chunk.js +5 -0
  82. package/build/{admin-edit-roles-page.6d567273.chunk.js → admin-edit-roles-page.24bdf746.chunk.js} +1 -1
  83. package/build/admin-edit-users.5d10d444.chunk.js +10 -0
  84. package/build/admin-users.2b3e4305.chunk.js +11 -0
  85. package/build/api-tokens-list-page.0af7d431.chunk.js +16 -0
  86. package/build/audit-logs-settings-page.0f73ccf8.chunk.js +1 -0
  87. package/build/content-manager.fb0833bd.chunk.js +1099 -0
  88. package/build/{content-type-builder.40534de5.chunk.js → content-type-builder.66066281.chunk.js} +18 -18
  89. package/build/email-settings-page.2f7e35c0.chunk.js +11 -0
  90. package/build/i18n-translation-ru-json.a3dbc125.chunk.js +1 -0
  91. package/build/index.html +1 -1
  92. package/build/main.ee3c1938.js +2859 -0
  93. package/build/review-workflows-settings-create-view.d24a32b9.chunk.js +1 -0
  94. package/build/review-workflows-settings-edit-view.6044b022.chunk.js +1 -0
  95. package/build/review-workflows-settings-list-view.3f0ef4bc.chunk.js +56 -0
  96. package/build/runtime~main.397ee447.js +2 -0
  97. package/build/{sso-settings-page.12b6d8ae.chunk.js → sso-settings-page.4dba0670.chunk.js} +1 -1
  98. package/build/transfer-tokens-list-page.d6986b03.chunk.js +16 -0
  99. package/build/users-advanced-settings-page.17052d72.chunk.js +9 -0
  100. package/build/users-email-settings-page.3de8ea50.chunk.js +9 -0
  101. package/build/users-permissions-translation-zh-Hans-json.8d82c809.chunk.js +1 -0
  102. package/build/users-providers-settings-page.0eaa916d.chunk.js +14 -0
  103. package/build/users-roles-settings-page.957ad48b.chunk.js +55 -0
  104. package/build/webhook-edit-page.665210af.chunk.js +33 -0
  105. package/build/zh-Hans-json.97efd015.chunk.js +1 -0
  106. package/ee/admin/hooks/useAuthProviders.js +25 -0
  107. package/ee/admin/hooks/{useLicenseLimitNotification/index.js → useLicenseLimitNotification.js} +2 -4
  108. package/ee/admin/hooks/{useLicenseLimits/useLicenseLimits.js → useLicenseLimits.js} +4 -1
  109. package/ee/admin/pages/App/constants.js +6 -5
  110. package/ee/admin/pages/AuthPage/components/Login/index.js +8 -4
  111. package/ee/admin/pages/AuthPage/components/Providers/index.js +8 -5
  112. package/ee/admin/pages/HomePage/index.js +1 -1
  113. package/ee/admin/pages/SettingsPage/constants.js +27 -42
  114. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js +1 -1
  115. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js +1 -1
  116. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js +1 -1
  117. package/ee/admin/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js +1 -1
  118. package/ee/admin/pages/SettingsPage/pages/Users/ListPage/index.js +1 -1
  119. package/package.json +10 -10
  120. package/admin/src/components/AuthenticatedApp/index.js +0 -118
  121. package/admin/src/components/AuthenticatedApp/utils/api.js +0 -85
  122. package/admin/src/components/AuthenticatedApp/utils/checkLatestStrapiVersion.js +0 -11
  123. package/admin/src/components/PluginsInitializer/index.js +0 -68
  124. package/admin/src/components/PluginsInitializer/init.js +0 -11
  125. package/admin/src/components/PluginsInitializer/reducer.js +0 -22
  126. package/admin/src/layouts/AppLayout/index.js +0 -33
  127. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/api.js +0 -23
  128. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/prefixAllUrls.js +0 -17
  129. package/admin/src/pages/SettingsPage/utils/createSectionsRoutes.js +0 -11
  130. package/admin/src/pages/SettingsPage/utils/getSectionsToDisplay.js +0 -5
  131. package/admin/src/pages/SettingsPage/utils/index.js +0 -2
  132. package/admin/src/pages/UseCasePage/index.js +0 -175
  133. package/admin/src/utils/checkFormValidity.js +0 -15
  134. package/admin/src/utils/getAttributesToDisplay.js +0 -19
  135. package/admin/src/utils/getExistingActions.js +0 -32
  136. package/admin/src/utils/index.js +0 -9
  137. package/admin/src/utils/makeUniqueRoutes.js +0 -6
  138. package/build/1386.879bcd90.chunk.js +0 -7
  139. package/build/2225.c6244756.chunk.js +0 -79
  140. package/build/2379.f1641312.chunk.js +0 -1
  141. package/build/2395.46f8d0c1.chunk.js +0 -26
  142. package/build/2801.5cef5ec8.chunk.js +0 -1
  143. package/build/3483.03c24f96.chunk.js +0 -1
  144. package/build/3739.63e352f1.chunk.js +0 -103
  145. package/build/3929.5632f24d.chunk.js +0 -114
  146. package/build/448.829e1344.chunk.js +0 -1
  147. package/build/4546.cfafae68.chunk.js +0 -1
  148. package/build/502.8ae8ef60.chunk.js +0 -1
  149. package/build/5483.6dd2e776.chunk.js +0 -6
  150. package/build/5542.2415a393.chunk.js +0 -63
  151. package/build/6691.4985ef22.chunk.js +0 -105
  152. package/build/7464.3e64a1d5.chunk.js +0 -1
  153. package/build/8276.10a3f883.chunk.js +0 -26
  154. package/build/9944.7af075a5.chunk.js +0 -26
  155. package/build/Admin-authenticatedApp.f5ece8ff.chunk.js +0 -79
  156. package/build/Admin_InternalErrorPage.f45f2462.chunk.js +0 -1
  157. package/build/Admin_homePage.ac9dfb86.chunk.js +0 -81
  158. package/build/Admin_marketplace.dde9c148.chunk.js +0 -55
  159. package/build/Admin_pluginsPage.bbe79434.chunk.js +0 -6
  160. package/build/Admin_profilePage.192edc52.chunk.js +0 -13
  161. package/build/Admin_settingsPage.97cb9d41.chunk.js +0 -111
  162. package/build/admin-app.91898385.chunk.js +0 -36
  163. package/build/admin-edit-users.79eeb125.chunk.js +0 -10
  164. package/build/admin-users.123aa08e.chunk.js +0 -11
  165. package/build/api-tokens-list-page.505bf7e0.chunk.js +0 -16
  166. package/build/audit-logs-settings-page.4b422831.chunk.js +0 -1
  167. package/build/content-manager.2af15f57.chunk.js +0 -1099
  168. package/build/email-settings-page.d494d1eb.chunk.js +0 -11
  169. package/build/i18n-translation-ru-json.401bc498.chunk.js +0 -1
  170. package/build/main.f13fc96c.js +0 -2856
  171. package/build/review-workflows-settings-create-view.cb08cfa2.chunk.js +0 -1
  172. package/build/review-workflows-settings-edit-view.3c7cbe63.chunk.js +0 -1
  173. package/build/review-workflows-settings-list-view.1611dc1f.chunk.js +0 -56
  174. package/build/runtime~main.bb4efc54.js +0 -2
  175. package/build/transfer-tokens-list-page.22147d2c.chunk.js +0 -16
  176. package/build/users-advanced-settings-page.f0760eb8.chunk.js +0 -9
  177. package/build/users-email-settings-page.ff4b32f3.chunk.js +0 -9
  178. package/build/users-permissions-translation-zh-Hans-json.6ab714ee.chunk.js +0 -1
  179. package/build/users-providers-settings-page.48de0306.chunk.js +0 -14
  180. package/build/users-roles-settings-page.3f9f063e.chunk.js +0 -30
  181. package/build/webhook-edit-page.6cb479ff.chunk.js +0 -33
  182. package/build/zh-Hans-json.937b395b.chunk.js +0 -1
  183. package/ee/admin/hooks/index.js +0 -4
  184. package/ee/admin/hooks/useAuthProviders/index.js +0 -50
  185. package/ee/admin/hooks/useAuthProviders/reducer.js +0 -26
  186. package/ee/admin/hooks/useLicenseLimits/index.js +0 -1
  187. /package/ee/admin/hooks/{useLicenseLimits/__mocks__/index.js → __mocks__/useLicenseLimits.js} +0 -0
@@ -1,9 +1,14 @@
1
1
  import { useEffect, useReducer } from 'react';
2
2
 
3
- import { useFetchClient, useNotification, useOverlayBlocker } from '@strapi/helper-plugin';
3
+ import {
4
+ getYupInnerErrors,
5
+ useFetchClient,
6
+ useNotification,
7
+ useOverlayBlocker,
8
+ } from '@strapi/helper-plugin';
4
9
  import omit from 'lodash/omit';
5
10
 
6
- import { checkFormValidity, formatAPIErrors } from '../../utils';
11
+ import { formatAPIErrors } from '../../utils/formatAPIErrors';
7
12
 
8
13
  import init from './init';
9
14
  import { initialState, reducer } from './reducer';
@@ -76,7 +81,13 @@ const useSettingsForm = (endPoint, schema, cbSuccess, fieldsToPick) => {
76
81
  const handleSubmit = async (e) => {
77
82
  e.preventDefault();
78
83
 
79
- const errors = await checkFormValidity(modifiedData, schema);
84
+ let errors = null;
85
+
86
+ try {
87
+ await schema.validate(modifiedData, { abortEarly: false });
88
+ } catch (err) {
89
+ errors = getYupInnerErrors(err);
90
+ }
80
91
 
81
92
  dispatch({
82
93
  type: 'SET_ERRORS',
@@ -7,8 +7,8 @@ import { selectAdminPermissions } from '../../pages/App/selectors';
7
7
  import { useEnterprise } from '../useEnterprise';
8
8
 
9
9
  import { LINKS_CE } from './constants';
10
- import formatLinks from './utils/formatLinks';
11
- import sortLinks from './utils/sortLinks';
10
+ import { formatLinks } from './utils/formatLinks';
11
+ import { sortLinks } from './utils/sortLinks';
12
12
 
13
13
  const useSettingsMenu = () => {
14
14
  const [{ isLoading, menu }, setData] = useState({
@@ -1,4 +1,4 @@
1
- const formatLinks = (menu) => {
1
+ export const formatLinks = (menu) => {
2
2
  return menu.map((menuSection) => {
3
3
  const formattedLinks = menuSection.links.map((link) => ({
4
4
  ...link,
@@ -8,5 +8,3 @@ const formatLinks = (menu) => {
8
8
  return { ...menuSection, links: formattedLinks };
9
9
  });
10
10
  };
11
-
12
- export default formatLinks;
@@ -1,5 +1,3 @@
1
1
  import sortBy from 'lodash/sortBy';
2
2
 
3
- const sortLinks = (links) => sortBy(links, (link) => link.id);
4
-
5
- export default sortLinks;
3
+ export const sortLinks = (links) => sortBy(links, (link) => link.id);
@@ -52,7 +52,7 @@ const run = async () => {
52
52
 
53
53
  // We need to make sure to fetch the project type before importing the StrapiApp
54
54
  // otherwise the strapi-babel-plugin does not work correctly
55
- const StrapiApp = await import(/* webpackChunkName: "admin-app" */ './StrapiApp');
55
+ const StrapiApp = await import(/* webpackChunkName: "StrapiApp" */ './StrapiApp');
56
56
 
57
57
  const app = StrapiApp.default({
58
58
  appPlugins: plugins,
@@ -75,7 +75,7 @@ const TextLink = styled(TypographyLineHeight)`
75
75
  }
76
76
  `;
77
77
 
78
- const Onboarding = () => {
78
+ export const Onboarding = () => {
79
79
  const triggerRef = useRef();
80
80
  const [isOpen, setIsOpen] = useState(false);
81
81
  const { formatMessage } = useIntl();
@@ -222,5 +222,3 @@ const Onboarding = () => {
222
222
  </Box>
223
223
  );
224
224
  };
225
-
226
- export default Onboarding;
@@ -4,80 +4,92 @@
4
4
  *
5
5
  */
6
6
 
7
- import React, { lazy, Suspense, useEffect, useMemo } from 'react';
7
+ import * as React from 'react';
8
8
 
9
+ import { Box, Flex } from '@strapi/design-system';
9
10
  import { LoadingIndicatorPage, useStrapiApp, useTracking } from '@strapi/helper-plugin';
10
11
  import { DndProvider } from 'react-dnd';
11
12
  import { HTML5Backend } from 'react-dnd-html5-backend';
12
13
  import { useDispatch, useSelector } from 'react-redux';
13
14
  import { Route, Switch } from 'react-router-dom';
14
15
 
15
- import GuidedTourModal from '../../components/GuidedTour/Modal';
16
16
  import LeftMenu from '../../components/LeftMenu';
17
- import { useConfigurations, useMenu } from '../../hooks';
18
- import AppLayout from '../../layouts/AppLayout';
19
- import { createRoute } from '../../utils';
17
+ import useConfigurations from '../../hooks/useConfigurations';
18
+ import useMenu from '../../hooks/useMenu';
19
+ import { createRoute } from '../../utils/createRoute';
20
20
  import { SET_APP_RUNTIME_STATUS } from '../App/constants';
21
21
 
22
- import Onboarding from './Onboarding';
23
-
24
- const CM = lazy(() =>
25
- import(/* webpackChunkName: "content-manager" */ '../../content-manager/pages/App')
22
+ const CM = React.lazy(() =>
23
+ import(/* webpackChunkName: "content-manager" */ '../../content-manager/pages/App').then(
24
+ (module) => ({ default: module.ContentManger })
25
+ )
26
26
  );
27
- const HomePage = lazy(() => import(/* webpackChunkName: "Admin_homePage" */ '../HomePage'));
28
- const InstalledPluginsPage = lazy(() =>
29
- import(/* webpackChunkName: "Admin_pluginsPage" */ '../InstalledPluginsPage')
27
+ const GuidedTourModal = React.lazy(() =>
28
+ import(/* webpackChunkName: "Admin_GuidedTourModal" */ '../../components/GuidedTour/Modal').then(
29
+ (module) => ({ default: module.GuidedTourModal })
30
+ )
30
31
  );
31
- const MarketplacePage = lazy(() =>
32
- import(/* webpackChunkName: "Admin_marketplace" */ '../MarketplacePage')
32
+ const HomePage = React.lazy(() =>
33
+ import(/* webpackChunkName: "Admin_homePage" */ '../HomePage').then((module) => ({
34
+ default: module.HomePage,
35
+ }))
33
36
  );
34
- const NotFoundPage = lazy(() =>
35
- import(/* webpackChunkName: "Admin_NotFoundPage" */ '../NotFoundPage')
37
+ const InstalledPluginsPage = React.lazy(() =>
38
+ import(/* webpackChunkName: "Admin_pluginsPage" */ '../InstalledPluginsPage').then((module) => ({
39
+ default: module.PluginsPage,
40
+ }))
36
41
  );
37
- const InternalErrorPage = lazy(() =>
38
- import(/* webpackChunkName: "Admin_InternalErrorPage" */ '../InternalErrorPage')
42
+ const MarketplacePage = React.lazy(() =>
43
+ import(/* webpackChunkName: "Admin_marketplace" */ '../MarketplacePage')
39
44
  );
40
-
41
- const ProfilePage = lazy(() =>
42
- import(/* webpackChunkName: "Admin_profilePage" */ '../ProfilePage')
45
+ const Onboarding = React.lazy(() =>
46
+ import(/* webpackChunkName: "Admin_Onboarding" */ './Onboarding').then((module) => ({
47
+ default: module.Onboarding,
48
+ }))
49
+ );
50
+ const ProfilePage = React.lazy(() =>
51
+ import(/* webpackChunkName: "Admin_profilePage" */ '../ProfilePage').then((module) => ({
52
+ default: module.ProfilePage,
53
+ }))
43
54
  );
44
- const SettingsPage = lazy(() =>
45
- import(/* webpackChunkName: "Admin_settingsPage" */ '../SettingsPage')
55
+ const SettingsPage = React.lazy(() =>
56
+ import(/* webpackChunkName: "Admin_settingsPage" */ '../SettingsPage').then((module) => ({
57
+ default: module.SettingsPage,
58
+ }))
46
59
  );
47
60
 
48
- // Simple hook easier for testing
49
- /**
50
- * TODO: remove this, it's bad.
51
- */
52
- const useTrackUsage = () => {
61
+ export const Admin = () => {
62
+ const { isLoading, generalSectionLinks, pluginsSectionLinks } = useMenu();
63
+ const { menu } = useStrapiApp();
64
+ const { showTutorials } = useConfigurations();
53
65
  const { trackUsage } = useTracking();
54
66
  const dispatch = useDispatch();
55
67
  const appStatus = useSelector((state) => state.admin_app.status);
56
68
 
57
- useEffect(() => {
69
+ React.useEffect(() => {
58
70
  // Make sure the event is only send once after accessing the admin panel
59
71
  // and not at runtime for example when regenerating the permissions with the ctb
60
72
  // or with i18n
61
73
  if (appStatus === 'init') {
62
74
  trackUsage('didAccessAuthenticatedAdministration');
63
-
64
75
  dispatch({ type: SET_APP_RUNTIME_STATUS });
65
76
  }
66
- // eslint-disable-next-line react-hooks/exhaustive-deps
67
- }, [appStatus]);
68
- };
77
+ }, [appStatus, dispatch, trackUsage]);
69
78
 
70
- const Admin = () => {
71
- useTrackUsage();
72
- const { isLoading, generalSectionLinks, pluginsSectionLinks } = useMenu();
73
- const { menu } = useStrapiApp();
74
- const { showTutorials } = useConfigurations();
79
+ const routes = menu
80
+ .filter((link) => link.Component)
75
81
 
76
- const routes = useMemo(() => {
77
- return menu
78
- .filter((link) => link.Component)
79
- .map(({ to, Component, exact }) => createRoute(Component, to, exact));
80
- }, [menu]);
82
+ /**
83
+ * `Component` is an async function, which is passed as property of the
84
+ * addMenuLink() API during the plugin registration step.
85
+ *
86
+ * Because of that we can't just render <Route component={Component} />,
87
+ * but have to await the function.
88
+ *
89
+ * This isn't a good React pattern and should be reconsidered.
90
+ */
91
+
92
+ .map(({ to, Component, exact }) => createRoute(Component, to, exact));
81
93
 
82
94
  if (isLoading) {
83
95
  return <LoadingIndicatorPage />;
@@ -85,40 +97,34 @@ const Admin = () => {
85
97
 
86
98
  return (
87
99
  <DndProvider backend={HTML5Backend}>
88
- <AppLayout
89
- sideNav={
90
- <LeftMenu
91
- generalSectionLinks={generalSectionLinks}
92
- pluginsSectionLinks={pluginsSectionLinks}
93
- />
94
- }
95
- >
96
- <Suspense fallback={<LoadingIndicatorPage />}>
97
- <Switch>
98
- <Route path="/" component={HomePage} exact />
99
- <Route path="/me" component={ProfilePage} exact />
100
- <Route path="/content-manager" component={CM} />
101
- {routes}
102
- <Route path="/settings/:settingId" component={SettingsPage} />
103
- <Route path="/settings" component={SettingsPage} exact />
104
- <Route path="/marketplace">
105
- <MarketplacePage />
106
- </Route>
107
- <Route path="/list-plugins" exact>
108
- <InstalledPluginsPage />
109
- </Route>
110
- <Route path="/404" component={NotFoundPage} />
111
- <Route path="/500" component={InternalErrorPage} />
112
- <Route path="" component={NotFoundPage} />
113
- </Switch>
114
- </Suspense>
100
+ <Flex alignItems="stretch">
101
+ <LeftMenu
102
+ generalSectionLinks={generalSectionLinks}
103
+ pluginsSectionLinks={pluginsSectionLinks}
104
+ />
105
+
106
+ <Box flex="1">
107
+ <React.Suspense fallback={<LoadingIndicatorPage />}>
108
+ <Switch>
109
+ <Route path="/" component={HomePage} exact />
110
+ <Route path="/me" component={ProfilePage} exact />
111
+ <Route path="/content-manager" component={CM} />
112
+ {routes}
113
+ <Route path="/settings/:settingId" component={SettingsPage} />
114
+ <Route path="/settings" component={SettingsPage} exact />
115
+ <Route path="/marketplace" component={MarketplacePage} />
116
+ <Route path="/list-plugins" component={InstalledPluginsPage} exact />
117
+ </Switch>
118
+ </React.Suspense>
119
+ </Box>
120
+
121
+ {/* TODO: we should move the logic to determine whether the guided tour is displayed
122
+ or not out of the component, to make the code-splitting more effective
123
+ */}
115
124
  <GuidedTourModal />
116
125
 
117
126
  {showTutorials && <Onboarding />}
118
- </AppLayout>
127
+ </Flex>
119
128
  </DndProvider>
120
129
  );
121
130
  };
122
-
123
- export default Admin;
124
- export { useTrackUsage };
@@ -1,4 +1,4 @@
1
1
  export const SET_APP_RUNTIME_STATUS = 'StrapiAdmin/APP/SET_APP_RUNTIME_STATUS';
2
2
  export const SET_ADMIN_PERMISSIONS = 'StrapiAdmin/App/SET_ADMIN_PERMISSIONS';
3
3
 
4
- export const ROUTES_CE = [];
4
+ export const AUTH_ROUTES_CE = [];
@@ -1,10 +1,4 @@
1
- /**
2
- *
3
- * App.js
4
- *
5
- */
6
-
7
- import React, { lazy, Suspense, useEffect, useMemo, useState } from 'react';
1
+ import * as React from 'react';
8
2
 
9
3
  import { SkipToContent } from '@strapi/design-system';
10
4
  import {
@@ -12,31 +6,57 @@ import {
12
6
  LoadingIndicatorPage,
13
7
  prefixFileUrlWithBackendUrl,
14
8
  TrackingProvider,
15
- useAppInfo,
16
9
  useFetchClient,
17
- useNotification,
18
10
  } from '@strapi/helper-plugin';
19
11
  import merge from 'lodash/merge';
20
12
  import { useIntl } from 'react-intl';
13
+ import { useQueries } from 'react-query';
21
14
  import { useDispatch } from 'react-redux';
22
15
  import { Route, Switch } from 'react-router-dom';
23
16
 
24
17
  import PrivateRoute from '../../components/PrivateRoute';
25
18
  import { ADMIN_PERMISSIONS_CE } from '../../constants';
26
- import { useConfigurations } from '../../hooks';
19
+ import useConfigurations from '../../hooks/useConfigurations';
27
20
  import { useEnterprise } from '../../hooks/useEnterprise';
28
- import { createRoute, makeUniqueRoutes } from '../../utils';
29
- import AuthPage from '../AuthPage';
30
- import NotFoundPage from '../NotFoundPage';
31
- import UseCasePage from '../UseCasePage';
32
21
 
33
- import { ROUTES_CE, SET_ADMIN_PERMISSIONS } from './constants';
22
+ import { AUTH_ROUTES_CE, SET_ADMIN_PERMISSIONS } from './constants';
23
+
24
+ const AuthPage = React.lazy(() =>
25
+ import(/* webpackChunkName: "Admin-AuthPage" */ '../AuthPage').then((module) => ({
26
+ default: module.AuthPage,
27
+ }))
28
+ );
29
+
30
+ const AuthenticatedApp = React.lazy(() =>
31
+ import(/* webpackChunkName: "Admin-AuthenticatedApp" */ '../../components/AuthenticatedApp').then(
32
+ (module) => ({ default: module.AuthenticatedApp })
33
+ )
34
+ );
34
35
 
35
- const AuthenticatedApp = lazy(() =>
36
- import(/* webpackChunkName: "Admin-authenticatedApp" */ '../../components/AuthenticatedApp')
36
+ const UseCasePage = React.lazy(() =>
37
+ import(/* webpackChunkName: "Admin-UseCasePage" */ '../UseCasePage').then((module) => ({
38
+ default: module.UseCasePage,
39
+ }))
37
40
  );
38
41
 
39
- function App() {
42
+ const NotFoundPage = React.lazy(() =>
43
+ import(/* webpackChunkName: "Admin_NotFoundPage" */ '../NotFoundPage').then((module) => ({
44
+ default: module.NotFoundPage,
45
+ }))
46
+ );
47
+
48
+ const InternalErrorPage = React.lazy(() =>
49
+ import(/* webpackChunkName: "Admin_InternalErrorPage" */ '../InternalErrorPage').then(
50
+ (module) => ({ default: module.InternalErrorPage })
51
+ )
52
+ );
53
+
54
+ export function App() {
55
+ const { updateProjectSettings } = useConfigurations();
56
+ const { formatMessage } = useIntl();
57
+ const dispatch = useDispatch();
58
+ const { get, post } = useFetchClient();
59
+
40
60
  const adminPermissions = useEnterprise(
41
61
  ADMIN_PERMISSIONS_CE,
42
62
  async () => (await import('../../../../ee/admin/constants')).ADMIN_PERMISSIONS_EE,
@@ -49,155 +69,173 @@ function App() {
49
69
  defaultValue: ADMIN_PERMISSIONS_CE,
50
70
  }
51
71
  );
52
- const routes = useEnterprise(
53
- ROUTES_CE,
54
- async () => (await import('../../../../ee/admin/pages/App/constants')).ROUTES_EE,
72
+
73
+ // Load authentication routes for CE and EE (SSO)
74
+ const authRoutes = useEnterprise(
75
+ AUTH_ROUTES_CE,
76
+ async () => (await import('../../../../ee/admin/pages/App/constants')).AUTH_ROUTES_EE,
55
77
  {
56
78
  defaultValue: [],
57
79
  }
58
80
  );
59
- const toggleNotification = useNotification();
60
- const { updateProjectSettings } = useConfigurations();
61
- const { formatMessage } = useIntl();
62
- const [{ isLoading, hasAdmin, uuid, deviceId }, setState] = useState({
63
- isLoading: true,
81
+
82
+ // TODO: this should be moved to redux
83
+ const [{ hasAdmin, uuid }, setState] = React.useState({
64
84
  hasAdmin: false,
85
+ uuid: undefined,
65
86
  });
66
- const dispatch = useDispatch();
67
- const appInfo = useAppInfo();
68
- const { get, post } = useFetchClient();
69
-
70
- const authRoutes = useMemo(() => {
71
- return makeUniqueRoutes(
72
- routes.map(({ to, Component, exact }) => createRoute(Component, to, exact))
73
- );
74
- }, [routes]);
75
-
76
- const [telemetryProperties, setTelemetryProperties] = useState(null);
77
87
 
78
- useEffect(() => {
88
+ // Store permissions in redux
89
+ React.useEffect(() => {
79
90
  dispatch({ type: SET_ADMIN_PERMISSIONS, payload: adminPermissions });
80
91
  }, [adminPermissions, dispatch]);
81
92
 
82
- useEffect(() => {
83
- const currentToken = auth.getToken();
84
-
85
- const renewToken = async () => {
86
- try {
93
+ const [
94
+ { data: token, error: errorRenewToken },
95
+ { data: initData, isLoading: isLoadingInit },
96
+ { data: telemetryProperties },
97
+ ] = useQueries([
98
+ {
99
+ queryKey: 'renew-token',
100
+ async queryFn() {
87
101
  const {
88
102
  data: {
89
103
  data: { token },
90
104
  },
91
- } = await post('/admin/renew-token', { token: currentToken });
92
- auth.updateToken(token);
93
- } catch (err) {
94
- // Refresh app
95
- auth.clearAppStorage();
96
- window.location.reload();
97
- }
98
- };
99
-
100
- if (currentToken) {
101
- renewToken();
102
- }
103
- }, [post]);
105
+ } = await post('/admin/renew-token', { token: auth.getToken() });
106
+
107
+ return token;
108
+ },
109
+
110
+ enabled: !!auth.getToken(),
111
+ },
104
112
 
105
- useEffect(() => {
106
- const getData = async () => {
107
- try {
113
+ {
114
+ queryKey: 'init',
115
+ async queryFn() {
108
116
  const {
109
- data: {
110
- data: { hasAdmin, uuid, menuLogo, authLogo },
111
- },
117
+ data: { data },
112
118
  } = await get(`/admin/init`);
113
119
 
114
- updateProjectSettings({
115
- menuLogo: prefixFileUrlWithBackendUrl(menuLogo),
116
- authLogo: prefixFileUrlWithBackendUrl(authLogo),
117
- });
120
+ return data;
121
+ },
122
+ },
118
123
 
119
- if (uuid) {
120
- const {
121
- data: { data: properties },
122
- } = await get(`/admin/telemetry-properties`, {
123
- // NOTE: needed because the interceptors of the fetchClient redirect to /login when receive a 401 and it would end up in an infinite loop when the user doesn't have a session.
124
- validateStatus: (status) => status < 500,
125
- });
126
-
127
- setTelemetryProperties(properties);
128
-
129
- try {
130
- const event = 'didInitializeAdministration';
131
- await post(
132
- 'https://analytics.strapi.io/api/v2/track',
133
- {
134
- // This event is anonymous
135
- event,
136
- userId: '',
137
- deviceId,
138
- eventPropeties: {},
139
- userProperties: { environment: appInfo.currentEnvironment },
140
- groupProperties: { ...properties, projectId: uuid },
141
- },
142
- {
143
- headers: {
144
- 'X-Strapi-Event': event,
145
- },
146
- }
147
- );
148
- } catch (e) {
149
- // Silent.
150
- }
151
- }
152
-
153
- setState({ isLoading: false, hasAdmin, uuid, deviceId });
154
- } catch (err) {
155
- toggleNotification({
156
- type: 'warning',
157
- message: { id: 'app.containers.App.notification.error.init' },
124
+ {
125
+ queryKey: 'telemetry-properties',
126
+ async queryFn() {
127
+ const {
128
+ data: { data },
129
+ } = await get(`/admin/telemetry-properties`, {
130
+ // NOTE: needed because the interceptors of the fetchClient redirect to /login when receive a
131
+ // 401 and it would end up in an infinite loop when the user doesn't have a session.
132
+ validateStatus: (status) => status < 500,
158
133
  });
159
- }
160
- };
161
134
 
162
- getData();
163
- // eslint-disable-next-line react-hooks/exhaustive-deps
164
- }, [toggleNotification, updateProjectSettings]);
135
+ return data;
136
+ },
137
+
138
+ enabled: !!auth.getToken(),
139
+ },
140
+ ]);
141
+
142
+ React.useEffect(() => {
143
+ // If the renew token could not be fetched, logout the user
144
+ if (errorRenewToken) {
145
+ auth.clearAppStorage();
146
+ window.location.reload();
147
+ } else if (token) {
148
+ auth.updateToken(token);
149
+ }
150
+ }, [errorRenewToken, token]);
151
+
152
+ // Store the fetched project settings (e.g. logos)
153
+ // TODO: this should be moved to redux
154
+ React.useEffect(() => {
155
+ if (!isLoadingInit && initData) {
156
+ updateProjectSettings({
157
+ menuLogo: prefixFileUrlWithBackendUrl(initData.menuLogo),
158
+ authLogo: prefixFileUrlWithBackendUrl(initData.authLogo),
159
+ });
160
+
161
+ // TODO: this should be stored in redux
162
+ setState((prev) => ({
163
+ ...prev,
164
+ hasAdmin: initData.hasAdmin,
165
+ uuid: initData.uuid,
166
+ }));
167
+ }
168
+ }, [initData, isLoadingInit, updateProjectSettings]);
169
+
170
+ // We can't use useTracking here, because `App` is not wrapped in the tracking provider
171
+ // context, which we can't do because the context values contain data that can only be
172
+ // accessed when a user is logged in.
173
+ // This should not use `useFetchClient`, because it does not communicate to the admin API.
174
+ React.useEffect(() => {
175
+ async function trackInitEvent() {
176
+ await fetch('https://analytics.strapi.io/api/v2/track', {
177
+ body: JSON.stringify({
178
+ event: 'didInitializeAdministration',
179
+ // This event is anonymous
180
+ userId: '',
181
+ eventPropeties: {},
182
+ userProperties: {},
183
+ groupProperties: { ...telemetryProperties, projectId: uuid },
184
+ }),
185
+
186
+ headers: {
187
+ 'Content-Type': 'application/json',
188
+ 'X-Strapi-Event': 'didInitializeAdministration',
189
+ },
190
+
191
+ method: 'POST',
192
+ });
193
+ }
165
194
 
166
- const setHasAdmin = (hasAdmin) => setState((prev) => ({ ...prev, hasAdmin }));
195
+ if (uuid) {
196
+ trackInitEvent();
197
+ }
198
+ }, [telemetryProperties, uuid]);
167
199
 
168
- const trackingInfo = useMemo(
200
+ const trackingContext = React.useMemo(
169
201
  () => ({
170
202
  uuid,
171
203
  telemetryProperties,
172
- deviceId,
173
204
  }),
174
- [uuid, telemetryProperties, deviceId]
205
+ [uuid, telemetryProperties]
175
206
  );
176
207
 
177
- if (isLoading) {
208
+ if (isLoadingInit) {
178
209
  return <LoadingIndicatorPage />;
179
210
  }
180
211
 
181
212
  return (
182
- <Suspense fallback={<LoadingIndicatorPage />}>
213
+ <React.Suspense fallback={<LoadingIndicatorPage />}>
183
214
  <SkipToContent>{formatMessage({ id: 'skipToContent' })}</SkipToContent>
184
- <TrackingProvider value={trackingInfo}>
215
+ <TrackingProvider value={trackingContext}>
185
216
  <Switch>
186
- {authRoutes}
217
+ {authRoutes.map(({ path, component }) => (
218
+ <Route key={path} path={path} component={component} exact />
219
+ ))}
220
+
187
221
  <Route
188
222
  path="/auth/:authType"
189
223
  render={(routerProps) => (
190
- <AuthPage {...routerProps} setHasAdmin={setHasAdmin} hasAdmin={hasAdmin} />
224
+ <AuthPage
225
+ {...routerProps}
226
+ setHasAdmin={(hasAdmin) => setState((prev) => ({ ...prev, hasAdmin }))}
227
+ hasAdmin={hasAdmin}
228
+ />
191
229
  )}
192
230
  exact
193
231
  />
194
232
  <PrivateRoute path="/usecase" component={UseCasePage} />
195
233
  <PrivateRoute path="/" component={AuthenticatedApp} />
234
+ <Route path="/404" component={NotFoundPage} />
235
+ <Route path="/500" component={InternalErrorPage} />
196
236
  <Route path="" component={NotFoundPage} />
197
237
  </Switch>
198
238
  </TrackingProvider>
199
- </Suspense>
239
+ </React.Suspense>
200
240
  );
201
241
  }
202
-
203
- export default App;