@strapi/admin 4.14.4 → 4.14.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 (235) hide show
  1. package/admin/.eslintrc.js +12 -0
  2. package/admin/custom.d.ts +20 -0
  3. package/admin/src/StrapiApp.js +14 -16
  4. package/admin/src/components/AuthenticatedApp/index.js +4 -4
  5. package/admin/src/components/ConfigurationProvider.tsx +67 -0
  6. package/admin/src/components/LanguageProvider.tsx +129 -0
  7. package/admin/src/components/{LeftMenu/index.js → LeftMenu.tsx} +23 -18
  8. package/admin/src/components/{NpsSurvey/index.js → NpsSurvey.tsx} +68 -21
  9. package/admin/src/components/PluginsInitializer.tsx +124 -0
  10. package/admin/src/components/Providers/index.js +6 -6
  11. package/admin/src/components/UnauthenticatedLogo.tsx +2 -2
  12. package/admin/src/components/{LocalesProvider/__mocks__/useLocalesProvider.js → __mocks__/LanguageProvider.js} +1 -1
  13. package/admin/src/content-manager/components/BlocksEditor/BlocksInput/index.js +48 -8
  14. package/admin/src/content-manager/components/BlocksEditor/Toolbar/index.js +146 -68
  15. package/admin/src/content-manager/components/BlocksEditor/hooks/useBlocksStore.js +50 -47
  16. package/admin/src/content-manager/components/BlocksEditor/hooks/useModifiersStore.js +11 -9
  17. package/admin/src/content-manager/components/BlocksEditor/index.js +1 -14
  18. package/admin/src/contexts/admin.ts +18 -0
  19. package/admin/src/contexts/configuration.ts +14 -4
  20. package/admin/src/hooks/__mocks__/useConfigurations.ts +2 -2
  21. package/admin/src/hooks/index.js +0 -3
  22. package/admin/src/hooks/{useAdminRoles/index.js → useAdminRoles.ts} +26 -10
  23. package/admin/src/hooks/useAdminUsers.ts +64 -0
  24. package/admin/src/hooks/useConfiguration.ts +5 -0
  25. package/admin/src/hooks/{useEnterprise/useEnterprise.js → useEnterprise.ts} +15 -5
  26. package/admin/src/hooks/useMenu.ts +153 -0
  27. package/admin/src/index.js +5 -2
  28. package/admin/src/layouts/UnauthenticatedLayout/LocaleToggle/index.js +2 -2
  29. package/admin/src/pages/Admin/index.js +4 -3
  30. package/admin/src/pages/App/index.js +5 -3
  31. package/admin/src/pages/AuthPage/components/Register/index.js +28 -33
  32. package/admin/src/pages/AuthPage/index.js +2 -2
  33. package/admin/src/pages/ProfilePage/index.js +2 -2
  34. package/admin/src/pages/SettingsPage/components/Tokens/Table/index.js +119 -87
  35. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/CustomizationInfos/index.js +2 -2
  36. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js +2 -2
  37. package/admin/src/pages/SettingsPage/pages/Roles/CreatePage/index.js +1 -1
  38. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/index.js +1 -1
  39. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js +1 -1
  40. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/index.js +1 -1
  41. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/index.js +1 -1
  42. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/index.js +1 -1
  43. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PermissionsDataManagerProvider/index.js +1 -1
  44. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/index.js +1 -1
  45. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/hooks/usePermissionsDataManager.ts +28 -0
  46. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/index.js +1 -1
  47. package/admin/src/{hooks/useAdminRolePermissions/index.js → pages/SettingsPage/pages/Roles/hooks/useAdminRolePermissions.ts} +13 -6
  48. package/admin/src/pages/SettingsPage/pages/TransferTokens/ListView/index.js +3 -5
  49. package/admin/src/shared/hooks/index.js +0 -1
  50. package/admin/src/shared/hooks/useInjectionZone/index.js +2 -2
  51. package/admin/src/types/adminAPI.ts +29 -0
  52. package/admin/src/utils/createRoute.js +4 -1
  53. package/admin/tsconfig.build.json +4 -0
  54. package/admin/tsconfig.json +7 -1
  55. package/build/{1049.f7aed23d.chunk.js → 1049.9236e785.chunk.js} +1 -1
  56. package/build/1222.fe92c653.chunk.js +35 -0
  57. package/build/{1227.f9c74718.chunk.js → 1227.e0f7447b.chunk.js} +1 -1
  58. package/build/135.ad267b59.chunk.js +1 -0
  59. package/build/{1386.6b8819c6.chunk.js → 1386.07f2bbb3.chunk.js} +1 -1
  60. package/build/1835.eaa696ba.chunk.js +1 -0
  61. package/build/{2225.d1bcf7e3.chunk.js → 2225.a2147b8f.chunk.js} +2 -2
  62. package/build/2325.d705b39a.chunk.js +1 -0
  63. package/build/2379.b0bc4013.chunk.js +1 -0
  64. package/build/{2395.aca6ce66.chunk.js → 2395.d37b1025.chunk.js} +1 -1
  65. package/build/2421.79e5b3d0.chunk.js +1 -0
  66. package/build/267.073a3bcb.chunk.js +1 -0
  67. package/build/2801.12522720.chunk.js +1 -0
  68. package/build/2878.145ebf7c.chunk.js +1 -0
  69. package/build/2950.216f2e89.chunk.js +1 -0
  70. package/build/2953.284a63c0.chunk.js +1 -0
  71. package/build/{8743.31c921b1.chunk.js → 3019.0d74d080.chunk.js} +123 -139
  72. package/build/3021.33ad47fb.chunk.js +103 -0
  73. package/build/{3483.5df8e010.chunk.js → 3483.8f1b25f8.chunk.js} +1 -1
  74. package/build/3911.488fbde3.chunk.js +95 -0
  75. package/build/{4174.df9aa09a.chunk.js → 4174.2c4f958e.chunk.js} +1 -1
  76. package/build/4429.7f044dc7.chunk.js +1 -0
  77. package/build/4555.c883d697.chunk.js +1 -0
  78. package/build/4663.b906cc10.chunk.js +1 -0
  79. package/build/4916.480053a6.chunk.js +1 -0
  80. package/build/4996.d285c30b.chunk.js +1 -0
  81. package/build/502.b845473a.chunk.js +1 -0
  82. package/build/5858.493b31ec.chunk.js +1 -0
  83. package/build/6345.334e7678.chunk.js +1 -0
  84. package/build/6373.1a21d665.chunk.js +105 -0
  85. package/build/6453.4160b5b7.chunk.js +1 -0
  86. package/build/7448.6fd14dd3.chunk.js +1 -0
  87. package/build/7464.91341b4f.chunk.js +1 -0
  88. package/build/7735.9e7c9fdd.chunk.js +10 -0
  89. package/build/782.7243b183.chunk.js +1 -0
  90. package/build/7849.2a500ed8.chunk.js +1 -0
  91. package/build/7897.dffa5ad5.chunk.js +6 -0
  92. package/build/8162.7d1100a0.chunk.js +1 -0
  93. package/build/{8276.d4426fd8.chunk.js → 8276.e9698944.chunk.js} +2 -2
  94. package/build/8894.5ca4852a.chunk.js +26 -0
  95. package/build/8980.f0045cc1.chunk.js +1 -0
  96. package/build/9153.42c1428a.chunk.js +1 -0
  97. package/build/{9218.8bc01ab9.chunk.js → 9218.306ad178.chunk.js} +1 -1
  98. package/build/9285.5f174057.chunk.js +1 -0
  99. package/build/9302.550cf5b7.chunk.js +146 -0
  100. package/build/9547.62987774.chunk.js +1 -0
  101. package/build/9754.b4e73779.chunk.js +1 -0
  102. package/build/Admin-authenticatedApp.e0bf203f.chunk.js +79 -0
  103. package/build/{Admin_InternalErrorPage.b66ee9c1.chunk.js → Admin_InternalErrorPage.e2431a95.chunk.js} +1 -1
  104. package/build/Admin_homePage.71ef8d06.chunk.js +81 -0
  105. package/build/{Admin_marketplace.31b962b8.chunk.js → Admin_marketplace.0db78604.chunk.js} +1 -1
  106. package/build/{Admin_pluginsPage.9217101d.chunk.js → Admin_pluginsPage.1083f7f0.chunk.js} +1 -1
  107. package/build/{Admin_profilePage.680123d9.chunk.js → Admin_profilePage.61704b7d.chunk.js} +2 -2
  108. package/build/Admin_settingsPage.39cb9fca.chunk.js +111 -0
  109. package/build/{Upload_ConfigureTheView.b40eea4d.chunk.js → Upload_ConfigureTheView.3cfeb108.chunk.js} +1 -1
  110. package/build/admin-app.06f5e70a.chunk.js +69 -0
  111. package/build/admin-edit-roles-page.556fac52.chunk.js +267 -0
  112. package/build/admin-edit-users.64fd1318.chunk.js +10 -0
  113. package/build/admin-roles-list.15918328.chunk.js +22 -0
  114. package/build/admin-users.74fddc87.chunk.js +11 -0
  115. package/build/{api-tokens-create-page.0dd63e91.chunk.js → api-tokens-create-page.c08ae118.chunk.js} +1 -1
  116. package/build/{api-tokens-edit-page.78d877f8.chunk.js → api-tokens-edit-page.ce18efdc.chunk.js} +1 -1
  117. package/build/api-tokens-list-page.783b7569.chunk.js +16 -0
  118. package/build/audit-logs-settings-page.12aeea8c.chunk.js +1 -0
  119. package/build/content-manager.2e3f660b.chunk.js +1220 -0
  120. package/build/{content-type-builder-list-view.3fffae65.chunk.js → content-type-builder-list-view.38ed3935.chunk.js} +7 -7
  121. package/build/{content-type-builder.98c71164.chunk.js → content-type-builder.758a9d23.chunk.js} +4 -4
  122. package/build/email-settings-page.e08a587e.chunk.js +11 -0
  123. package/build/{i18n-settings-page.a9708926.chunk.js → i18n-settings-page.3186e3e9.chunk.js} +1 -1
  124. package/build/index.html +1 -1
  125. package/build/main.00ea6f5a.js +2665 -0
  126. package/build/{review-workflows-settings-create-view.b7b0c6c5.chunk.js → review-workflows-settings-create-view.5cdc4d64.chunk.js} +1 -1
  127. package/build/{review-workflows-settings-edit-view.c331b3fe.chunk.js → review-workflows-settings-edit-view.53bf7865.chunk.js} +1 -1
  128. package/build/review-workflows-settings-list-view.b4a8aefb.chunk.js +56 -0
  129. package/build/runtime~main.be0e0649.js +2 -0
  130. package/build/sso-settings-page.6a35d473.chunk.js +1 -0
  131. package/build/{transfer-tokens-create-page.e7f541d3.chunk.js → transfer-tokens-create-page.2662d519.chunk.js} +1 -1
  132. package/build/{transfer-tokens-edit-page.bd1276c2.chunk.js → transfer-tokens-edit-page.f64d8d8c.chunk.js} +1 -1
  133. package/build/transfer-tokens-list-page.e6fd5f87.chunk.js +16 -0
  134. package/build/{upload-settings.97ef4c92.chunk.js → upload-settings.450a1de0.chunk.js} +1 -1
  135. package/build/{upload.f08715a1.chunk.js → upload.0d53e7a3.chunk.js} +1 -1
  136. package/build/{users-advanced-settings-page.36a3c363.chunk.js → users-advanced-settings-page.4a1f1f6d.chunk.js} +1 -1
  137. package/build/users-email-settings-page.ea81fe82.chunk.js +9 -0
  138. package/build/users-providers-settings-page.10280cdb.chunk.js +14 -0
  139. package/build/{users-roles-settings-page.d5a8e8a1.chunk.js → users-roles-settings-page.4a7158be.chunk.js} +1 -1
  140. package/build/{webhook-edit-page.87456194.chunk.js → webhook-edit-page.65ac30ee.chunk.js} +2 -2
  141. package/build/{webhook-list-page.c88a382b.chunk.js → webhook-list-page.f57285ca.chunk.js} +1 -1
  142. package/jest.config.front.js +4 -0
  143. package/package.json +23 -17
  144. package/scripts/build.js +1 -1
  145. package/server/controllers/admin.js +3 -2
  146. package/shared/entities.ts +33 -0
  147. package/shared/permissions.ts +52 -0
  148. package/admin/src/components/ConfigurationsProvider/index.js +0 -66
  149. package/admin/src/components/ConfigurationsProvider/reducer.js +0 -29
  150. package/admin/src/components/LanguageProvider/index.js +0 -54
  151. package/admin/src/components/LanguageProvider/init.js +0 -13
  152. package/admin/src/components/LanguageProvider/reducer.js +0 -30
  153. package/admin/src/components/LanguageProvider/utils/localStorageKey.js +0 -3
  154. package/admin/src/components/LocalesProvider/context.js +0 -5
  155. package/admin/src/components/LocalesProvider/index.js +0 -21
  156. package/admin/src/components/LocalesProvider/useLocalesProvider.js +0 -11
  157. package/admin/src/components/NpsSurvey/hooks/useNpsSurveySettings.js +0 -17
  158. package/admin/src/components/PluginsInitializer/index.js +0 -68
  159. package/admin/src/components/PluginsInitializer/init.js +0 -11
  160. package/admin/src/components/PluginsInitializer/reducer.js +0 -22
  161. package/admin/src/contexts/Admin/index.js +0 -5
  162. package/admin/src/contexts/MarketPlace/index.js +0 -18
  163. package/admin/src/contexts/PermisssionsDataManagerContext/index.js +0 -5
  164. package/admin/src/contexts/index.js +0 -3
  165. package/admin/src/hooks/useAdminRoles/__mocks__/index.js +0 -5
  166. package/admin/src/hooks/useAdminUsers/index.js +0 -1
  167. package/admin/src/hooks/useAdminUsers/useAdminUsers.js +0 -47
  168. package/admin/src/hooks/useConfigurations.ts +0 -5
  169. package/admin/src/hooks/useEnterprise/index.js +0 -1
  170. package/admin/src/hooks/useMenu/index.js +0 -86
  171. package/admin/src/hooks/useMenu/utils/checkPermissions.js +0 -13
  172. package/admin/src/hooks/useMenu/utils/getGeneralLinks.js +0 -31
  173. package/admin/src/hooks/useMenu/utils/getPluginSectionLinks.js +0 -17
  174. package/admin/src/hooks/usePermissionsDataManager/index.js +0 -7
  175. package/admin/src/shared/hooks/useAdminProvider/index.js +0 -11
  176. package/build/2224.8af54440.chunk.js +0 -138
  177. package/build/2379.f0baf826.chunk.js +0 -1
  178. package/build/2421.a478ba24.chunk.js +0 -105
  179. package/build/2801.c49f88a1.chunk.js +0 -1
  180. package/build/3911.d4fada48.chunk.js +0 -95
  181. package/build/412.72afdf0c.chunk.js +0 -689
  182. package/build/502.8666bbef.chunk.js +0 -25
  183. package/build/5702.5b433d50.chunk.js +0 -1
  184. package/build/6186.c33ce082.chunk.js +0 -116
  185. package/build/6715.48e37308.chunk.js +0 -1
  186. package/build/6812.00ef5b0d.chunk.js +0 -26
  187. package/build/7464.43a4527c.chunk.js +0 -1
  188. package/build/7818.d2196a53.chunk.js +0 -29
  189. package/build/7897.5c03247b.chunk.js +0 -25
  190. package/build/8690.33243bba.chunk.js +0 -38
  191. package/build/Admin-authenticatedApp.27545a1b.chunk.js +0 -112
  192. package/build/Admin_homePage.a6281dd6.chunk.js +0 -124
  193. package/build/Admin_settingsPage.33378310.chunk.js +0 -111
  194. package/build/admin-app.e8c52c37.chunk.js +0 -36
  195. package/build/admin-edit-roles-page.fcf056bf.chunk.js +0 -275
  196. package/build/admin-edit-users.89efe3c4.chunk.js +0 -10
  197. package/build/admin-roles-list.8b77704a.chunk.js +0 -22
  198. package/build/admin-users.e3f1be14.chunk.js +0 -19
  199. package/build/api-tokens-list-page.ae13346c.chunk.js +0 -16
  200. package/build/audit-logs-settings-page.e9c92a75.chunk.js +0 -9
  201. package/build/content-manager.5849dbe3.chunk.js +0 -1226
  202. package/build/email-settings-page.ecfec9b3.chunk.js +0 -11
  203. package/build/email-translation-ar-json.88304564.chunk.js +0 -1
  204. package/build/email-translation-cs-json.6eaeec6a.chunk.js +0 -1
  205. package/build/email-translation-de-json.1b334230.chunk.js +0 -1
  206. package/build/email-translation-dk-json.85402492.chunk.js +0 -1
  207. package/build/email-translation-en-json.4211d4d0.chunk.js +0 -1
  208. package/build/email-translation-es-json.0b6b1006.chunk.js +0 -1
  209. package/build/email-translation-fr-json.78be2787.chunk.js +0 -1
  210. package/build/email-translation-id-json.c97239fe.chunk.js +0 -1
  211. package/build/email-translation-it-json.a2ed8c78.chunk.js +0 -1
  212. package/build/email-translation-ja-json.63eebd02.chunk.js +0 -1
  213. package/build/email-translation-ko-json.4de49b23.chunk.js +0 -1
  214. package/build/email-translation-ms-json.7390477e.chunk.js +0 -1
  215. package/build/email-translation-nl-json.377bdd9f.chunk.js +0 -1
  216. package/build/email-translation-pl-json.97d0db97.chunk.js +0 -1
  217. package/build/email-translation-pt-BR-json.81cca553.chunk.js +0 -1
  218. package/build/email-translation-pt-json.2a2a0643.chunk.js +0 -1
  219. package/build/email-translation-ru-json.6bce37dd.chunk.js +0 -1
  220. package/build/email-translation-sk-json.53da2fcd.chunk.js +0 -1
  221. package/build/email-translation-th-json.660fa9a8.chunk.js +0 -1
  222. package/build/email-translation-tr-json.e6c0f8fc.chunk.js +0 -1
  223. package/build/email-translation-uk-json.bd1fb6bf.chunk.js +0 -1
  224. package/build/email-translation-vi-json.9fb7e6d7.chunk.js +0 -1
  225. package/build/email-translation-zh-Hans-json.c6841563.chunk.js +0 -1
  226. package/build/email-translation-zh-json.7a2232ea.chunk.js +0 -1
  227. package/build/main.3abb6f34.js +0 -3278
  228. package/build/review-workflows-settings-list-view.70218dc1.chunk.js +0 -75
  229. package/build/runtime~main.450561b1.js +0 -2
  230. package/build/sso-settings-page.1a9e7f8f.chunk.js +0 -1
  231. package/build/transfer-tokens-list-page.5de6bb9f.chunk.js +0 -16
  232. package/build/users-email-settings-page.47b47962.chunk.js +0 -149
  233. package/build/users-providers-settings-page.1e0c8376.chunk.js +0 -154
  234. /package/admin/src/hooks/{useAdminUsers/__mocks__/index.js → __mocks__/useAdminUsers.ts} +0 -0
  235. /package/admin/src/{hooks/useAdminRolePermissions/__mocks__/index.js → pages/SettingsPage/pages/Roles/hooks/__mocks__/useAdminRolePermissions.ts} +0 -0
