@strapi/admin 4.5.1 → 4.6.0-alpha.0

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 (129) hide show
  1. package/admin/src/components/LeftMenu/index.js +22 -4
  2. package/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js +4 -3
  3. package/admin/src/content-manager/pages/ListSettingsView/components/Settings.js +79 -76
  4. package/admin/src/core/apis/CustomFields.js +46 -1
  5. package/admin/src/pages/MarketplacePage/components/SortSelect/index.js +20 -0
  6. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormApiTokenContainer/index.js +2 -2
  7. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormBody/index.js +1 -1
  8. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormHead/index.js +1 -1
  9. package/admin/src/translations/en.json +4 -0
  10. package/admin/src/translations/nl.json +210 -23
  11. package/admin/src/translations/pt.json +33 -32
  12. package/admin/src/translations/zh.json +280 -95
  13. package/build/1233.32d6888d.chunk.js +169 -0
  14. package/build/2438.afe24949.chunk.js +2525 -0
  15. package/build/2517.5cc235ba.chunk.js +117 -0
  16. package/build/4306.53359960.chunk.js +98 -0
  17. package/build/4318.daf31770.chunk.js +30 -0
  18. package/build/4986.3820d11d.chunk.js +145 -0
  19. package/build/{8469.41c8d25f.chunk.js → 5015.f080b64e.chunk.js} +6 -1
  20. package/build/504.9aeff724.chunk.js +758 -0
  21. package/build/805.a1894307.chunk.js +138 -0
  22. package/build/8633.8da5488a.chunk.js +1 -0
  23. package/build/8881.bfdb6877.chunk.js +245 -0
  24. package/build/9707.932a3c12.chunk.js +70 -0
  25. package/build/Admin-authenticatedApp.cfc3b4c9.chunk.js +80 -0
  26. package/build/Admin_InternalErrorPage.e0317a5e.chunk.js +1 -0
  27. package/build/Admin_homePage.b4db4df8.chunk.js +77 -0
  28. package/build/Admin_marketplace.fa51405b.chunk.js +26 -0
  29. package/build/Admin_pluginsPage.14d2840f.chunk.js +6 -0
  30. package/build/Admin_profilePage.6c2c8398.chunk.js +15 -0
  31. package/build/Admin_settingsPage.5e740514.chunk.js +178 -0
  32. package/build/admin-app.ee1211cb.chunk.js +112 -0
  33. package/build/admin-edit-roles-page.c7c338b3.chunk.js +1 -0
  34. package/build/admin-edit-users.d254c128.chunk.js +10 -0
  35. package/build/admin-users.7c423e41.chunk.js +11 -0
  36. package/build/{api-tokens-create-page.93dd0689.chunk.js → api-tokens-create-page.4ca2778d.chunk.js} +1 -1
  37. package/build/{api-tokens-edit-page.b0adac81.chunk.js → api-tokens-edit-page.70a791c2.chunk.js} +1 -1
  38. package/build/api-tokens-list-page.fe994b6b.chunk.js +16 -0
  39. package/build/content-manager.794d3373.chunk.js +1200 -0
  40. package/build/content-type-builder-list-view.95012cf0.chunk.js +201 -0
  41. package/build/content-type-builder-translation-pt-json.96a31576.chunk.js +1 -0
  42. package/build/content-type-builder-translation-zh-json.3b0afd31.chunk.js +1 -0
  43. package/build/content-type-builder.95b9d6a2.chunk.js +145 -0
  44. package/build/email-settings-page.4bb3606f.chunk.js +15 -0
  45. package/build/email-translation-pt-json.159505ab.chunk.js +1 -0
  46. package/build/email-translation-zh-json.62b1c6fe.chunk.js +1 -0
  47. package/build/en-json.7dd57947.chunk.js +1 -0
  48. package/build/i18n-settings-page.195d42fe.chunk.js +1 -0
  49. package/build/i18n-translation-zh-json.eeebb849.chunk.js +1 -0
  50. package/build/index.html +1 -1
  51. package/build/main.a6470578.js +2031 -0
  52. package/build/nl-json.26f39180.chunk.js +1 -0
  53. package/build/pt-json.cd67ba86.chunk.js +1 -0
  54. package/build/runtime~main.6e7d95b9.js +2 -0
  55. package/build/sso-settings-page.eb30a02e.chunk.js +1 -0
  56. package/build/upload-settings.3010911f.chunk.js +18 -0
  57. package/build/upload-translation-pt-json.5c452b48.chunk.js +1 -0
  58. package/build/upload-translation-zh-json.ac5711de.chunk.js +1 -0
  59. package/build/upload.9f19f2e8.chunk.js +64 -0
  60. package/build/users-advanced-settings-page.9df41d67.chunk.js +13 -0
  61. package/build/users-email-settings-page.56d82eaf.chunk.js +28 -0
  62. package/build/users-permissions-translation-zh-json.92f406f9.chunk.js +1 -0
  63. package/build/users-providers-settings-page.96bb7da0.chunk.js +33 -0
  64. package/build/users-roles-settings-page.445e5e16.chunk.js +30 -0
  65. package/build/webhook-edit-page.c5efc08b.chunk.js +75 -0
  66. package/build/webhook-list-page.42533b59.chunk.js +42 -0
  67. package/build/zh-json.2ecc6b99.chunk.js +1 -0
  68. package/ee/server/services/passport/provider-registry.js +1 -1
  69. package/jest.config.front.js +0 -1
  70. package/package.json +11 -10
  71. package/webpack.alias.js +3 -2
  72. package/webpack.config.js +13 -1
  73. package/build/1856.db9f5782.chunk.js +0 -174
  74. package/build/2077.fed8c9c3.chunk.js +0 -206
  75. package/build/2912.fccb2c43.chunk.js +0 -259
  76. package/build/4318.5e670740.chunk.js +0 -30
  77. package/build/4610.7614b003.chunk.js +0 -342
  78. package/build/4715.8e33d630.chunk.js +0 -387
  79. package/build/4800.a6935af6.chunk.js +0 -1
  80. package/build/4982.9e58ea3f.chunk.js +0 -325
  81. package/build/6925.bb6dd64d.chunk.js +0 -762
  82. package/build/7379.e972985f.chunk.js +0 -1
  83. package/build/7692.31e83caa.chunk.js +0 -470
  84. package/build/7841.4804bd98.chunk.js +0 -259
  85. package/build/7866.6db2248d.chunk.js +0 -505
  86. package/build/8380.37126e0d.chunk.js +0 -299
  87. package/build/8549.5e5fb6b6.chunk.js +0 -159
  88. package/build/8738.5a02bffb.chunk.js +0 -463
  89. package/build/9066.5d980488.chunk.js +0 -101
  90. package/build/9420.7addc099.chunk.js +0 -505
  91. package/build/9649.b6afc945.chunk.js +0 -199
  92. package/build/Admin-authenticatedApp.c07d2a86.chunk.js +0 -80
  93. package/build/Admin_InternalErrorPage.12e24216.chunk.js +0 -1
  94. package/build/Admin_homePage.26d32e30.chunk.js +0 -72
  95. package/build/Admin_marketplace.444ff7b8.chunk.js +0 -22
  96. package/build/Admin_pluginsPage.4d59785a.chunk.js +0 -1
  97. package/build/Admin_profilePage.da32abbc.chunk.js +0 -15
  98. package/build/Admin_settingsPage.bf2234e1.chunk.js +0 -178
  99. package/build/admin-app.b157c10a.chunk.js +0 -112
  100. package/build/admin-edit-roles-page.69d9fcb2.chunk.js +0 -1
  101. package/build/admin-edit-users.c585212f.chunk.js +0 -10
  102. package/build/admin-users.d71f198a.chunk.js +0 -11
  103. package/build/api-tokens-list-page.bb36535f.chunk.js +0 -16
  104. package/build/content-manager.f38edbb6.chunk.js +0 -1202
  105. package/build/content-type-builder-list-view.5b3cd768.chunk.js +0 -194
  106. package/build/content-type-builder-translation-pt-json.766bd747.chunk.js +0 -1
  107. package/build/content-type-builder-translation-zh-json.2cc55621.chunk.js +0 -1
  108. package/build/content-type-builder.16af63a6.chunk.js +0 -145
  109. package/build/email-settings-page.91c925a5.chunk.js +0 -103
  110. package/build/email-translation-pt-json.959ea070.chunk.js +0 -1
  111. package/build/email-translation-zh-json.3455468b.chunk.js +0 -1
  112. package/build/en-json.4a269f6b.chunk.js +0 -1
  113. package/build/i18n-settings-page.4ef64441.chunk.js +0 -101
  114. package/build/main.ca8b0ee3.js +0 -9465
  115. package/build/nl-json.2b8cc3a0.chunk.js +0 -1
  116. package/build/pt-json.3161ca22.chunk.js +0 -1
  117. package/build/runtime~main.ede9da1e.js +0 -2
  118. package/build/sso-settings-page.9ceb0140.chunk.js +0 -1
  119. package/build/upload-settings.3f7ad973.chunk.js +0 -101
  120. package/build/upload-translation-zh-json.ee8fba96.chunk.js +0 -1
  121. package/build/upload.7084cea6.chunk.js +0 -7
  122. package/build/users-advanced-settings-page.6a838320.chunk.js +0 -101
  123. package/build/users-email-settings-page.73c41236.chunk.js +0 -1
  124. package/build/users-permissions-translation-zh-json.e03ae2a4.chunk.js +0 -1
  125. package/build/users-providers-settings-page.f8e78537.chunk.js +0 -1
  126. package/build/users-roles-settings-page.b33ec5e5.chunk.js +0 -30
  127. package/build/webhook-edit-page.dc9442ce.chunk.js +0 -23
  128. package/build/webhook-list-page.a110c462.chunk.js +0 -134
  129. package/build/zh-json.608aaf24.chunk.js +0 -1
