@strapi/admin 4.2.1-alpha.0 → 4.3.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/admin/src/app.js +7 -4
  2. package/admin/src/components/OverlayBlocker/index.js +4 -0
  3. package/admin/src/content-manager/components/DynamicTable/CellContent/utils/hasContent.js +1 -1
  4. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +10 -16
  5. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +1 -0
  6. package/admin/src/content-manager/components/SelectMany/index.js +2 -4
  7. package/admin/src/content-manager/components/SelectWrapper/index.js +1 -13
  8. package/admin/src/content-manager/components/Wysiwyg/EditorStylesContainer.js +1 -1
  9. package/admin/src/content-manager/pages/ListView/index.js +2 -2
  10. package/admin/src/hooks/{useFetchInstalledPlugins → useFetchEnabledPlugins}/index.js +4 -4
  11. package/admin/src/hooks/{useFetchInstalledPlugins → useFetchEnabledPlugins}/utils/api.js +2 -2
  12. package/admin/src/hooks/useFetchMarketplaceProviders/index.js +1 -1
  13. package/admin/src/pages/Admin/index.js +15 -5
  14. package/admin/src/pages/App/constants.js +1 -0
  15. package/admin/src/pages/App/index.js +17 -3
  16. package/admin/src/pages/App/reducer.js +22 -0
  17. package/admin/src/pages/InstalledPluginsPage/Plugins.js +2 -2
  18. package/admin/src/pages/MarketplacePage/components/NpmPackageCard/index.js +7 -8
  19. package/admin/src/pages/MarketplacePage/components/NpmPackagesGrid/index.js +9 -3
  20. package/admin/src/pages/MarketplacePage/index.js +9 -39
  21. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoModalStepper/FromUrlForm.js +7 -3
  22. package/admin/src/reducers.js +4 -2
  23. package/admin/src/translations/en.json +2 -0
  24. package/admin/src/translations/hi.json +699 -0
  25. package/admin/src/translations/languageNativeNames.js +2 -0
  26. package/admin/src/translations/pl.json +238 -233
  27. package/admin/src/translations/sa.json +698 -0
  28. package/admin/src/translations/zh-Hans.json +28 -1
  29. package/admin/src/tsconfig.json +10 -0
  30. package/build/20.9e5a98b6.chunk.js +308 -0
  31. package/build/{9115.623dc4f7.chunk.js → 2336.cb1c0d6e.chunk.js} +1 -1
  32. package/build/2863.bf3c1eff.chunk.js +194 -0
  33. package/build/3531.ef1d2cfc.chunk.js +10 -0
  34. package/build/413.d5986568.chunk.js +284 -0
  35. package/build/{4715.4588fdf5.chunk.js → 4715.a6e62860.chunk.js} +8 -8
  36. package/build/5250.9988a0ad.chunk.js +12 -0
  37. package/build/{9158.e48d88af.chunk.js → 5520.9dcd6a9f.chunk.js} +32 -32
  38. package/build/5833.970a963d.chunk.js +112 -0
  39. package/build/6925.fb35248e.chunk.js +761 -0
  40. package/build/8773.16dea88d.chunk.js +327 -0
  41. package/build/{7757.f6eb5e92.chunk.js → 9166.5c585d7c.chunk.js} +25 -25
  42. package/build/9262.25aa12a5.chunk.js +1 -0
  43. package/build/Admin-authenticatedApp.d38dc4dd.chunk.js +80 -0
  44. package/build/Admin_homePage.e3571a75.chunk.js +71 -0
  45. package/build/Admin_marketplace.284bf8fc.chunk.js +11 -0
  46. package/build/Admin_pluginsPage.8537476f.chunk.js +1 -0
  47. package/build/{Admin_profilePage.249cbfc9.chunk.js → Admin_profilePage.b7f85e78.chunk.js} +2 -2
  48. package/build/Admin_settingsPage.858ef0d7.chunk.js +178 -0
  49. package/build/admin-edit-roles-page.33647266.chunk.js +1 -0
  50. package/build/admin-edit-users.8cd7519b.chunk.js +10 -0
  51. package/build/admin-users.6581eec5.chunk.js +11 -0
  52. package/build/{api-tokens-edit-page.3e453fc1.chunk.js → api-tokens-create-page.efa1d2aa.chunk.js} +1 -1
  53. package/build/api-tokens-edit-page.6101f051.chunk.js +1 -0
  54. package/build/{api-tokens-list-page.872c3800.chunk.js → api-tokens-list-page.782e872f.chunk.js} +2 -2
  55. package/build/{codemirror-css.98490df3.chunk.js → codemirror-css.4e2bbed3.chunk.js} +2 -2
  56. package/build/{codemirror-javacript.cafbda9c.chunk.js → codemirror-javacript.41bdefda.chunk.js} +1 -1
  57. package/build/codemirror-theme.a82cae4e.chunk.js +34 -0
  58. package/build/content-manager.adf6fc82.chunk.js +1182 -0
  59. package/build/content-type-builder-translation-pl-json.4a42349b.chunk.js +1 -0
  60. package/build/content-type-builder.366a3b10.chunk.js +142 -0
  61. package/build/{cropper-css.0055cd53.chunk.js → cropper-css.12fe038c.chunk.js} +4 -4
  62. package/build/{email-settings-page.1f235173.chunk.js → email-settings-page.d72e7f4c.chunk.js} +2 -2
  63. package/build/email-translation-pl-json.a03bcf98.chunk.js +1 -0
  64. package/build/{en-json.0a5ba154.chunk.js → en-json.729eb94d.chunk.js} +1 -1
  65. package/build/{fontawesome-css-all.b88d464e.chunk.js → fontawesome-css-all.15068c6e.chunk.js} +3 -3
  66. package/build/{fontawesome-css.a92a7b6c.chunk.js → fontawesome-css.418f40da.chunk.js} +2 -2
  67. package/build/hi-json.b4ae16d1.chunk.js +1 -0
  68. package/build/highlight.js.af2de364.chunk.js +86 -0
  69. package/build/i18n-settings-page.8803df0b.chunk.js +101 -0
  70. package/build/i18n-translation-pl-json.cea5bf23.chunk.js +1 -0
  71. package/build/index.html +1 -1
  72. package/build/main.689d6439.js +8688 -0
  73. package/build/pl-json.4d11f53d.chunk.js +1 -0
  74. package/build/runtime~main.308a7c41.js +2 -0
  75. package/build/sa-json.be504091.chunk.js +1 -0
  76. package/build/sso-settings-page.2bae79df.chunk.js +1 -0
  77. package/build/{upload-settings.4ee2f135.chunk.js → upload-settings.f1e587c0.chunk.js} +6 -6
  78. package/build/upload-translation-de-json.745613c0.chunk.js +1 -0
  79. package/build/upload-translation-dk-json.cb25dcf0.chunk.js +1 -0
  80. package/build/upload-translation-en-json.fddec9a6.chunk.js +1 -0
  81. package/build/upload-translation-es-json.1f344b53.chunk.js +1 -0
  82. package/build/upload-translation-fr-json.e21c0c7a.chunk.js +1 -0
  83. package/build/upload-translation-he-json.4ce77b7b.chunk.js +1 -0
  84. package/build/upload-translation-it-json.5ce11e0b.chunk.js +1 -0
  85. package/build/upload-translation-ja-json.22afae44.chunk.js +1 -0
  86. package/build/upload-translation-ko-json.9a2c21cb.chunk.js +1 -0
  87. package/build/upload-translation-ms-json.0605d6da.chunk.js +1 -0
  88. package/build/upload-translation-pl-json.c1f86b50.chunk.js +1 -0
  89. package/build/upload-translation-pt-BR-json.95686cfb.chunk.js +1 -0
  90. package/build/upload-translation-ru-json.37bd1546.chunk.js +1 -0
  91. package/build/upload-translation-sk-json.b03d4904.chunk.js +1 -0
  92. package/build/upload-translation-th-json.64dd70ce.chunk.js +1 -0
  93. package/build/upload-translation-uk-json.1328cb3e.chunk.js +1 -0
  94. package/build/{upload-translation-zh-Hans-json.c9622577.chunk.js → upload-translation-zh-Hans-json.6832ff81.chunk.js} +1 -1
  95. package/build/upload-translation-zh-json.ee8fba96.chunk.js +1 -0
  96. package/build/upload.1346f4b1.chunk.js +7 -0
  97. package/build/{users-advanced-settings-page.747b2ec1.chunk.js → users-advanced-settings-page.f38654fc.chunk.js} +1 -1
  98. package/build/users-email-settings-page.824a3cdb.chunk.js +101 -0
  99. package/build/users-permissions-translation-en-json.765abf48.chunk.js +1 -0
  100. package/build/users-permissions-translation-pl-json.1dbdd4a1.chunk.js +1 -0
  101. package/build/users-providers-settings-page.82a4ba58.chunk.js +101 -0
  102. package/build/{users-roles-settings-page.1bf4ffc5.chunk.js → users-roles-settings-page.1206751f.chunk.js} +3 -3
  103. package/build/{webhook-edit-page.142b23ac.chunk.js → webhook-edit-page.5c9bada0.chunk.js} +2 -2
  104. package/build/{webhook-list-page.671582a0.chunk.js → webhook-list-page.822927af.chunk.js} +1 -1
  105. package/build/zh-Hans-json.cbc69f3d.chunk.js +1 -0
  106. package/index.js +47 -239
  107. package/package.json +31 -24
  108. package/scripts/build.js +19 -3
  109. package/server/controllers/admin.js +23 -0
  110. package/server/policies/index.js +1 -0
  111. package/server/policies/isTelemetryEnabled.js +16 -0
  112. package/server/routes/admin.js +8 -0
  113. package/server/routes/serve-admin-panel.js +1 -1
  114. package/utils/create-cache-dir.js +131 -0
  115. package/utils/get-custom-app-config-file.js +28 -0
  116. package/utils/get-custom-webpack-config.js +38 -0
  117. package/utils/get-plugins-path.js +26 -0
  118. package/utils/index.js +13 -0
  119. package/utils/should-build-admin.js +52 -0
  120. package/utils/watch-admin-files.js +59 -0
  121. package/webpack.config.js +34 -6
  122. package/admin/src/content-manager/components/SelectWrapper/ClearIndicator.js +0 -18
  123. package/admin/src/content-manager/components/SelectWrapper/DropdownIndicator.js +0 -24
  124. package/admin/src/content-manager/components/SelectWrapper/IconBox.js +0 -20
  125. package/admin/src/content-manager/components/SelectWrapper/IndicatorSeparator.js +0 -3
  126. package/admin/src/content-manager/components/SelectWrapper/utils/getSelectStyles.js +0 -92
  127. package/admin/src/pages/MarketplacePage/utils/api.js +0 -9
  128. package/build/2758.9475712b.chunk.js +0 -162
  129. package/build/2912.dd031292.chunk.js +0 -253
  130. package/build/4982.c57c5675.chunk.js +0 -308
  131. package/build/6925.fafef528.chunk.js +0 -761
  132. package/build/7197.47565569.chunk.js +0 -113
  133. package/build/7589.77ef4fbf.chunk.js +0 -194
  134. package/build/7841.9e9cf739.chunk.js +0 -253
  135. package/build/8681.aec05472.chunk.js +0 -163
  136. package/build/9298.cb3b6bc1.chunk.js +0 -334
  137. package/build/948.d64fb515.chunk.js +0 -2
  138. package/build/Admin-authenticatedApp.63a5061a.chunk.js +0 -80
  139. package/build/Admin_homePage.447df176.chunk.js +0 -71
  140. package/build/Admin_marketplace.8a503eec.chunk.js +0 -11
  141. package/build/Admin_pluginsPage.91a96fa5.chunk.js +0 -1
  142. package/build/Admin_settingsPage.0d138832.chunk.js +0 -180
  143. package/build/admin-edit-roles-page.7c2c9752.chunk.js +0 -1
  144. package/build/admin-edit-users.b835bc48.chunk.js +0 -11
  145. package/build/admin-users.19900b75.chunk.js +0 -12
  146. package/build/api-tokens-create-page.8d299dde.chunk.js +0 -1
  147. package/build/codemirror-theme.b3c64617.chunk.js +0 -34
  148. package/build/content-manager.002bfd99.chunk.js +0 -1204
  149. package/build/content-type-builder-translation-pl-json.a866acda.chunk.js +0 -1
  150. package/build/content-type-builder.a0450dfe.chunk.js +0 -141
  151. package/build/email-translation-pl-json.6da50d0f.chunk.js +0 -1
  152. package/build/highlight.js.9d8ef460.chunk.js +0 -86
  153. package/build/i18n-settings-page.06e88cf2.chunk.js +0 -101
  154. package/build/main.e3a13431.js +0 -8404
  155. package/build/pl-json.f65302c2.chunk.js +0 -1
  156. package/build/runtime~main.dacf1aff.js +0 -2
  157. package/build/sso-settings-page.a7c2e854.chunk.js +0 -1
  158. package/build/upload-translation-de-json.b642da08.chunk.js +0 -1
  159. package/build/upload-translation-dk-json.fc61df13.chunk.js +0 -1
  160. package/build/upload-translation-en-json.59269508.chunk.js +0 -1
  161. package/build/upload-translation-es-json.8ec935ef.chunk.js +0 -1
  162. package/build/upload-translation-fr-json.eb9b4f84.chunk.js +0 -1
  163. package/build/upload-translation-he-json.c226f2dc.chunk.js +0 -1
  164. package/build/upload-translation-it-json.8e58456e.chunk.js +0 -1
  165. package/build/upload-translation-ja-json.1378a2e7.chunk.js +0 -1
  166. package/build/upload-translation-ko-json.5e06e112.chunk.js +0 -1
  167. package/build/upload-translation-ms-json.dc3bf0d7.chunk.js +0 -1
  168. package/build/upload-translation-pl-json.6071e38c.chunk.js +0 -1
  169. package/build/upload-translation-pt-BR-json.7e8d9550.chunk.js +0 -1
  170. package/build/upload-translation-ru-json.da2529f3.chunk.js +0 -1
  171. package/build/upload-translation-sk-json.bfdf4f09.chunk.js +0 -1
  172. package/build/upload-translation-th-json.6a48b826.chunk.js +0 -1
  173. package/build/upload-translation-uk-json.6fb09148.chunk.js +0 -1
  174. package/build/upload-translation-zh-json.711f804b.chunk.js +0 -1
  175. package/build/upload.0d4153e8.chunk.js +0 -105
  176. package/build/users-email-settings-page.8b9a266d.chunk.js +0 -1
  177. package/build/users-permissions-translation-en-json.3fe86528.chunk.js +0 -1
  178. package/build/users-permissions-translation-pl-json.3c4fe81c.chunk.js +0 -1
  179. package/build/users-providers-settings-page.fc9d8f9d.chunk.js +0 -1
  180. package/build/zh-Hans-json.c84ce330.chunk.js +0 -1