@@ -12,5 +12,17 @@ module.exports = {
12
12
  files: ['**/*.ts', '**/*.tsx'],
13
13
  extends: ['custom/front/typescript'],
14
14
  },
15
+ {
16
+ files: ['./tests/*', '**/*.test.*'],
17
+ env: {
18
+ jest: true,
19
+ },
20
+ rules: {
21
+ /**
22
+ * So we can do `import { render } from '@tests/utils'`
23
+ */
24
+ 'import/no-unresolved': 'off',
25
+ },
26
+ },
15
27
  ],
16
28
  };
package/admin/custom.d.ts CHANGED
@@ -6,3 +6,23 @@ declare module 'styled-components' {
6
6
  // eslint-disable-next-line @typescript-eslint/no-empty-interface
7
7
  export interface DefaultTheme extends StrapiTheme {}
8
8
  }
9
+
10
+ declare global {
11
+ interface Window {
12
+ strapi: {
13
+ backendURL: string;
14
+ isEE: boolean;
15
+ features: {
16
+ SSO: 'sso';
17
+ AUDIT_LOGS: 'audit-logs';
18
+ REVIEW_WORKFLOWS: 'review-workflows';
19
+ isEnabled: (featureName?: string) => boolean;
20
+ };
21
+ flags: {
22
+ nps?: boolean;
23
+ };
24
+ projectType: 'Community' | 'Enterprise';
25
+ telemetryDisabled: boolean;
26
+ };
27
+ }
28
+ }
@@ -9,7 +9,7 @@ import { Helmet } from 'react-helmet';
9
9
  import { BrowserRouter } from 'react-router-dom';