@@ -2,7 +2,7 @@ import React, { useRef, useState } from 'react';
2
2
  import styled from 'styled-components';
3
3
  import PropTypes from 'prop-types';
4
4
  import { useIntl } from 'react-intl';
5
- import { NavLink as RouterNavLink } from 'react-router-dom';
5
+ import { NavLink as RouterNavLink, useLocation } from 'react-router-dom';
6
6
  import { Divider } from '@strapi/design-system/Divider';
7
7
  import {
8
8
  MainNav,
@@ -19,7 +19,7 @@ import { Typography } from '@strapi/design-system/Typography';
19
19
  import { Stack } from '@strapi/design-system/Stack';
20
20
  import Write from '@strapi/icons/Write';
21
21
  import Exit from '@strapi/icons/Exit';
22
- import { auth, usePersistentState, useAppInfos } from '@strapi/helper-plugin';
22
+ import { auth, usePersistentState, useAppInfos, useTracking } from '@strapi/helper-plugin';
23
23
  import useConfigurations from '../../hooks/useConfigurations';
24
24
 
25
25
  const LinkUserWrapper = styled(Box)`
@@ -59,6 +59,8 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
59
59
  const [condensed, setCondensed] = usePersistentState('navbar-condensed', false);
60
60
  const { userDisplayName } = useAppInfos();
61
61
  const { formatMessage } = useIntl();
62
+ const { trackUsage } = useTracking();
63
+ const { pathname } = useLocation();
62
64
 
63
65
  const initials = userDisplayName
64
66
  .split(' ')
@@ -82,6 +84,10 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
82
84
  }
83
85
  };
84
86
 
87
+ const handleClickOnLink = (destination = null) => {
88
+ trackUsage('willNavigate', { from: pathname, to: destination });
89
+ };
90
+
85
91
  const menuTitle = formatMessage({
86
92
  id: 'app.components.LeftMenu.navbrand.title',
87
93
  defaultMessage: 'Strapi Dashboard',
@@ -110,7 +116,12 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
110
116
  <Divider />
111
117
 
112
118
  <NavSections>
113
- <NavLink as={RouterNavLink} to="/content-manager" icon={<Write />}>
119
+ <NavLink
120
+ as={RouterNavLink}
121
+ to="/content-manager"
122
+ icon={<Write />}
123
+ onClick={() => handleClickOnLink('/content-manager')}
124
+ >
114
125
  {formatMessage({ id: 'global.content-manager', defaultMessage: 'Content manager' })}
115
126
  </NavLink>
116
127
 
@@ -125,7 +136,13 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
125
136
  const Icon = link.icon;
126
137
 
127
138
  return (
128
- <NavLink as={RouterNavLink} to={link.to} key={link.to} icon={<Icon />}>
139
+ <NavLink
140
+ as={RouterNavLink}
141
+ to={link.to}
142
+ key={link.to}
143
+ icon={<Icon />}
144
+ onClick={() => handleClickOnLink(link.to)}
145
+ >
129
146
  {formatMessage(link.intlLabel)}
130
147
  </NavLink>
131
148
  );
@@ -152,6 +169,7 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
152
169
  to={link.to}
153
170
  key={link.to}
154
171
  icon={<LinkIcon />}
172
+ onClick={() => handleClickOnLink(link.to)}
155
173
  >
156
174
  {formatMessage(link.intlLabel)}
157
175
  </NavLink>
@@ -5,6 +5,7 @@ import { useIntl } from 'react-intl';
5
5
  import { Typography } from '@strapi/design-system/Typography';
6
6
  import { Box } from '@strapi/design-system/Box';
7
7
  import { Badge } from '@strapi/design-system/Badge';
8
+ import { Flex } from '@strapi/design-system/Flex';
8
9
  import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu';
9
10
  import { Loader } from '@strapi/design-system/Loader';
10
11
  import styled from 'styled-components';
@@ -38,8 +39,8 @@ const RelationMultiple = ({ fieldSchema, metadatas, name, entityId, value, conte
38
39
  const [isOpen, setIsOpen] = useState(false);
39
40
 
40
41
  const Label = (
41
- <>
42
- <Badge>{value.count}</Badge>{' '}
42
+ <Flex gap={1} wrap="nowrap">
43
+ <Badge>{value.count}</Badge>
43
44
  {formatMessage(
44
45
  {
45
46
  id: 'content-manager.containers.ListPage.items',
@@ -47,7 +48,7 @@ const RelationMultiple = ({ fieldSchema, metadatas, name, entityId, value, conte
47
48
  },
48
49
  { number: value.count }
49
50
  )}
50
- </>
51
+ </Flex>
51
52
  );
52
53
 
53
54
  const notify = () => {
@@ -1,92 +1,95 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import styled from 'styled-components';
4
3
  import { useIntl } from 'react-intl';
5
- import { Flex } from '@strapi/design-system/Flex';
4
+ import { Box } from '@strapi/design-system/Box';
6
5
  import { Grid, GridItem } from '@strapi/design-system/Grid';
7
6
  import { Select, Option } from '@strapi/design-system/Select';
8
7
  import { ToggleInput } from '@strapi/design-system/ToggleInput';
9
- import { Box } from '@strapi/design-system/Box';
8
+ import { Stack } from '@strapi/design-system/Stack';
10
9
  import { Typography } from '@strapi/design-system/Typography';
11
10
  import { getTrad } from '../../../utils';
12
11
 
13
- const FlexGap = styled(Flex)`
14
- gap: ${({ theme }) => theme.spaces[4]};
15
- `;
16
-
17
12
  const Settings = ({ modifiedData, onChange, sortOptions }) => {
18
13
  const { formatMessage } = useIntl();
19
14
  const { settings, metadatas } = modifiedData;
20
15
 
21
16
  return (
22
- <>
23
- <Box paddingBottom={4}>
24
- <Typography variant="delta" as="h2">
25
- {formatMessage({
26
- id: getTrad('containers.SettingPage.settings'),
27
- defaultMessage: 'Settings',
28
- })}
29
- </Typography>
30
- </Box>
31
- <FlexGap justifyContent="space-between" wrap="wrap" paddingBottom={6}>
32
- <ToggleInput
33
- label={formatMessage({
34
- id: getTrad('form.Input.search'),
35
- defaultMessage: 'Enable search',
36
- })}
37
- onChange={(e) => {
38
- onChange({ target: { name: 'settings.searchable', value: e.target.checked } });
39
- }}
40
- onLabel={formatMessage({
41
- id: 'app.components.ToggleCheckbox.on-label',
42
- defaultMessage: 'on',
43
- })}
44
- offLabel={formatMessage({
45
- id: 'app.components.ToggleCheckbox.off-label',
46
- defaultMessage: 'off',
47
- })}
48
- name="settings.searchable"
49
- checked={settings.searchable}
50
- />
51
- <ToggleInput
52
- label={formatMessage({
53
- id: getTrad('form.Input.filters'),
54
- defaultMessage: 'Enable filters',
55
- })}
56
- onChange={(e) => {
57
- onChange({ target: { name: 'settings.filterable', value: e.target.checked } });
58
- }}
59
- onLabel={formatMessage({
60
- id: 'app.components.ToggleCheckbox.on-label',
61
- defaultMessage: 'on',
62
- })}
63
- offLabel={formatMessage({
64
- id: 'app.components.ToggleCheckbox.off-label',
65
- defaultMessage: 'off',
66
- })}
67
- name="settings.filterable"
68
- checked={settings.filterable}
69
- />
70
- <ToggleInput
71
- label={formatMessage({
72
- id: getTrad('form.Input.bulkActions'),
73
- defaultMessage: 'Enable bulk actions',
74
- })}
75
- onChange={(e) => {
76
- onChange({ target: { name: 'settings.bulkable', value: e.target.checked } });
77
- }}
78
- onLabel={formatMessage({
79
- id: 'app.components.ToggleCheckbox.on-label',
80
- defaultMessage: 'on',
81
- })}
82
- offLabel={formatMessage({
83
- id: 'app.components.ToggleCheckbox.off-label',
84
- defaultMessage: 'off',
85
- })}
86
- name="settings.bulkable"
87
- checked={settings.bulkable}
88
- />
89
- </FlexGap>
17
+ <Stack spacing={4}>
18
+ <Typography variant="delta" as="h2">
19
+ {formatMessage({
20
+ id: getTrad('containers.SettingPage.settings'),
21
+ defaultMessage: 'Settings',
22
+ })}
23
+ </Typography>
24
+
25
+ <Stack horizontal justifyContent="space-between" spacing={4}>
26
+ <Box width="100%">
27
+ <ToggleInput
28
+ label={formatMessage({
29
+ id: getTrad('form.Input.search'),
30
+ defaultMessage: 'Enable search',
31
+ })}
32
+ onChange={(e) => {
33
+ onChange({ target: { name: 'settings.searchable', value: e.target.checked } });
34
+ }}
35
+ onLabel={formatMessage({
36
+ id: 'app.components.ToggleCheckbox.on-label',
37
+ defaultMessage: 'on',
38
+ })}
39
+ offLabel={formatMessage({
40
+ id: 'app.components.ToggleCheckbox.off-label',
41
+ defaultMessage: 'off',
42
+ })}
43
+ name="settings.searchable"
44
+ checked={settings.searchable}
45
+ />
46
+ </Box>
47
+
48
+ <Box width="100%">
49
+ <ToggleInput
50
+ label={formatMessage({
51
+ id: getTrad('form.Input.filters'),
52
+ defaultMessage: 'Enable filters',
53
+ })}
54
+ onChange={(e) => {
55
+ onChange({ target: { name: 'settings.filterable', value: e.target.checked } });
56
+ }}
57
+ onLabel={formatMessage({
58
+ id: 'app.components.ToggleCheckbox.on-label',
59
+ defaultMessage: 'on',
60
+ })}
61
+ offLabel={formatMessage({
62
+ id: 'app.components.ToggleCheckbox.off-label',
63
+ defaultMessage: 'off',
64
+ })}
65
+ name="settings.filterable"
66
+ checked={settings.filterable}
67
+ />
68
+ </Box>
69
+
70
+ <Box width="100%">
71
+ <ToggleInput
72
+ label={formatMessage({
73
+ id: getTrad('form.Input.bulkActions'),
74
+ defaultMessage: 'Enable bulk actions',
75
+ })}
76
+ onChange={(e) => {
77
+ onChange({ target: { name: 'settings.bulkable', value: e.target.checked } });
78
+ }}
79
+ onLabel={formatMessage({
80
+ id: 'app.components.ToggleCheckbox.on-label',
81
+ defaultMessage: 'on',
82
+ })}
83
+ offLabel={formatMessage({
84
+ id: 'app.components.ToggleCheckbox.off-label',
85
+ defaultMessage: 'off',
86
+ })}
87
+ name="settings.bulkable"
88
+ checked={settings.bulkable}
89
+ />
90
+ </Box>
91
+ </Stack>
92
+
90
93
  <Grid gap={4}>
91
94
  <GridItem s={12} col={6}>
92
95
  <Select
@@ -145,7 +148,7 @@ const Settings = ({ modifiedData, onChange, sortOptions }) => {
145
148
  </Select>
146
149
  </GridItem>
147
150
  </Grid>
148
- </>
151
+ </Stack>
149
152
  );
150
153
  };
151
154
 
@@ -19,6 +19,40 @@ const ALLOWED_TYPES = [
19
19
  'uid',
20
20
  ];
21
21
 
22
+ const ALLOWED_ROOT_LEVEL_OPTIONS = [
23
+ 'min',
24
+ 'minLength',
25
+ 'max',
26
+ 'maxLength',
27
+ 'required',
28
+ 'regex',
29
+ 'enum',
30
+ 'unique',
31
+ 'private',
32
+ 'default',
33
+ ];
34
+
35
+ const optionValidationsReducer = (acc, option) => {
36
+ if (option.items) {
37
+ return option.items.reduce(optionValidationsReducer, acc);
38
+ }
39
+
40
+ if (!option.name) {
41
+ acc.push({
42
+ isValidOptionPath: false,
43
+ errorMessage: "The 'name' property is required on an options object",
44
+ });
45
+ } else {
46
+ acc.push({
47
+ isValidOptionPath:
48
+ ALLOWED_ROOT_LEVEL_OPTIONS.includes(option.name) || option.name.startsWith('options'),
49
+ errorMessage: `'${option.name}' must be prefixed with 'options.'`,
50
+ });
51
+ }
52
+
53
+ return acc;
54
+ };
55
+
22
56
  class CustomFields {
23
57
  constructor() {
24
58
  this.customFields = {};
@@ -32,7 +66,8 @@ class CustomFields {
32
66
  });
33
67
  } else {
34
68
  // Handle individual custom field
35
- const { name, pluginId, type, intlLabel, intlDescription, components } = customFields;
69
+ const { name, pluginId, type, intlLabel, intlDescription, components, options } =
70
+ customFields;
36
71
 
37
72
  // Ensure required attributes are provided
38
73
  invariant(name, 'A name must be provided');
@@ -55,6 +90,16 @@ class CustomFields {
55
90
  `Custom field name: '${name}' is not a valid object key`
56
91
  );
57
92
 
93
+ // Ensure options have valid name paths
94
+ const allFormOptions = [...(options?.base || []), ...(options?.advanced || [])];
95
+
96
+ if (allFormOptions.length) {
97
+ const optionPathValidations = allFormOptions.reduce(optionValidationsReducer, []);
98
+ optionPathValidations.forEach(({ isValidOptionPath, errorMessage }) => {
99
+ invariant(isValidOptionPath, errorMessage);
100
+ });
101
+ }
102
+
58
103
  // When no plugin is specified, default to the global namespace
59
104
  const uid = pluginId ? `plugin::${pluginId}.${name}` : `global::${name}`;
60
105
 
@@ -37,6 +37,26 @@ const SortSelect = ({ sortQuery, handleSelectChange }) => {
37
37
  defaultMessage: 'Newest',
38
38
  },
39
39
  },
40
+ 'githubStars:desc': {
41
+ selected: {
42
+ id: 'admin.pages.MarketPlacePage.sort.githubStars.selected',
43
+ defaultMessage: 'Sort by GitHub stars',
44
+ },
45
+ option: {
46
+ id: 'admin.pages.MarketPlacePage.sort.githubStars',
47
+ defaultMessage: 'Number of GitHub stars',
48
+ },
49
+ },
50
+ 'npmDownloads:desc': {
51
+ selected: {
52
+ id: 'admin.pages.MarketPlacePage.sort.npmDownloads.selected',
53
+ defaultMessage: 'Sort by npm downloads',
54
+ },
55
+ option: {
56
+ id: 'admin.pages.MarketPlacePage.sort.npmDownloads',
57
+ defaultMessage: 'Number of downloads',
58
+ },
59
+ },
40
60
  };
41
61
 
42
62
  return (
@@ -229,14 +229,14 @@ FormApiTokenContainer.propTypes = {
229
229
  values: PropTypes.shape({
230
230
  name: PropTypes.string,
231
231
  description: PropTypes.string,
232
- lifespan: PropTypes.string,
232
+ lifespan: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
233
233
  type: PropTypes.string,
234
234
  }).isRequired,
235
235
  isCreating: PropTypes.bool.isRequired,
236
236
  apiToken: PropTypes.shape({
237
237
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
238
238
  type: PropTypes.string,
239
- lifespan: PropTypes.number,
239
+ lifespan: PropTypes.string,
240
240
  name: PropTypes.string,
241
241
  accessKey: PropTypes.string,
242
242
  permissions: PropTypes.array,
@@ -50,7 +50,7 @@ FormBody.propTypes = {
50
50
  apiToken: PropTypes.shape({
51
51
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
52
52
  type: PropTypes.string,
53
- lifespan: PropTypes.number,
53
+ lifespan: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
54
54
  name: PropTypes.string,
55
55
  accessKey: PropTypes.string,
56
56
  permissions: PropTypes.array,
@@ -69,7 +69,7 @@ FormHead.propTypes = {
69
69
  apiToken: PropTypes.shape({
70
70
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
71
71
  type: PropTypes.string,
72
- lifespan: PropTypes.number,
72
+ lifespan: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
73
73
  name: PropTypes.string,
74
74
  accessKey: PropTypes.string,
75
75
  permissions: PropTypes.array,
@@ -290,6 +290,10 @@
290
290
  "admin.pages.MarketPlacePage.sort.newest": "Newest",
291
291
  "admin.pages.MarketPlacePage.sort.alphabetical.selected": "Sort by alphabetical order",
292
292
  "admin.pages.MarketPlacePage.sort.newest.selected": "Sort by newest",
293
+ "admin.pages.MarketPlacePage.sort.githubStars": "Number of GitHub stars",
294
+ "admin.pages.MarketPlacePage.sort.githubStars.selected": "Sort by GitHub stars",
295
+ "admin.pages.MarketPlacePage.sort.npmDownloads": "Number of downloads",
296
+ "admin.pages.MarketPlacePage.sort.npmDownloads.selected": "Sort by npm downloads",
293
297
  "admin.pages.MarketPlacePage.filters.collections": "Collections",
294
298
  "admin.pages.MarketPlacePage.filters.collectionsSelected": "{count, plural, =0 {No collections} one {# collection} other {# collections}} selected",
295
299
  "admin.pages.MarketPlacePage.filters.categories": "Categories",