package/admin/src/app.js CHANGED
@@ -1,6 +1,9 @@
1
+ const config = {
2
+ locales: ['fr'],
3
+ };
4
+ const bootstrap = () => {};
5
+
1
6
  export default {
2
- config: {
3
- locales: ['fr'],
4
- },
5
- bootstrap() {},
7
+ config,
8
+ bootstrap,
6
9
  };
@@ -37,6 +37,10 @@ const Portal = ({ isOpen }) => {
37
37
  return null;
38
38
  };
39
39
 
40
+ Portal.propTypes = {
41
+ isOpen: PropTypes.bool.isRequired,
42
+ };
43
+
40
44
  const OverlayBlockerProvider = ({ children }) => {
41
45
  const [isOpen, setIsOpen] = useState(false);
42
46
 
@@ -45,7 +45,7 @@ export default function hasContent(type, content, metadatas, fieldSchema) {
45
45
  return !isEmpty(content);
46
46
  }
47
47
 
48
- return content.count > 0;
48
+ return content?.count > 0;
49
49
  }
50
50
 
51
51
  /*
@@ -15,12 +15,12 @@ import {
15
15
  useOverlayBlocker,
16
16
  useTracking,
17
17
  getYupInnerErrors,
18
+ getAPIInnerErrors,
18
19
  } from '@strapi/helper-plugin';
19
20
 
20
21
  import { getTrad, removeKeyInObject } from '../../utils';
21
22
  import reducer, { initialState } from './reducer';
22
23
  import { cleanData, createYupSchema } from './utils';
23
- import { getAPIInnerError } from './utils/getAPIInnerError';
24
24
 
25
25
  const EditViewDataManagerProvider = ({
26
26
  allLayoutData,
@@ -233,9 +233,13 @@ const EditViewDataManagerProvider = ({
233
233
  ({ target: { name, value, type } }, shouldSetInitialValue = false) => {
234
234
  let inputValue = value;
235
235
 
236
- // Empty string is not a valid date,
237
- // Set the date to null when it's empty
238
- if (type === 'date' && value === '') {
236
+ // Allow to reset text, textarea, email, uid, select/enum, and number
237
+ if (
238
+ ['text', 'textarea', 'string', 'email', 'uid', 'select', 'select-one', 'number'].includes(
239
+ type
240
+ ) &&
241
+ !value
242
+ ) {
239
243
  inputValue = null;
240
244
  }
241
245
 
@@ -248,16 +252,6 @@ const EditViewDataManagerProvider = ({
248
252
  return;
249
253
  }
250
254
 
251
- // Allow to reset enum
252
- if (type === 'select-one' && value === '') {
253
- inputValue = null;
254
- }
255
-
256
- // Allow to reset number input
257
- if (type === 'number' && value === '') {
258
- inputValue = null;
259
- }
260
-
261
255
  dispatch({
262
256
  type: 'ON_CHANGE',
263
257
  keys: name.split('.'),
@@ -316,7 +310,7 @@ const EditViewDataManagerProvider = ({
316
310
  } catch (err) {
317
311
  errors = {
318
312
  ...errors,
319
- ...getAPIInnerError(err),
313
+ ...getAPIInnerErrors(err),
320
314
  };
321
315
  }
322
316
 
@@ -352,7 +346,7 @@ const EditViewDataManagerProvider = ({
352
346
  } catch (err) {
353
347
  errors = {
354
348
  ...errors,
355
- ...getAPIInnerError(err),
349
+ ...getAPIInnerErrors(err),
356
350
  };
357
351
  }
358
352
 
@@ -57,6 +57,7 @@ const cleanData = (retrievedData, currentSchema, componentsSchema) => {
57
57
  } else {
58
58
  cleanedData = value ? recursiveCleanData(value, componentsSchema[component]) : value;
59
59
  }
60
+
60
61
  break;
61
62
  case 'dynamiczone':
62
63
  cleanedData = value.map(componentData => {
@@ -2,7 +2,8 @@ import React, { memo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useIntl } from 'react-intl';
4
4
  import isEmpty from 'lodash/isEmpty';
5
- import Select, { createFilter } from 'react-select';
5
+ import { createFilter } from 'react-select';
6
+ import { ReactSelect as Select } from '@strapi/helper-plugin';
6
7
  import { Box } from '@strapi/design-system/Box';
7
8
  import { Stack } from '@strapi/design-system/Stack';
8
9
  import { Typography } from '@strapi/design-system/Typography';
@@ -24,7 +25,6 @@ function SelectMany({
24
25
  options,
25
26
  placeholder,
26
27
  searchToPersist,
27
- styles,
28
28
  targetModel,
29
29
  value,
30
30
  description,
@@ -72,7 +72,6 @@ function SelectMany({
72
72
  placeholder={formatMessage(
73
73
  placeholder || { id: 'global.select', defaultMessage: 'Select...' }
74
74
  )}
75
- styles={styles}
76
75
  value={[]}
77
76
  />
78
77
  <Box paddingTop={3} style={{ overflow: 'auto' }}>
@@ -138,7 +137,6 @@ SelectMany.propTypes = {
138
137
  defaultMessage: PropTypes.string.isRequired,
139
138
  }),
140
139
  searchToPersist: PropTypes.string,
141
- styles: PropTypes.object.isRequired,
142
140
  targetModel: PropTypes.string.isRequired,
143
141
  value: PropTypes.array,
144
142
  description: PropTypes.string,
@@ -2,7 +2,7 @@ import React, { useCallback, useState, useEffect, useMemo, memo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useIntl } from 'react-intl';
4
4
  import { useLocation } from 'react-router-dom';
5
- import { useTheme } from 'styled-components';
5
+ import { Stack } from '@strapi/design-system/Stack';
6
6
  import findIndex from 'lodash/findIndex';
7
7
  import get from 'lodash/get';
8
8
  import isArray from 'lodash/isArray';
@@ -16,18 +16,13 @@ import {
16
16
  } from '@strapi/helper-plugin';
17
17
  import { stringify } from 'qs';
18
18
  import axios from 'axios';
19
- import { Stack } from '@strapi/design-system/Stack';
20
19
  import { axiosInstance } from '../../../core/utils';
21
20
  import { getTrad } from '../../utils';
22
21
  import Label from './Label';
23
22
  import SelectOne from '../SelectOne';
24
23
  import SelectMany from '../SelectMany';
25
- import ClearIndicator from './ClearIndicator';
26
- import DropdownIndicator from './DropdownIndicator';
27
- import IndicatorSeparator from './IndicatorSeparator';
28
24
  import Option from './Option';
29
25
  import { connect, select } from './utils';
30
- import getSelectStyles from './utils/getSelectStyles';
31
26
 
32
27
  const initialPaginationState = {
33
28
  contains: '',
@@ -77,7 +72,6 @@ function SelectWrapper({
77
72
  onRemoveRelation,
78
73
  } = useCMEditViewDataManager();
79
74
  const { pathname } = useLocation();
80
- const theme = useTheme();
81
75
 
82
76
  const value = get(modifiedData, name, null);
83
77
  const [state, setState] = useState(initialPaginationState);
@@ -278,8 +272,6 @@ function SelectWrapper({
278
272
  return <NotAllowedInput intlLabel={intlLabel} labelAction={labelAction} />;
279
273
  }
280
274
 
281
- const styles = getSelectStyles(theme);
282
-
283
275
  return (
284
276
  <Stack spacing={1}>
285
277
  <Label
@@ -293,9 +285,6 @@ function SelectWrapper({
293
285
  <Component
294
286
  addRelation={handleAddRelation}
295
287
  components={{
296
- ClearIndicator,
297
- DropdownIndicator,
298
- IndicatorSeparator,
299
288
  Option,
300
289
  }}
301
290
  displayNavigationLink={shouldDisplayRelationLink}
@@ -315,7 +304,6 @@ function SelectWrapper({
315
304
  onRemove={onRemoveRelation}
316
305
  placeholder={placeholder}
317
306
  searchToPersist={searchToPersist}
318
- styles={styles}
319
307
  targetModel={targetModel}
320
308
  value={value}
321
309
  description={description}
@@ -16,7 +16,7 @@ export const EditorStylesContainer = styled.div`
16
16
  height: ${({ isExpandMode }) => (isExpandMode ? '100%' : '290px')};
17
17
  color: ${({ theme }) => theme.colors.neutral800};
18
18
  direction: ltr;
19
- font-family: --apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
19
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
20
20
  'Open Sans', 'Helvetica Neue', sans-serif;
21
21
  }
22
22
 
@@ -309,8 +309,8 @@ function ListView({
309
309
  { target: headerLayoutTitle }
310
310
  )}
311
311
  placeholder={formatMessage({
312
- id: 'app.component.search.placeholder',
313
- defaultMessage: 'Search...',
312
+ id: 'global.search',
313
+ defaultMessage: 'Search',
314
314
  })}
315
315
  trackedEvent="didSearch"
316
316
  />
@@ -1,11 +1,11 @@
1
1
  import { useQuery } from 'react-query';
2
2
  import { useNotification } from '@strapi/helper-plugin';
3
- import { fetchInstalledPlugins } from './utils/api';
3
+ import { fetchEnabledPlugins } from './utils/api';
4
4
 
5
- const useFetchInstalledPlugins = notifyLoad => {
5
+ const useFetchEnabledPlugins = notifyLoad => {
6
6
  const toggleNotification = useNotification();
7
7
 
8
- return useQuery('list-installed-plugins', () => fetchInstalledPlugins(), {
8
+ return useQuery('list-enabled-plugins', () => fetchEnabledPlugins(), {
9
9
  onSuccess: () => {
10
10
  if (notifyLoad) {
11
11
  notifyLoad();
@@ -20,4 +20,4 @@ const useFetchInstalledPlugins = notifyLoad => {
20
20
  });
21
21
  };
22
22
 
23
- export default useFetchInstalledPlugins;
23
+ export default useFetchEnabledPlugins;
@@ -1,9 +1,9 @@
1
1
  import { axiosInstance } from '../../../core/utils';
2
2
 
3
- const fetchInstalledPlugins = async () => {
3
+ const fetchEnabledPlugins = async () => {
4
4
  const { data } = await axiosInstance.get('/admin/plugins');
5
5
 
6
6
  return data;
7
7
  };
8
8
 
9
- export { fetchInstalledPlugins };
9
+ export { fetchEnabledPlugins };
@@ -2,7 +2,7 @@ import { useQuery } from 'react-query';
2
2
  import { useNotification } from '@strapi/helper-plugin';
3
3
  import { fetchMarketplacePlugins } from './utils/api';
4
4
 
5
- const useFetchMarketplaceProviders = (notifyLoad) => {
5
+ const useFetchMarketplaceProviders = notifyLoad => {
6
6
  const toggleNotification = useNotification();
7
7
 
8
8
  return useQuery('list-marketplace-providers', () => fetchMarketplacePlugins(), {
@@ -6,16 +6,17 @@
6
6
 
7
7
  import React, { Suspense, useEffect, useMemo, lazy } from 'react';
8
8
  import { Switch, Route } from 'react-router-dom';
9
- // Components from @strapi/helper-plugin
10
9
  import { useTracking, LoadingIndicatorPage, useStrapiApp } from '@strapi/helper-plugin';
10
+ import { useDispatch, useSelector } from 'react-redux';
11
11
  import { DndProvider } from 'react-dnd';
12
12
  import { HTML5Backend } from 'react-dnd-html5-backend';
13
+ import GuidedTourModal from '../../components/GuidedTour/Modal';
13
14
  import LeftMenu from '../../components/LeftMenu';
14
15
  import AppLayout from '../../layouts/AppLayout';
15
16
  import { useMenu } from '../../hooks';
16
- import Onboarding from './Onboarding';
17
17
  import { createRoute } from '../../utils';
18
- import GuidedTourModal from '../../components/GuidedTour/Modal';
18
+ import { SET_APP_RUNTIME_STATUS } from '../App/constants';
19
+ import Onboarding from './Onboarding';
19
20
 
20
21
  const CM = lazy(() =>
21
22
  import(/* webpackChunkName: "content-manager" */ '../../content-manager/pages/App')