10
10
 
11
11
  import Logo from './assets/images/logo-strapi-2022.svg';
12
- import localStorageKey from './components/LanguageProvider/utils/localStorageKey';
12
+ import { LANGUAGE_LOCAL_STORAGE_KEY } from './components/LanguageProvider';
13
13
  import Providers from './components/Providers';
14
14
  import { customFields, Plugin } from './core/apis';
15
15
  import configureStore from './core/store/configureStore';
@@ -454,21 +454,19 @@ class StrapiApp {
454
454
  showReleaseNotification={this.configurations.notifications.releases}
455
455
  store={store}
456
456
  >
457
- <>
458
- <Helmet
459
- link={[
460
- {
461
- rel: 'icon',
462
- type: 'image/png',
463
- href: this.configurations.head.favicon,
464
- },
465
- ]}
466
- htmlAttributes={{ lang: localStorage.getItem(localStorageKey) || 'en' }}
467
- />
468
- <BrowserRouter basename={basename}>
469
- <App store={store} />
470
- </BrowserRouter>
471
- </>
457
+ <Helmet
458
+ link={[
459
+ {
460
+ rel: 'icon',
461
+ type: 'image/png',
462
+ href: this.configurations.head.favicon,
463
+ },
464
+ ]}
465
+ htmlAttributes={{ lang: localStorage.getItem(LANGUAGE_LOCAL_STORAGE_KEY) || 'en' }}
466
+ />
467
+ <BrowserRouter basename={basename}>
468
+ <App store={store} />
469
+ </BrowserRouter>
472
470
  </Providers>
473
471
  );