@@ -40,11 +41,20 @@ const SettingsPage = lazy(() =>
40
41
  // Simple hook easier for testing
41
42
  const useTrackUsage = () => {
42
43
  const { trackUsage } = useTracking();
44
+ const dispatch = useDispatch();
45
+ const appStatus = useSelector(state => state.admin_app.status);
43
46
 
44
47
  useEffect(() => {
45
- trackUsage('didAccessAuthenticatedAdministration');
48
+ // Make sure the event is only send once after accessing the admin panel
49
+ // and not at runtime for example when regenerating the permissions with the ctb
50
+ // or with i18n
51
+ if (appStatus === 'init') {
52
+ trackUsage('didAccessAuthenticatedAdministration');
53
+
54
+ dispatch({ type: SET_APP_RUNTIME_STATUS });
55
+ }
46
56
  // eslint-disable-next-line react-hooks/exhaustive-deps
47
- }, []);
57
+ }, [appStatus]);
48
58
  };
49
59
 
50
60
  const Admin = () => {
@@ -0,0 +1 @@
1
+ export const SET_APP_RUNTIME_STATUS = 'StrapiAdmin/APP/SET_APP_RUNTIME_STATUS';
@@ -14,6 +14,7 @@ import {
14
14
  TrackingContext,
15
15
  prefixFileUrlWithBackendUrl,
16
16
  } from '@strapi/helper-plugin';
17
+ import axios from 'axios';
17
18
  import { SkipToContent } from '@strapi/design-system/Main';
18
19
  import { useIntl } from 'react-intl';
19
20
  import PrivateRoute from '../../components/PrivateRoute';
@@ -41,6 +42,8 @@ function App() {
41
42
  );
42
43
  }, []);