474
472
  }
@@ -12,10 +12,10 @@ import { useQueries } from 'react-query';
12
12
  // TODO: DS add loader
13
13
 
14
14
  import packageJSON from '../../../../package.json';
15
- import { useConfigurations } from '../../hooks';
15
+ import { useConfiguration } from '../../hooks/useConfiguration';
16
16
  import { getFullName, hashAdminUserEmail } from '../../utils';
17
- import NpsSurvey from '../NpsSurvey';
18
- import PluginsInitializer from '../PluginsInitializer';
17
+ import { NpsSurvey } from '../NpsSurvey';
18
+ import { PluginsInitializer } from '../PluginsInitializer';
19
19
  import RBACProvider from '../RBACProvider';
20
20
 
21
21
  import { fetchAppInfo, fetchCurrentUserPermissions, fetchUserRoles } from './utils/api';
@@ -31,7 +31,7 @@ const AuthenticatedApp = () => {
31
31
  const userName = get(userInfo, 'username') || getFullName(userInfo.firstname, userInfo.lastname);
32
32
  const [userDisplayName, setUserDisplayName] = useState(userName);
33
33
  const [userId, setUserId] = useState(null);
34
- const { showReleaseNotification } = useConfigurations();
34
+ const { showReleaseNotification } = useConfiguration();
35
35
  const [
36
36
  { data: appInfos, status },
37
37
  { data: tagName, isLoading },
@@ -0,0 +1,67 @@
1
+ import * as React from 'react';
2
+
3
+ import { ConfigurationContext, ConfigurationContextValue } from '../contexts/configuration';
4
+
5
+ export interface ConfigurationProviderProps {
6
+ children: React.ReactNode;
7
+ authLogo: string;
8
+ menuLogo: string;
9
+ showReleaseNotification?: boolean;
10
+ showTutorials?: boolean;
11
+ }
12
+
13
+ type LogoKeys = keyof ConfigurationContextValue['logos'];
14
+
15
+ const ConfigurationProvider = ({
16
+ children,
17
+ authLogo: defaultAuthLogo,
18
+ menuLogo: defaultMenuLogo,
19
+ showReleaseNotification = false,
20
+ showTutorials = false,
21
+ }: ConfigurationProviderProps) => {
22
+ const [{ menuLogo, authLogo }, setLogos] = React.useState<{
23
+ [_Key in `${LogoKeys}Logo`]?: ConfigurationContextValue['logos'][LogoKeys]['custom'];
24
+ }>({
25
+ menuLogo: null,
26
+ authLogo: null,
27
+ });
28
+
29
+ const updateProjectSettings: ConfigurationContextValue['updateProjectSettings'] =
30
+ React.useCallback(
31
+ ({ menuLogo, authLogo }) => {
32
+ setLogos({
33
+ menuLogo: menuLogo || defaultMenuLogo,
34
+ authLogo: authLogo || defaultAuthLogo,
35
+ });
36
+ },
37
+ [defaultAuthLogo, defaultMenuLogo]
38
+ );
39
+
40
+ const configurationValue = React.useMemo(() => {
41
+ return {
42
+ logos: {
43
+ menu: { custom: menuLogo, default: defaultMenuLogo },
44
+ auth: { custom: authLogo, default: defaultAuthLogo },
45
+ },
46
+ updateProjectSettings,
47
+ showReleaseNotification,
48
+ showTutorials,
49
+ };
50
+ }, [
51
+ menuLogo,
52
+ defaultMenuLogo,
53
+ authLogo,
54
+ defaultAuthLogo,
55
+ updateProjectSettings,
56
+ showReleaseNotification,
57
+ showTutorials,
58
+ ]);
59
+
60
+ return (
61
+ <ConfigurationContext.Provider value={configurationValue}>
62
+ {children}
63
+ </ConfigurationContext.Provider>
64
+ );
65
+ };
66
+
67
+ export { ConfigurationProvider };
@@ -0,0 +1,129 @@
1
+ /*
2
+ *
3
+ * LanguageProvider
4
+ *
5
+ * this component connects the redux state language locale to the
6
+ * IntlProvider component and i18n messages (loaded from `app/translations`)
7
+ */
8
+
9
+ import * as React from 'react';
10
+
11
+ import defaultsDeep from 'lodash/defaultsDeep';
12
+ import { IntlProvider } from 'react-intl';
13
+
14
+ /* -------------------------------------------------------------------------------------------------
15
+ * LocalesContext
16
+ * -----------------------------------------------------------------------------------------------*/
17
+
18
+ interface LocalesContextValue {
19
+ changeLocale: (locale: keyof State['localeNames']) => void;
20
+ localeNames: Record<string, string>;
21
+ }
22
+
23
+ const LocalesContext = React.createContext<LocalesContextValue>({
24
+ changeLocale: () => {
25
+ throw new Error('LocalesContext: changeLocale() is not implemented');
26
+ },
27
+ localeNames: {},
28
+ });
29
+
30
+ const useLocales = () => React.useContext(LocalesContext);
31
+
32
+ /* -------------------------------------------------------------------------------------------------
33
+ * LanguageProvider
34
+ * -----------------------------------------------------------------------------------------------*/
35
+
36
+ const LANGUAGE_LOCAL_STORAGE_KEY = 'strapi-admin-language';
37
+
38
+ interface LanguageProviderProps {
39
+ children: React.ReactNode;
40
+ localeNames: Record<string, string>;
41
+ messages: Record<string, Record<string, string>>;
42
+ }
43
+
44
+ const LanguageProvider = ({ children, localeNames, messages }: LanguageProviderProps) => {
45
+ const [{ locale }, dispatch] = React.useReducer<React.Reducer<State, Action>, State>(
46
+ reducer,
47
+ initialState,
48
+ () => {
49
+ const languageFromLocaleStorage = window.localStorage.getItem(LANGUAGE_LOCAL_STORAGE_KEY);
50
+ if (languageFromLocaleStorage && localeNames[languageFromLocaleStorage]) {
51
+ return {
52
+ locale: languageFromLocaleStorage,
53
+ localeNames,
54
+ };
55
+ } else {
56
+ return {
57
+ locale: 'en',
58
+ localeNames,
59
+ };
60
+ }
61
+ }
62
+ );
63
+
64
+ React.useEffect(() => {
65
+ // Set user language in local storage.
66
+ window.localStorage.setItem(LANGUAGE_LOCAL_STORAGE_KEY, locale);
67
+ document.documentElement.setAttribute('lang', locale);
68
+ }, [locale]);
69
+
70
+ const changeLocale = React.useCallback((locale: keyof State['localeNames']) => {
71
+ dispatch({
72
+ type: 'CHANGE_LOCALE',
73
+ locale,
74
+ });
75
+ }, []);
76
+
77
+ const appMessages = defaultsDeep(messages[locale], messages.en);
78
+
79
+ const contextValue = React.useMemo(
80
+ () => ({ changeLocale, localeNames }),
81
+ [changeLocale, localeNames]
82
+ );
83
+
84
+ return (
85
+ <IntlProvider locale={locale} defaultLocale="en" messages={appMessages} textComponent="span">
86
+ <LocalesContext.Provider value={contextValue}>{children}</LocalesContext.Provider>
87
+ </IntlProvider>
88
+ );
89
+ };
90
+
91
+ /* -------------------------------------------------------------------------------------------------
92
+ * Reducer
93
+ * -----------------------------------------------------------------------------------------------*/
94
+
95
+ interface State {
96
+ localeNames: Record<string, string>;
97
+ locale: keyof State['localeNames'];
98
+ }
99
+
100
+ const initialState: State = {
101
+ localeNames: { en: 'English' },
102
+ locale: 'en',
103
+ };
104
+
105
+ interface ChangeLocaleAction {
106
+ type: 'CHANGE_LOCALE';
107
+ locale: keyof State['localeNames'];
108
+ }
109
+
110
+ type Action = ChangeLocaleAction;
111
+
112
+ const reducer = (state = initialState, action: Action) => {
113
+ switch (action.type) {
114
+ case 'CHANGE_LOCALE': {
115
+ const { locale } = action;
116
+
117
+ if (!state.localeNames[locale]) {
118
+ return state;
119
+ }
120
+
121
+ return { ...state, locale };
122
+ }
123
+ default: {
124
+ return state;
125
+ }
126
+ }
127
+ };
128
+
129
+ export { LanguageProvider, useLocales, LANGUAGE_LOCAL_STORAGE_KEY };
@@ -1,4 +1,4 @@
1
- import React, { useRef, useState } from 'react';
1
+ import * as React from 'react';
2
2
 
3
3
  import { Box, Divider, Flex, FocusTrap, Typography } from '@strapi/design-system';
4
4
  import {
@@ -19,12 +19,12 @@ import {
19
19
  useTracking,
20
20
  } from '@strapi/helper-plugin';
21
21
  import { Exit, Write } from '@strapi/icons';
22
- import PropTypes from 'prop-types';
23
22
  import { useIntl } from 'react-intl';
24
23
  import { NavLink as RouterNavLink, useHistory, useLocation } from 'react-router-dom';
25
24
  import styled from 'styled-components';
26
25
 
27
- import { useConfigurations } from '../../hooks';
26
+ import { useConfiguration } from '../hooks/useConfiguration';
27
+ import { Menu } from '../hooks/useMenu';
28
28
 
29
29
  const LinkUserWrapper = styled(Box)`
30
30
  width: ${150 / 16}rem;
@@ -33,7 +33,7 @@ const LinkUserWrapper = styled(Box)`
33
33
  left: ${({ theme }) => theme.spaces[5]};
34
34
  `;
35
35
 
36
- const LinkUser = styled(RouterNavLink)`
36
+ const LinkUser = styled(RouterNavLink)<{ logout?: boolean }>`
37
37
  display: flex;
38
38
  justify-content: space-between;
39
39
  align-items: center;
@@ -54,12 +54,14 @@ const LinkUser = styled(RouterNavLink)`
54
54
  }
55
55
  `;
56
56
 
57
- const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
58
- const buttonRef = useRef();
59
- const [userLinksVisible, setUserLinksVisible] = useState(false);
57
+ interface LeftMenuProps extends Pick<Menu, 'generalSectionLinks' | 'pluginsSectionLinks'> {}
58
+
59
+ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }: LeftMenuProps) => {
60
+ const navUserRef = React.useRef<HTMLDivElement>(null!);
61
+ const [userLinksVisible, setUserLinksVisible] = React.useState(false);
60
62
  const {
61
63
  logos: { menu },
62
- } = useConfigurations();
64
+ } = useConfiguration();
63
65
  const [condensed, setCondensed] = usePersistentState('navbar-condensed', false);
64
66
  const { userDisplayName } = useAppInfo();
65
67
  const { formatMessage } = useIntl();
@@ -83,16 +85,19 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
83
85
  history.push('/auth/login');
84
86
  };
85
87
 
86
- const handleBlur = (e) => {
88
+ const handleBlur: React.FocusEventHandler = (e) => {
87
89
  if (
88
90
  !e.currentTarget.contains(e.relatedTarget) &&
91
+ /**
92
+ * TODO: can we replace this by just using the navUserRef?
93
+ */
89
94
  e.relatedTarget?.parentElement?.id !== 'main-nav-user-button'
90
95
  ) {
91
96
  setUserLinksVisible(false);
92
97
  }
93
98
  };
94
99
 
95
- const handleClickOnLink = (destination = null) => {
100
+ const handleClickOnLink = (destination: string) => {
96
101
  trackUsage('willNavigate', { from: pathname, to: destination });
97
102
  };
98
103
 
@@ -126,6 +131,7 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
126
131
  <NavSections>
127
132
  <NavLink
128
133
  as={RouterNavLink}
134
+ // @ts-expect-error the props from the passed as prop are not inferred // joined together
129
135
  to="/content-manager"
130
136
  icon={<Write />}
131
137
  onClick={() => handleClickOnLink('/content-manager')}
@@ -146,6 +152,7 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
146
152
  return (
147
153
  <NavLink
148
154
  as={RouterNavLink}
155
+ // @ts-expect-error the props from the passed as prop are not inferred // joined together
149
156
  to={link.to}
150
157
  key={link.to}
151
158
  icon={<Icon />}
@@ -172,8 +179,11 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
172
179
  <NavLink
173
180
  as={RouterNavLink}
174
181
  badgeContent={
175
- (link.notificationsCount > 0 && link.notificationsCount.toString()) || undefined
182
+ link.notificationsCount && link.notificationsCount > 0
183
+ ? link.notificationsCount.toString()
184
+ : undefined
176
185
  }
186
+ // @ts-expect-error the props from the passed as prop are not inferred // joined together
177
187
  to={link.to}
178
188
  key={link.to}
179
189
  icon={<LinkIcon />}
@@ -190,7 +200,7 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
190
200
  <NavFooter>
191
201
  <NavUser
192
202
  id="main-nav-user-button"
193
- ref={buttonRef}
203
+ ref={navUserRef}
194
204
  onClick={handleToggleUserLinks}
195
205
  initials={initials}
196
206
  >
@@ -244,9 +254,4 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
244
254
  );
245
255
  };
246
256
 
247
- LeftMenu.propTypes = {
248
- generalSectionLinks: PropTypes.array.isRequired,
249
- pluginsSectionLinks: PropTypes.array.isRequired,
250
- };
251
-
252
- export default LeftMenu;
257
+ export { LeftMenu };
@@ -13,7 +13,7 @@ import {
13
13
  FieldInput,
14
14
  VisuallyHidden,
15
15
  } from '@strapi/design-system';
16
- import { auth, useNotification, useAppInfo } from '@strapi/helper-plugin';
16
+ import { auth, useNotification, useAppInfo, usePersistentState } from '@strapi/helper-plugin';
17
17
  import { Cross } from '@strapi/icons';
18
18
  import { Formik, Form } from 'formik';
19
19
  import { useIntl } from 'react-intl';
@@ -21,8 +21,6 @@ import { useMutation } from 'react-query';
21
21
  import styled, { useTheme } from 'styled-components';
22
22
  import * as yup from 'yup';
23
23
 
24
- import { useNpsSurveySettings } from './hooks/useNpsSurveySettings';
25
-
26
24
  const FieldWrapper = styled(Field)`
27
25
  height: ${32 / 16}rem;
28
26
  width: ${32 / 16}rem;
@@ -67,21 +65,27 @@ const delays = {
67
65
 
68
66
  const ratingArray = [...Array(11).keys()];
69
67
 
70
- const checkIfShouldShowSurvey = (settings) => {
68
+ const checkIfShouldShowSurvey = (settings: NpsSurveySettings) => {
71
69
  const { enabled, lastResponseDate, firstDismissalDate, lastDismissalDate } = settings;
72
70
 
73
71
  // This function goes through all the cases where we'd want to not show the survey:
74
- // 1. If the survey is disabled, abort mission, don't bother checking the other settings.
75
- // 2. If the user has already responded to the survey, check if enough time has passed since the last response.
76
- // 3. If the user has dismissed the survey twice or more before, check if enough time has passed since the last dismissal.
77
- // 4. If the user has only dismissed the survey once before, check if enough time has passed since the first dismissal.
72
+ // 1. If the survey is disabled by strapi, abort mission, don't bother checking the other settings.
73
+ // 2. If the survey is disabled by user, abort mission, don't bother checking the other settings.
74
+ // 3. If the user has already responded to the survey, check if enough time has passed since the last response.
75
+ // 4. If the user has dismissed the survey twice or more before, check if enough time has passed since the last dismissal.
76
+ // 5. If the user has only dismissed the survey once before, check if enough time has passed since the first dismissal.
78
77
  // If none of these cases check out, then we show the survey.
79
78
  // Note that submitting a response resets the dismissal counts.
80
- // Checks 3 and 4 should not be reversed, since the first dismissal will also exist if the user has dismissed the survey twice or more before.
79
+ // Checks 4 and 5 should not be reversed, since the first dismissal will also exist if the user has dismissed the survey twice or more before.
81
80
 
82
81
  // For users who had created an account before the NPS feature was introduced,
83
82
  // we assume that they would have enabled the NPS feature if they had the chance.
84
83
 
84
+ // Global strapi disable for NSP.
85
+ if (window.strapi.flags.nps === false) {
86
+ return false;
87
+ }
88
+
85
89
  // User chose not to enable the NPS feature when signing up
86
90
  if (enabled === false) {
87
91
  return false;
@@ -132,7 +136,16 @@ const NpsSurvey = () => {
132
136
  const toggleNotification = useNotification();
133
137
  const { currentEnvironment, strapiVersion } = useAppInfo();
134
138
 
135
- const { mutate, isLoading } = useMutation(
139
+ interface NpsSurveyMutationBody {
140
+ email: string;
141
+ rating: number | null;
142
+ comment: string;
143
+ environment?: string;
144
+ version?: string;
145
+ license: 'Enterprise' | 'Community';
146
+ }
147
+
148
+ const { mutate, isLoading } = useMutation<unknown, unknown, NpsSurveyMutationBody>(
136
149
  async (form) => {
137
150
  const res = await fetch('https://analytics.strapi.io/submit-nps', {
138
151
  method: 'POST',
@@ -152,7 +165,7 @@ const NpsSurvey = () => {
152
165
  onSuccess() {
153
166
  setNpsSurveySettings((settings) => ({
154
167
  ...settings,
155
- lastResponseDate: new Date(),
168
+ lastResponseDate: new Date().toString(),
156
169
  firstDismissalDate: null,
157
170
  lastDismissalDate: null,
158
171
  }));
@@ -165,7 +178,7 @@ const NpsSurvey = () => {
165
178
  onError() {
166
179
  toggleNotification({
167
180
  type: 'warning',
168
- message: formatMessage({ id: 'notification.error' }),
181
+ message: formatMessage({ id: 'notification.error', defaultMessage: 'An error occurred' }),
169
182
  });
170
183
  },
171
184
  }
@@ -197,12 +210,18 @@ const NpsSurvey = () => {
197
210
  return null;
198
211
  }
199
212
 
200
- const handleSubmitResponse = ({ npsSurveyRating: rating, npsSurveyFeedback: comment }) => {
201
- const { email } = auth.getUserInfo();
213
+ const handleSubmitResponse = ({
214
+ npsSurveyRating,
215
+ npsSurveyFeedback,
216
+ }: {
217
+ npsSurveyRating: NpsSurveyMutationBody['rating'];
218
+ npsSurveyFeedback: NpsSurveyMutationBody['comment'];
219
+ }) => {
220
+ const userInfo = auth.getUserInfo();
202
221
  mutate({
203
- email,
204
- rating,
205
- comment,
222
+ email: typeof userInfo === 'object' && userInfo !== null ? userInfo.email : '',
223
+ rating: npsSurveyRating,
224
+ comment: npsSurveyFeedback,
206
225
  environment: currentEnvironment,
207
226
  version: strapiVersion,
208
227
  license: window.strapi.projectType,
@@ -218,10 +237,10 @@ const NpsSurvey = () => {
218
237
 
219
238
  if (settings.firstDismissalDate) {
220
239
  // If the user dismisses the survey for the second time
221
- nextSettings.lastDismissalDate = new Date();
240
+ nextSettings.lastDismissalDate = new Date().toString();
222
241
  } else {
223
242
  // If the user dismisses the survey for the first time
224
- nextSettings.firstDismissalDate = new Date();
243
+ nextSettings.firstDismissalDate = new Date().toString();
225
244
  }
226
245
 
227
246
  return nextSettings;
@@ -295,7 +314,7 @@ const NpsSurvey = () => {
295
314
  return (
296
315
  <FieldWrapper
297
316
  key={number}
298
- className={values.npsSurveyRating === number ? 'selected' : null} // "selected" class added when child radio button is checked
317
+ className={values.npsSurveyRating === number ? 'selected' : undefined} // "selected" class added when child radio button is checked
299
318
  hasRadius
300
319
  background="primary100"
301
320
  borderColor="primary200"
@@ -365,4 +384,32 @@ const NpsSurvey = () => {
365
384
  );
366
385
  };
367
386
 
368
- export default NpsSurvey;
387
+ interface NpsSurveySettings {
388
+ enabled: boolean;
389
+ lastResponseDate: string | null;
390
+ firstDismissalDate: string | null;
391
+ lastDismissalDate: string | null;
392
+ }
393
+
394
+ /**
395
+ * We exported to make it available during admin user registration.
396
+ * Because we only enable the NPS for users who subscribe to the newsletter when signing up
397
+ */
398
+ function useNpsSurveySettings() {
399
+ const [npsSurveySettings, setNpsSurveySettings] = usePersistentState<NpsSurveySettings>(
400
+ 'STRAPI_NPS_SURVEY_SETTINGS',
401
+ {
402
+ enabled: true,
403
+ lastResponseDate: null,
404
+ firstDismissalDate: null,
405
+ lastDismissalDate: null,
406
+ }
407
+ );
408
+
409
+ /**
410
+ * TODO: should this just be an array so we can alias the `usePersistentState` hook?
411
+ */
412
+ return { npsSurveySettings, setNpsSurveySettings };
413
+ }
414
+
415
+ export { NpsSurvey, useNpsSurveySettings };