43
44
 
45
+ const [telemetryProperties, setTelemetryProperties] = useState(null);
46
+
44
47
  useEffect(() => {
45
48
  const currentToken = auth.getToken();
46
49
 
@@ -69,12 +72,20 @@ function App() {
69
72
  const getData = async () => {
70
73
  try {
71
74
  const {
72
- data: { hasAdmin, uuid, menuLogo },
73
- } = await request('/admin/init', { method: 'GET' });
75
+ data: {
76
+ data: { hasAdmin, uuid, menuLogo },
77
+ },
78
+ } = await axios.get(`${strapi.backendURL}/admin/init`);
74
79
 
75
80
  updateProjectSettings({ menuLogo: prefixFileUrlWithBackendUrl(menuLogo) });
76
81
 
77
82
  if (uuid) {
83
+ const {
84
+ data: { data: properties },
85
+ } = await axios.get(`${strapi.backendURL}/admin/telemetry-properties`);
86
+
87
+ setTelemetryProperties(properties);
88
+
78
89
  try {
79
90
  const deviceId = await getUID();
80
91
 
@@ -84,6 +95,9 @@ function App() {
84
95
  event: 'didInitializeAdministration',
85
96
  uuid,
86
97
  deviceId,
98
+ properties: {
99
+ ...properties,
100
+ },
87
101
  }),
88
102
  headers: {
89
103
  'Content-Type': 'application/json',
@@ -115,7 +129,7 @@ function App() {
115
129
  return (
116
130
  <Suspense fallback={<LoadingIndicatorPage />}>
117
131
  <SkipToContent>{formatMessage({ id: 'skipToContent' })}</SkipToContent>
118
- <TrackingContext.Provider value={uuid}>
132
+ <TrackingContext.Provider value={{ uuid, telemetryProperties }}>
119
133
  <Switch>
120
134
  {authRoutes}
121
135
  <Route
@@ -0,0 +1,22 @@
1
+ import produce from 'immer';
2
+ import { SET_APP_RUNTIME_STATUS } from './constants';
3
+
4
+ const initialState = {
5
+ status: 'init',
6
+ };
7
+
8
+ const reducer = (state = initialState, action) =>
9
+ /* eslint-disable-next-line consistent-return */
10
+ produce(state, draftState => {
11
+ switch (action.type) {
12
+ case SET_APP_RUNTIME_STATUS: {
13
+ draftState.status = 'runtime';
14
+ break;
15
+ }
16
+ default:
17
+ return draftState;
18
+ }
19
+ });
20
+
21
+ export { initialState };
22
+ export default reducer;
@@ -6,7 +6,7 @@ import { Layout, HeaderLayout, ContentLayout } from '@strapi/design-system/Layou
6
6
  import { Main } from '@strapi/design-system/Main';
7
7
  import { Typography } from '@strapi/design-system/Typography';
8
8
  import { Table, Thead, Tbody, Tr, Td, Th } from '@strapi/design-system/Table';
9
- import useFetchInstalledPlugins from '../../hooks/useFetchInstalledPlugins';
9
+ import useFetchEnabledPlugins from '../../hooks/useFetchEnabledPlugins';
10
10
 
11
11
  const Plugins = () => {
12
12
  const { formatMessage } = useIntl();
@@ -30,7 +30,7 @@ const Plugins = () => {
30
30
  );
31
31
  };
32
32
 
33
- const { status, data } = useFetchInstalledPlugins(notifyPluginPageLoad);
33
+ const { status, data } = useFetchEnabledPlugins(notifyPluginPageLoad);
34
34
 
35
35
  const isLoading = status !== 'success' && status !== 'error';
36
36
 
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useIntl } from 'react-intl';
4
4
  import styled from 'styled-components';
5
+ import pluralize from 'pluralize';
5
6
  import { Box } from '@strapi/design-system/Box';
6
7
  import { Stack } from '@strapi/design-system/Stack';
7
8
  import { Typography } from '@strapi/design-system/Typography';
@@ -27,7 +28,7 @@ const EllipsisText = styled(Typography)`
27
28
 
28
29
  const NpmPackageCard = ({
29
30
  npmPackage,
30
- installedPackageNames,
31
+ isInstalled,
31
32
  useYarn,
32
33
  isInDevelopmentMode,
33
34
  npmPackageType,
@@ -36,8 +37,6 @@ const NpmPackageCard = ({
36
37
  const { formatMessage } = useIntl();
37
38
  const { trackUsage } = useTracking();
38
39
 
39
- const isInstalled = installedPackageNames.includes(attributes.npmPackageName);
40
-
41
40
  const commandToCopy = useYarn
42
41
  ? `yarn add ${attributes.npmPackageName}`
43
42
  : `npm install ${attributes.npmPackageName}`;
@@ -47,10 +46,9 @@ const NpmPackageCard = ({
47
46
  defaultMessage: 'Made by Strapi',
48
47
  });
49
48
 
50
- const npmPackageHref =
51
- npmPackageType === 'provider'
52
- ? attributes.npmPackageUrl
53
- : `https://market.strapi.io/plugins/${attributes.slug}`;
49
+ const npmPackageHref = `https://market.strapi.io/${pluralize.plural(npmPackageType)}/${
50
+ attributes.slug
51
+ }`;
54
52
 
55
53
  return (
56
54
  <Flex
@@ -65,6 +63,7 @@ const NpmPackageCard = ({
65
63
  shadow="tableShadow"
66
64
  height="100%"
67
65
  alignItems="normal"
66
+ data-testid="npm-package-card"
68
67
  >
69
68
  <Box>
70
69
  <Box
@@ -167,7 +166,7 @@ NpmPackageCard.propTypes = {
167
166
  strapiCompatibility: PropTypes.oneOf(['v3', 'v4']),
168
167
  }).isRequired,
169
168
  }).isRequired,
170
- installedPackageNames: PropTypes.arrayOf(PropTypes.string).isRequired,
169
+ isInstalled: PropTypes.bool.isRequired,
171
170
  useYarn: PropTypes.bool.isRequired,
172
171
  isInDevelopmentMode: PropTypes.bool,
173
172
  npmPackageType: PropTypes.string.isRequired,
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useCallback } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { Grid, GridItem } from '@strapi/design-system/Grid';
4
4
  import NpmPackageCard from '../NpmPackageCard';
@@ -10,13 +10,19 @@ const NpmPackagesGrid = ({
10
10
  isInDevelopmentMode,
11
11
  npmPackageType,
12
12
  }) => {
13
+ // Check if an individual package is in the dependencies
14
+ const isAlreadyInstalled = useCallback(
15
+ npmPackageName => installedPackageNames.includes(npmPackageName),
16
+ [installedPackageNames]
17
+ );
18
+
13
19
  return (
14
20
  <Grid gap={4}>
15
- {npmPackages.map((npmPackage) => (
21
+ {npmPackages.map(npmPackage => (
16
22
  <GridItem col={4} s={6} xs={12} style={{ height: '100%' }} key={npmPackage.id}>
17
23
  <NpmPackageCard
18
24
  npmPackage={npmPackage}
19
- installedPackageNames={installedPackageNames}
25
+ isInstalled={isAlreadyInstalled(npmPackage.attributes.npmPackageName)}
20
26
  useYarn={useYarn}
21
27
  isInDevelopmentMode={isInDevelopmentMode}
22
28
  npmPackageType={npmPackageType}
@@ -1,7 +1,6 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
3
  import { Helmet } from 'react-helmet';
4
- import { useQuery } from 'react-query';
5
4
  import matchSorter from 'match-sorter';
6
5
  import {
7
6
  AnErrorOccurred,
@@ -23,8 +22,6 @@ import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/design-system/
23
22
 
24
23
  import EmptyNpmPackageSearch from './components/EmptyNpmPackageSearch';
25
24
  import PageHeader from './components/PageHeader';
26
- import { fetchAppInformation } from './utils/api';
27
- import useFetchInstalledPlugins from '../../hooks/useFetchInstalledPlugins';
28
25
  import useFetchMarketplaceProviders from '../../hooks/useFetchMarketplaceProviders';
29
26
  import useFetchMarketplacePlugins from '../../hooks/useFetchMarketplacePlugins';
30
27
  import adminPermissions from '../../permissions';
@@ -53,7 +50,7 @@ const MarketPlacePage = () => {
53
50
  const toggleNotification = useNotification();
54
51
  const [searchQuery, setSearchQuery] = useState('');
55
52
  const [npmPackageType, setNpmPackageType] = useState('plugin');
56
- const { autoReload: isInDevelopmentMode } = useAppInfos();
53
+ const { autoReload: isInDevelopmentMode, dependencies, useYarn } = useAppInfos();
57
54
  const isOnline = useNavigatorOnLine();
58
55
 
59
56
  useFocusWhenNavigate();
@@ -85,37 +82,9 @@ const MarketPlacePage = () => {
85
82
  data: marketplaceProvidersResponse,
86
83
  } = useFetchMarketplaceProviders(notifyMarketplaceLoad);
87
84
 
88
- const {
89
- status: installedPluginsStatus,
90
- data: installedPluginsResponse,
91
- } = useFetchInstalledPlugins();
92
-
93
- const { data: appInfoResponse, status: appInfoStatus } = useQuery(
94
- 'app-information',
95
- fetchAppInformation,
96
- {
97
- onError: () => {
98
- toggleNotification({
99
- type: 'warning',
100
- message: { id: 'notification.error', defaultMessage: 'An error occured' },
101
- });
102
- },
103
- }
104
- );
105
-
106
- const isLoading = [
107
- marketplacePluginsStatus,
108
- marketplaceProvidersStatus,
109
- installedPluginsStatus,
110
- appInfoStatus,
111
- ].includes('loading');
85
+ const isLoading = [marketplacePluginsStatus, marketplaceProvidersStatus].includes('loading');
112
86
 
113
- const hasFailed = [
114
- marketplacePluginsStatus,
115
- marketplaceProvidersStatus,
116
- installedPluginsStatus,
117
- appInfoStatus,
118
- ].includes('error');
87
+ const hasFailed = [marketplacePluginsStatus, marketplaceProvidersStatus].includes('error');
119
88
 
120
89
  useEffect(() => {
121
90
  trackUsageRef.current('didGoToMarketplace');
@@ -208,8 +177,8 @@ const MarketPlacePage = () => {
208
177
  setNpmPackageType(packageType);
209
178
  };
210
179
 
211
- // Check if plugins are installed already
212
- const installedPluginNames = installedPluginsResponse.plugins.map(plugin => plugin.packageName);
180
+ // Check if plugins and providers are installed already
181
+ const installedPackageNames = Object.keys(dependencies);
213
182
 
214
183
  return (
215
184
  <Layout>
@@ -278,8 +247,8 @@ const MarketPlacePage = () => {
278
247
  ) : (
279
248
  <NpmPackagesGrid
280
249
  npmPackages={pluginSearchResults}
281
- installedPackageNames={installedPluginNames}
282
- useYarn={appInfoResponse.data.useYarn}
250
+ installedPackageNames={installedPackageNames}
251
+ useYarn={useYarn}
283
252
  isInDevelopmentMode={isInDevelopmentMode}
284
253
  npmPackageType="plugin"
285
254
  />
@@ -292,7 +261,8 @@ const MarketPlacePage = () => {
292
261
  ) : (
293
262
  <NpmPackagesGrid
294
263
  npmPackages={providerSearchResults}
295
- useYarn={appInfoResponse.data.useYarn}
264
+ installedPackageNames={installedPackageNames}
265
+ useYarn={useYarn}
296
266
  isInDevelopmentMode={isInDevelopmentMode}
297
267
  npmPackageType="provider"
298
268
  />
@@ -18,8 +18,7 @@ const FromUrlForm = ({ goTo, next, onClose, setLocalImage }) => {
18
18
  setLogoUrl(e.target.value);
19
19
  };
20
20
 
21
- const handleSubmit = async e => {
22
- e.preventDefault();
21
+ const handleSubmit = async () => {
23
22
  try {
24
23
  const file = await urlToFile(logoUrl);
25
24
  const asset = await parseFileMetadatas(file);
@@ -36,7 +35,12 @@ const FromUrlForm = ({ goTo, next, onClose, setLocalImage }) => {
36
35
  };
37
36
 
38
37
  return (
39
- <form onSubmit={handleSubmit}>
38
+ <form
39
+ onSubmit={e => {
40
+ e.preventDefault();
41
+ handleSubmit();
42
+ }}
43
+ >
40
44
  <Box paddingLeft={8} paddingRight={8} paddingTop={6} paddingBottom={6}>
41
45
  <TextInput
42
46
  label={formatMessage({
@@ -1,12 +1,13 @@
1
+ import appReducer from './pages/App/reducer';
1
2
  import rbacProviderReducer from './components/RBACProvider/reducer';
2
- import appReducer from './content-manager/pages/App/reducer';
3
+ import cmAppReducer from './content-manager/pages/App/reducer';
3
4
  import editViewLayoutManagerReducer from './content-manager/pages/EditViewLayoutManager/reducer';
4
5
  import listViewReducer from './content-manager/pages/ListView/reducer';
5
6
  import rbacManagerReducer from './content-manager/hooks/useSyncRbac/reducer';
6
7
  import editViewCrudReducer from './content-manager/sharedReducers/crudReducer/reducer';
7
8
 
8
9
  const contentManagerReducers = {
9
- 'content-manager_app': appReducer,
10
+ 'content-manager_app': cmAppReducer,
10
11
  'content-manager_listView': listViewReducer,
11
12
  'content-manager_rbacManager': rbacManagerReducer,
12
13
  'content-manager_editViewLayoutManager': editViewLayoutManagerReducer,
@@ -14,6 +15,7 @@ const contentManagerReducers = {
14
15
  };
15
16
 
16
17
  const reducers = {
18
+ admin_app: appReducer,
17
19
  rbacProvider: rbacProviderReducer,
18
20
  ...contentManagerReducers,
19
21
  };
@@ -679,6 +679,7 @@
679
679
  "content-manager.apiError.This attribute must be unique": "{field} must be unique",
680
680
  "form.button.continue": "Continue",
681
681
  "form.button.done": "Done",
682
+ "global.search": "Search",
682
683
  "global.actions": "Actions",
683
684
  "global.back": "Back",
684
685
  "global.change-password": "Change password",
@@ -721,6 +722,7 @@
721
722
  "notification.success.title": "Success:",
722
723
  "notification.version.update.message": "A new version of Strapi is available!",
723
724
  "notification.warning.title": "Warning:",
725
+ "notification.warning.404": "404 - Not found",
724
726
  "or": "OR",
725
727
  "request.error.model.unknown": "This model doesn't exist",
726
728
  "skipToContent": "Skip to content",