@strapi/admin 4.12.6 → 4.13.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 (211) hide show
  1. package/admin/src/StrapiApp.js +1 -1
  2. package/admin/src/components/AuthenticatedApp/index.js +118 -0
  3. package/admin/src/components/AuthenticatedApp/utils/api.js +85 -0
  4. package/admin/src/components/AuthenticatedApp/utils/checkLatestStrapiVersion.js +11 -0
  5. package/admin/src/components/GuidedTour/Modal/index.js +3 -1
  6. package/admin/src/components/NpsSurvey/hooks/useNpsSurveySettings.js +17 -0
  7. package/admin/src/components/NpsSurvey/index.js +365 -0
  8. package/admin/src/components/PluginsInitializer/index.js +68 -0
  9. package/admin/src/components/PluginsInitializer/init.js +11 -0
  10. package/admin/src/components/PluginsInitializer/reducer.js +22 -0
  11. package/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumns.js +2 -0
  12. package/admin/src/content-manager/components/DynamicZone/components/DynamicZoneLabel.js +1 -1
  13. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +8 -1
  14. package/admin/src/content-manager/components/Filter/CustomInputs/AdminUsersFilter.js +42 -0
  15. package/admin/src/content-manager/components/Filter/Filter.js +54 -0
  16. package/admin/src/content-manager/components/Filter/index.js +1 -0
  17. package/admin/src/content-manager/components/InputUID/index.js +222 -216
  18. package/admin/src/content-manager/components/RelationInput/RelationInput.js +4 -0
  19. package/admin/src/content-manager/components/RepeatableComponent/index.js +32 -2
  20. package/admin/src/content-manager/components/Wysiwyg/Editor.js +432 -68
  21. package/admin/src/content-manager/components/Wysiwyg/WysiwygStyles.js +0 -7
  22. package/admin/src/content-manager/components/Wysiwyg/index.js +147 -152
  23. package/admin/src/content-manager/hooks/useAllowedAttributes.js +47 -0
  24. package/admin/src/content-manager/pages/App/index.js +16 -5
  25. package/admin/src/content-manager/pages/EditView/Information/index.js +9 -8
  26. package/admin/src/content-manager/pages/ListSettingsView/components/Settings.js +40 -7
  27. package/admin/src/content-manager/pages/ListSettingsView/index.js +6 -2
  28. package/admin/src/content-manager/pages/ListView/components/BulkActionButtons/SelectedEntriesModal/index.js +2 -2
  29. package/admin/src/content-manager/pages/ListView/components/FieldPicker/index.js +67 -69
  30. package/admin/src/content-manager/pages/ListView/components/TableRows/index.js +1 -1
  31. package/admin/src/content-manager/pages/ListView/components/ViewSettingsMenu/index.js +80 -0
  32. package/admin/src/content-manager/pages/ListView/index.js +236 -67
  33. package/admin/src/content-manager/utils/getDisplayName.js +33 -0
  34. package/admin/src/content-manager/utils/index.js +1 -0
  35. package/admin/src/hooks/useSettingsForm/index.js +3 -14
  36. package/admin/src/hooks/useSettingsMenu/index.js +2 -2
  37. package/admin/src/hooks/useSettingsMenu/utils/formatLinks.js +3 -1
  38. package/admin/src/hooks/useSettingsMenu/utils/sortLinks.js +3 -1
  39. package/admin/src/index.js +1 -1
  40. package/admin/src/layouts/AppLayout/index.js +33 -0
  41. package/admin/src/pages/Admin/Onboarding/index.js +3 -1
  42. package/admin/src/pages/Admin/index.js +73 -77
  43. package/admin/src/pages/App/constants.js +1 -1
  44. package/admin/src/pages/App/index.js +122 -160
  45. package/admin/src/pages/AuthPage/components/Register/index.js +5 -0
  46. package/admin/src/pages/AuthPage/index.js +4 -2
  47. package/admin/src/pages/HomePage/index.js +3 -1
  48. package/admin/src/pages/InstalledPluginsPage/index.js +3 -1
  49. package/admin/src/pages/{InternalErrorPage.js → InternalErrorPage/index.js} +4 -3
  50. package/admin/src/pages/{NotFoundPage.js → NotFoundPage/index.js} +3 -1
  51. package/admin/src/pages/ProfilePage/index.js +4 -2
  52. package/admin/src/pages/SettingsPage/constants.js +132 -67
  53. package/admin/src/pages/SettingsPage/index.js +18 -23
  54. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +1 -1
  55. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/index.js +1 -1
  56. package/admin/src/pages/SettingsPage/pages/Users/EditPage/index.js +1 -2
  57. package/admin/src/pages/SettingsPage/pages/Users/ListPage/DynamicTable/TableRows/index.js +1 -1
  58. package/admin/src/pages/UseCasePage/index.js +175 -0
  59. package/admin/src/plugins.js +7 -8
  60. package/admin/src/translations/en.json +10 -1
  61. package/admin/src/utils/checkFormValidity.js +15 -0
  62. package/admin/src/utils/createRoute.js +7 -5
  63. package/admin/src/utils/formatAPIErrors.js +3 -1
  64. package/admin/src/utils/getAttributesToDisplay.js +19 -0
  65. package/admin/src/utils/getExistingActions.js +32 -0
  66. package/admin/src/utils/getFullName.js +1 -1
  67. package/admin/src/utils/index.js +9 -0
  68. package/admin/src/utils/makeUniqueRoutes.js +6 -0
  69. package/admin/src/utils/sortLinks.js +3 -1
  70. package/admin/src/utils/uniqueAdminHash.js +9 -2
  71. package/build/{1049.9d69d231.chunk.js → 1049.ec69f5e0.chunk.js} +1 -1
  72. package/build/1227.9f37e1dc.chunk.js +1 -0
  73. package/build/1386.ea73b677.chunk.js +7 -0
  74. package/build/{2225.33287e1b.chunk.js → 2225.649fb7bc.chunk.js} +2 -2
  75. package/build/{2237.03792b63.chunk.js → 2237.b832ae6e.chunk.js} +4 -4
  76. package/build/{2379.401f56f3.chunk.js → 2379.1f98a31a.chunk.js} +1 -1
  77. package/build/{2395.e6a79fbb.chunk.js → 2395.0e5e8ded.chunk.js} +1 -1
  78. package/build/{2801.31393ffe.chunk.js → 2801.8e1aa82a.chunk.js} +1 -1
  79. package/build/3483.19381b40.chunk.js +1 -0
  80. package/build/3739.63e352f1.chunk.js +103 -0
  81. package/build/4174.f1f39e40.chunk.js +1 -0
  82. package/build/448.829e1344.chunk.js +1 -0
  83. package/build/4546.a5946d22.chunk.js +1 -0
  84. package/build/4724.aea5c8c1.chunk.js +6 -0
  85. package/build/{502.8dd074ff.chunk.js → 502.7bba43b1.chunk.js} +1 -1
  86. package/build/6158.c3c13c20.chunk.js +1 -0
  87. package/build/6691.4985ef22.chunk.js +105 -0
  88. package/build/{7464.592a9295.chunk.js → 7464.eb057bec.chunk.js} +1 -1
  89. package/build/78.dcc6df5c.chunk.js +1 -0
  90. package/build/{8276.e519a707.chunk.js → 8276.be3ed581.chunk.js} +2 -2
  91. package/build/{2747.d1442a90.chunk.js → 9806.5d5a0e8d.chunk.js} +64 -72
  92. package/build/9944.7af075a5.chunk.js +26 -0
  93. package/build/Admin-authenticatedApp.43b6ec9a.chunk.js +79 -0
  94. package/build/Admin_InternalErrorPage.38155af3.chunk.js +1 -0
  95. package/build/Admin_homePage.6f128523.chunk.js +81 -0
  96. package/build/Admin_marketplace.061a6e5a.chunk.js +55 -0
  97. package/build/Admin_pluginsPage.16f837b8.chunk.js +6 -0
  98. package/build/Admin_profilePage.678bce24.chunk.js +13 -0
  99. package/build/Admin_settingsPage.af7309e4.chunk.js +111 -0
  100. package/build/{Upload_ConfigureTheView.345ac1e0.chunk.js → Upload_ConfigureTheView.3fc1c100.chunk.js} +1 -1
  101. package/build/admin-app.d63bd229.chunk.js +36 -0
  102. package/build/{admin-edit-roles-page.24bdf746.chunk.js → admin-edit-roles-page.38a6c863.chunk.js} +3 -3
  103. package/build/admin-edit-users.545fc882.chunk.js +10 -0
  104. package/build/{admin-roles-list.23ddff26.chunk.js → admin-roles-list.1e2e814d.chunk.js} +1 -1
  105. package/build/admin-users.b8ea5677.chunk.js +11 -0
  106. package/build/{api-tokens-create-page.46c2ea84.chunk.js → api-tokens-create-page.e0c15627.chunk.js} +1 -1
  107. package/build/{api-tokens-edit-page.58139df9.chunk.js → api-tokens-edit-page.9f2dce47.chunk.js} +1 -1
  108. package/build/{api-tokens-list-page.0af7d431.chunk.js → api-tokens-list-page.d747051c.chunk.js} +2 -2
  109. package/build/{audit-logs-settings-page.0f73ccf8.chunk.js → audit-logs-settings-page.96f9d608.chunk.js} +1 -1
  110. package/build/content-manager.ccff1078.chunk.js +1097 -0
  111. package/build/{content-type-builder-list-view.bf9be456.chunk.js → content-type-builder-list-view.b71cf240.chunk.js} +1 -1
  112. package/build/{content-type-builder.66066281.chunk.js → content-type-builder.e5669749.chunk.js} +18 -18
  113. package/build/{email-settings-page.2f7e35c0.chunk.js → email-settings-page.2809f0bf.chunk.js} +1 -1
  114. package/build/en-json.e12fd5fc.chunk.js +1 -0
  115. package/build/{i18n-settings-page.47f78016.chunk.js → i18n-settings-page.5f716172.chunk.js} +1 -1
  116. package/build/index.html +1 -1
  117. package/build/main.c6c9e04c.js +2859 -0
  118. package/build/{review-workflows-settings-create-view.d24a32b9.chunk.js → review-workflows-settings-create-view.4a156a19.chunk.js} +1 -1
  119. package/build/{review-workflows-settings-edit-view.6044b022.chunk.js → review-workflows-settings-edit-view.ce984d1f.chunk.js} +1 -1
  120. package/build/{review-workflows-settings-list-view.3f0ef4bc.chunk.js → review-workflows-settings-list-view.419b8deb.chunk.js} +2 -2
  121. package/build/runtime~main.dcf1cb45.js +2 -0
  122. package/build/{sso-settings-page.4dba0670.chunk.js → sso-settings-page.45153df5.chunk.js} +1 -1
  123. package/build/{transfer-tokens-create-page.1597e6ab.chunk.js → transfer-tokens-create-page.ebba16d8.chunk.js} +1 -1
  124. package/build/{transfer-tokens-edit-page.8741529f.chunk.js → transfer-tokens-edit-page.d7bb2b3e.chunk.js} +1 -1
  125. package/build/{transfer-tokens-list-page.d6986b03.chunk.js → transfer-tokens-list-page.cfe1736c.chunk.js} +2 -2
  126. package/build/{upload-settings.7f93d4c0.chunk.js → upload-settings.cc5ad813.chunk.js} +1 -1
  127. package/build/{upload.37488080.chunk.js → upload.756efc28.chunk.js} +1 -1
  128. package/build/{users-advanced-settings-page.17052d72.chunk.js → users-advanced-settings-page.818d84eb.chunk.js} +1 -1
  129. package/build/{users-email-settings-page.3de8ea50.chunk.js → users-email-settings-page.c1967c09.chunk.js} +1 -1
  130. package/build/{users-providers-settings-page.0eaa916d.chunk.js → users-providers-settings-page.11893e08.chunk.js} +1 -1
  131. package/build/{users-roles-settings-page.957ad48b.chunk.js → users-roles-settings-page.2b051e6a.chunk.js} +1 -1
  132. package/build/webhook-edit-page.de45c635.chunk.js +33 -0
  133. package/build/{webhook-list-page.65e1b5bb.chunk.js → webhook-list-page.ca91df8b.chunk.js} +1 -1
  134. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/AssigneeFilter.js +42 -0
  135. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/StageFilter.js +70 -0
  136. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/constants.js +71 -0
  137. package/ee/admin/content-manager/pages/EditView/InformationBox/InformationBoxEE.js +9 -217
  138. package/ee/admin/content-manager/pages/EditView/InformationBox/components/AssigneeSelect/AssigneeSelect.js +147 -0
  139. package/ee/admin/content-manager/pages/EditView/InformationBox/components/AssigneeSelect/index.js +1 -0
  140. package/ee/admin/content-manager/pages/EditView/InformationBox/components/StageSelect/StageSelect.js +243 -0
  141. package/ee/admin/content-manager/pages/EditView/InformationBox/components/StageSelect/index.js +1 -0
  142. package/ee/admin/content-manager/pages/EditView/InformationBox/constants.js +2 -0
  143. package/ee/admin/content-manager/pages/ListSettingsView/constants.js +7 -0
  144. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/ReviewWorkflowsAssigneeEE.js +21 -0
  145. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants.js +44 -17
  146. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/index.js +1 -0
  147. package/ee/admin/pages/App/constants.js +5 -6
  148. package/ee/admin/pages/SettingsPage/constants.js +42 -27
  149. package/ee/server/constants/workflows.js +1 -0
  150. package/ee/server/controllers/index.js +1 -0
  151. package/ee/server/controllers/workflows/assignees/index.js +44 -0
  152. package/ee/server/routes/review-workflows.js +17 -0
  153. package/ee/server/services/index.js +1 -0
  154. package/ee/server/services/review-workflows/assignees.js +54 -0
  155. package/ee/server/services/review-workflows/metrics/index.js +5 -0
  156. package/ee/server/services/review-workflows/review-workflows.js +20 -11
  157. package/ee/server/validation/review-workflows.js +8 -0
  158. package/index.js +2 -6
  159. package/package.json +9 -9
  160. package/scripts/build.js +15 -15
  161. package/scripts/create-dev-plugins-file.js +5 -38
  162. package/server/controllers/role.js +2 -0
  163. package/server/controllers/user.js +2 -0
  164. package/server/services/permission/permissions-manager/index.js +3 -1
  165. package/server/services/permission/permissions-manager/sanitize.js +19 -7
  166. package/server/services/permission/permissions-manager/validate.js +218 -0
  167. package/utils/create-cache-dir.js +62 -16
  168. package/utils/create-plugins-exclude-path.js +3 -23
  169. package/utils/get-plugins.js +110 -0
  170. package/utils/index.js +1 -1
  171. package/webpack.config.js +10 -13
  172. package/admin/src/components/AuthenticatedApp.js +0 -229
  173. package/admin/src/content-manager/components/AttributeFilter/Filters.js +0 -58
  174. package/admin/src/content-manager/components/AttributeFilter/hooks/useAllowedAttributes.js +0 -42
  175. package/admin/src/content-manager/components/AttributeFilter/index.js +0 -40
  176. package/admin/src/content-manager/components/Wysiwyg/EditorStylesContainer.js +0 -344
  177. package/admin/src/pages/UseCasePage.js +0 -174
  178. package/build/2166.c837469a.chunk.js +0 -1
  179. package/build/3483.8517171f.chunk.js +0 -1
  180. package/build/3984.dda474f7.chunk.js +0 -1
  181. package/build/4546.7a3c0d03.chunk.js +0 -1
  182. package/build/5483.5bfbb00d.chunk.js +0 -6
  183. package/build/6158.c974fd83.chunk.js +0 -1
  184. package/build/748.fd2e5afd.chunk.js +0 -105
  185. package/build/773.6381d62d.chunk.js +0 -18
  186. package/build/7826.399afe81.chunk.js +0 -103
  187. package/build/8261.2525d35c.chunk.js +0 -7
  188. package/build/8299.62b67c72.chunk.js +0 -1
  189. package/build/Admin-AuthPage.90d64342.chunk.js +0 -35
  190. package/build/Admin-AuthenticatedApp.379ac945.chunk.js +0 -24
  191. package/build/Admin-UseCasePage.1f757db5.chunk.js +0 -13
  192. package/build/Admin_GuidedTourModal.8ccf1fbc.chunk.js +0 -12
  193. package/build/Admin_InternalErrorPage.9de92c6d.chunk.js +0 -9
  194. package/build/Admin_NotFoundPage.21620424.chunk.js +0 -9
  195. package/build/Admin_Onboarding.dbfa32f6.chunk.js +0 -43
  196. package/build/Admin_homePage.2000cbe9.chunk.js +0 -86
  197. package/build/Admin_marketplace.ec80e29b.chunk.js +0 -63
  198. package/build/Admin_pluginsPage.0c6851f8.chunk.js +0 -14
  199. package/build/Admin_profilePage.78cd8495.chunk.js +0 -21
  200. package/build/Admin_settingsPage.1760c3ce.chunk.js +0 -119
  201. package/build/StrapiApp.221fac30.chunk.js +0 -5
  202. package/build/admin-edit-users.5d10d444.chunk.js +0 -10
  203. package/build/admin-users.2b3e4305.chunk.js +0 -11
  204. package/build/content-manager.fb0833bd.chunk.js +0 -1099
  205. package/build/en-json.08c05fcf.chunk.js +0 -1
  206. package/build/main.ee3c1938.js +0 -2859
  207. package/build/runtime~main.397ee447.js +0 -2
  208. package/build/webhook-edit-page.665210af.chunk.js +0 -33
  209. package/scripts/create-plugins-file.js +0 -92
  210. package/utils/get-plugins-path.js +0 -41
  211. /package/server/services/permission/permissions-manager/{query-builers.js → query-builders.js} +0 -0
@@ -0,0 +1,11 @@
1
+ const init = (plugins) => {
2
+ return {
3
+ plugins: Object.keys(plugins).reduce((acc, current) => {
4
+ acc[current] = { ...plugins[current] };
5
+
6
+ return acc;
7
+ }, {}),
8
+ };
9
+ };
10
+
11
+ export default init;
@@ -0,0 +1,22 @@
1
+ import produce from 'immer';
2
+ import set from 'lodash/set';
3
+
4
+ const initialState = {
5
+ plugins: null,
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_PLUGIN_READY': {
13
+ set(draftState, ['plugins', action.pluginId, 'isReady'], true);
14
+ break;
15
+ }
16
+ default:
17
+ return draftState;
18
+ }
19
+ });
20
+
21
+ export { initialState };
22
+ export default reducer;
@@ -0,0 +1,2 @@
1
+ // Overwritten in EE
2
+ export default () => [];
@@ -29,7 +29,7 @@ export const DynamicZoneLabel = ({
29
29
  paddingBottom={3}
30
30
  paddingRight={4}
31
31
  paddingLeft={4}
32
- borderRadius={26}
32
+ borderRadius="26px"
33
33
  background="neutral0"
34
34
  shadow="filterShadow"
35
35
  color="neutral500"
@@ -239,7 +239,14 @@ const reducer = (state, action) =>
239
239
  (value) => {
240
240
  return value.type === 'relation';
241
241
  },
242
- (_, { path }) => {
242
+ (value, { path }) => {
243
+ const relationFieldName = path[path.length - 1];
244
+
245
+ // When editing, we don't want to fetch the relations with creator fields because we already have it
246
+ if (value && (relationFieldName === 'createdBy' || relationFieldName === 'updatedBy')) {
247
+ return value;
248
+ }
249
+
243
250
  if (state.modifiedData?.id === data.id && get(state.modifiedData, path)) {
244
251
  return get(state.modifiedData, path);
245
252
  }
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+
3
+ import { Combobox, ComboboxOption } from '@strapi/design-system';
4
+ import PropTypes from 'prop-types';
5
+ import { useIntl } from 'react-intl';
6
+
7
+ import { useAdminUsers } from '../../../../hooks/useAdminUsers';
8
+ import { getDisplayName } from '../../../utils';
9
+
10
+ export const AdminUsersFilter = ({ value, onChange }) => {
11
+ const { formatMessage } = useIntl();
12
+ const { users, isLoading } = useAdminUsers();
13
+
14
+ return (
15
+ <Combobox
16
+ value={value}
17
+ aria-label={formatMessage({
18
+ id: 'content-manager.components.Filters.usersSelect.label',
19
+ defaultMessage: 'Search and select an user to filter',
20
+ })}
21
+ onChange={onChange}
22
+ loading={isLoading}
23
+ >
24
+ {users.map((user) => {
25
+ return (
26
+ <ComboboxOption key={user.id} value={user.id.toString()}>
27
+ {getDisplayName(user, formatMessage)}
28
+ </ComboboxOption>
29
+ );
30
+ })}
31
+ </Combobox>
32
+ );
33
+ };
34
+
35
+ AdminUsersFilter.propTypes = {
36
+ onChange: PropTypes.func.isRequired,
37
+ value: PropTypes.string,
38
+ };
39
+
40
+ AdminUsersFilter.defaultProps = {
41
+ value: '',
42
+ };
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+
3
+ import { Button } from '@strapi/design-system';
4
+ import { FilterListURLQuery, FilterPopoverURLQuery, useTracking } from '@strapi/helper-plugin';
5
+ import { Filter as FilterIcon } from '@strapi/icons';
6
+ import PropTypes from 'prop-types';
7
+ import { useIntl } from 'react-intl';
8
+
9
+ export const Filter = ({ displayedFilters }) => {
10
+ const [isVisible, setIsVisible] = React.useState(false);
11
+ const { formatMessage } = useIntl();
12
+ const buttonRef = React.useRef();
13
+ const { trackUsage } = useTracking();
14
+
15
+ const handleToggle = () => {
16
+ if (!isVisible) {
17
+ trackUsage('willFilterEntries');
18
+ }
19
+ setIsVisible((prev) => !prev);
20
+ };
21
+
22
+ return (
23
+ <>
24
+ <Button
25
+ variant="tertiary"
26
+ ref={buttonRef}
27
+ startIcon={<FilterIcon />}
28
+ onClick={handleToggle}
29
+ size="S"
30
+ >
31
+ {formatMessage({ id: 'app.utils.filters', defaultMessage: 'Filters' })}
32
+ </Button>
33
+ {isVisible && (
34
+ <FilterPopoverURLQuery
35
+ displayedFilters={displayedFilters}
36
+ isVisible={isVisible}
37
+ onToggle={handleToggle}
38
+ source={buttonRef}
39
+ />
40
+ )}
41
+ <FilterListURLQuery filtersSchema={displayedFilters} />
42
+ </>
43
+ );
44
+ };
45
+
46
+ Filter.propTypes = {
47
+ displayedFilters: PropTypes.arrayOf(
48
+ PropTypes.shape({
49
+ name: PropTypes.string.isRequired,
50
+ metadatas: PropTypes.shape({ label: PropTypes.string }),
51
+ fieldSchema: PropTypes.shape({ type: PropTypes.string }),
52
+ })
53
+ ).isRequired,
54
+ };
@@ -0,0 +1 @@
1
+ export * from './Filter';
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import React, { forwardRef, useEffect, useRef, useState } from 'react';
2
2
 
3
3
  import { Flex, TextInput, Typography } from '@strapi/design-system';
4
4
  import {
@@ -16,247 +16,253 @@ import useDebounce from '../../../hooks/useDebounce';
16
16
  import { FieldActionWrapper, LoadingWrapper, TextValidation } from './endActionStyle';
17
17
  import UID_REGEX from './regex';
18
18
 
19
- const InputUID = ({
20
- attribute,
21
- contentTypeUID,
22
- hint,
23
- disabled,
24
- error,
25
- intlLabel,
26
- labelAction,
27
- name,
28
- onChange,
29
- value,
30
- placeholder,
31
- required,
32
- }) => {
33
- const { modifiedData, initialData, layout } = useCMEditViewDataManager();
34
- const [isLoading, setIsLoading] = useState(false);
35
- const [availability, setAvailability] = useState(null);
36
- const debouncedValue = useDebounce(value, 300);
37
- const generateUid = useRef();
38
- const toggleNotification = useNotification();
39
- const { formatAPIError } = useAPIErrorHandler();
40
- const initialValue = initialData[name];
41
- const { formatMessage } = useIntl();
42
- const createdAtName = layout?.options?.timestamps ?? 0;
43
- const isCreation = !initialData[createdAtName];
44
- const debouncedTargetFieldValue = useDebounce(modifiedData[attribute.targetField], 300);
45
- const [isCustomized, setIsCustomized] = useState(false);
46
- const [regenerateLabel, setRegenerateLabel] = useState(null);
47
- const { post } = useFetchClient();
19
+ const InputUID = forwardRef(
20
+ (
21
+ {
22
+ attribute,
23
+ contentTypeUID,
24
+ hint,
25
+ disabled,
26
+ error,
27
+ intlLabel,
28
+ labelAction,
29
+ name,
30
+ onChange,
31
+ value,
32
+ placeholder,
33
+ required,
34
+ },
35
+ forwardedRef
36
+ ) => {
37
+ const { modifiedData, initialData, layout } = useCMEditViewDataManager();
38
+ const [isLoading, setIsLoading] = useState(false);
39
+ const [availability, setAvailability] = useState(null);
40
+ const debouncedValue = useDebounce(value, 300);
41
+ const generateUid = useRef();
42
+ const toggleNotification = useNotification();
43
+ const { formatAPIError } = useAPIErrorHandler();
44
+ const initialValue = initialData[name];
45
+ const { formatMessage } = useIntl();
46
+ const createdAtName = layout?.options?.timestamps ?? 0;
47
+ const isCreation = !initialData[createdAtName];
48
+ const debouncedTargetFieldValue = useDebounce(modifiedData[attribute.targetField], 300);
49
+ const [isCustomized, setIsCustomized] = useState(false);
50
+ const [regenerateLabel, setRegenerateLabel] = useState(null);
51
+ const { post } = useFetchClient();
48
52
 
49
- const label = intlLabel.id
50
- ? formatMessage(
51
- { id: intlLabel.id, defaultMessage: intlLabel.defaultMessage },
52
- { ...intlLabel.values }
53
- )
54
- : name;
53
+ const label = intlLabel.id
54
+ ? formatMessage(
55
+ { id: intlLabel.id, defaultMessage: intlLabel.defaultMessage },
56
+ { ...intlLabel.values }
57
+ )
58
+ : name;
55
59
 
56
- const formattedPlaceholder = placeholder
57
- ? formatMessage(
58
- { id: placeholder.id, defaultMessage: placeholder.defaultMessage },
59
- { ...placeholder.values }
60
- )
61
- : '';
60
+ const formattedPlaceholder = placeholder
61
+ ? formatMessage(
62
+ { id: placeholder.id, defaultMessage: placeholder.defaultMessage },
63
+ { ...placeholder.values }
64
+ )
65
+ : '';
62
66
 
63
- generateUid.current = async (shouldSetInitialValue = false) => {
64
- setIsLoading(true);
67
+ generateUid.current = async (shouldSetInitialValue = false) => {
68
+ setIsLoading(true);
65
69
 
66
- try {
67
- const {
68
- data: { data },
69
- } = await post('/content-manager/uid/generate', {
70
- contentTypeUID,
71
- field: name,
72
- data: modifiedData,
73
- });
70
+ try {
71
+ const {
72
+ data: { data },
73
+ } = await post('/content-manager/uid/generate', {
74
+ contentTypeUID,
75
+ field: name,
76
+ data: modifiedData,
77
+ });
74
78
 
75
- onChange({ target: { name, value: data, type: 'text' } }, shouldSetInitialValue);
76
- setIsLoading(false);
77
- } catch (error) {
78
- setIsLoading(false);
79
- toggleNotification({
80
- type: 'warning',
81
- message: formatAPIError(error),
82
- });
83
- }
84
- };
85
-
86
- const checkAvailability = async () => {
87
- if (!value) {
88
- return;
89
- }
90
-
91
- setIsLoading(true);
79
+ onChange({ target: { name, value: data, type: 'text' } }, shouldSetInitialValue);
80
+ setIsLoading(false);
81
+ } catch (error) {
82
+ setIsLoading(false);
83
+ toggleNotification({
84
+ type: 'warning',
85
+ message: formatAPIError(error),
86
+ });
87
+ }
88
+ };
92
89
 
93
- try {
94
- const { data } = await post('/content-manager/uid/check-availability', {
95
- contentTypeUID,
96
- field: name,
97
- value: value ? value.trim() : '',
98
- });
90
+ const checkAvailability = async () => {
91
+ if (!value) {
92
+ return;
93
+ }
99
94
 
100
- setIsLoading(false);
101
- setAvailability(data);
102
- } catch (error) {
103
- setIsLoading(false);
104
- toggleNotification({
105
- type: 'warning',
106
- message: formatAPIError(error),
107
- });
108
- }
109
- };
95
+ setIsLoading(true);
110
96
 
111
- // FIXME: we need to find a better way to autofill the input when it is required.
112
- useEffect(() => {
113
- if (!value && attribute.required) {
114
- generateUid.current(true);
115
- }
116
- }, [attribute.required, generateUid, value]);
97
+ try {
98
+ const { data } = await post('/content-manager/uid/check-availability', {
99
+ contentTypeUID,
100
+ field: name,
101
+ value: value ? value.trim() : '',
102
+ });
117
103
 
118
- useEffect(() => {
119
- if (debouncedValue?.trim().match(UID_REGEX) && debouncedValue !== initialValue) {
120
- checkAvailability();
121
- }
104
+ setIsLoading(false);
105
+ setAvailability(data);
106
+ } catch (error) {
107
+ setIsLoading(false);
108
+ toggleNotification({
109
+ type: 'warning',
110
+ message: formatAPIError(error),
111
+ });
112
+ }
113
+ };
122
114
 
123
- if (!debouncedValue) {
124
- setAvailability(null);
125
- }
126
- // eslint-disable-next-line react-hooks/exhaustive-deps
127
- }, [initialValue, debouncedValue]);
115
+ // FIXME: we need to find a better way to autofill the input when it is required.
116
+ useEffect(() => {
117
+ if (!value && attribute.required) {
118
+ generateUid.current(true);
119
+ }
120
+ }, [attribute.required, generateUid, value]);
128
121
 
129
- useEffect(() => {
130
- let timer;
122
+ useEffect(() => {
123
+ if (debouncedValue?.trim().match(UID_REGEX) && debouncedValue !== initialValue) {
124
+ checkAvailability();
125
+ }
131
126
 
132
- if (availability?.isAvailable) {
133
- timer = setTimeout(() => {
127
+ if (!debouncedValue) {
134
128
  setAvailability(null);
135
- }, 4000);
136
- }
129
+ }
130
+ // eslint-disable-next-line react-hooks/exhaustive-deps
131
+ }, [initialValue, debouncedValue]);
132
+
133
+ useEffect(() => {
134
+ let timer;
137
135
 
138
- return () => {
139
- if (timer) {
140
- clearTimeout(timer);
136
+ if (availability?.isAvailable) {
137
+ timer = setTimeout(() => {
138
+ setAvailability(null);
139
+ }, 4000);
141
140
  }
142
- };
143
- }, [availability]);
144
141
 
145
- useEffect(() => {
146
- if (
147
- !isCustomized &&
148
- isCreation &&
149
- debouncedTargetFieldValue &&
150
- modifiedData[attribute.targetField] &&
151
- !value
152
- ) {
153
- generateUid.current(true);
154
- }
155
- // eslint-disable-next-line react-hooks/exhaustive-deps
156
- }, [debouncedTargetFieldValue, isCustomized, isCreation]);
142
+ return () => {
143
+ if (timer) {
144
+ clearTimeout(timer);
145
+ }
146
+ };
147
+ }, [availability]);
157
148
 
158
- const handleGenerateMouseEnter = () => {
159
- setRegenerateLabel(
160
- formatMessage({
161
- id: 'content-manager.components.uid.regenerate',
162
- defaultMessage: 'Regenerate',
163
- })
164
- );
165
- };
149
+ useEffect(() => {
150
+ if (
151
+ !isCustomized &&
152
+ isCreation &&
153
+ debouncedTargetFieldValue &&
154
+ modifiedData[attribute.targetField] &&
155
+ !value
156
+ ) {
157
+ generateUid.current(true);
158
+ }
159
+ // eslint-disable-next-line react-hooks/exhaustive-deps
160
+ }, [debouncedTargetFieldValue, isCustomized, isCreation]);
166
161
 
167
- const handleGenerateMouseLeave = () => {
168
- setRegenerateLabel(null);
169
- };
162
+ const handleGenerateMouseEnter = () => {
163
+ setRegenerateLabel(
164
+ formatMessage({
165
+ id: 'content-manager.components.uid.regenerate',
166
+ defaultMessage: 'Regenerate',
167
+ })
168
+ );
169
+ };
170
170
 
171
- const handleChange = (e) => {
172
- if (e.target.value && isCreation) {
173
- setIsCustomized(true);
174
- }
171
+ const handleGenerateMouseLeave = () => {
172
+ setRegenerateLabel(null);
173
+ };
175
174
 
176
- onChange(e);
177
- };
175
+ const handleChange = (e) => {
176
+ if (e.target.value && isCreation) {
177
+ setIsCustomized(true);
178
+ }
178
179
 
179
- return (
180
- <TextInput
181
- disabled={disabled}
182
- error={error}
183
- endAction={
184
- <Flex position="relative" gap={1}>
185
- {availability && !regenerateLabel && (
186
- <TextValidation
187
- alignItems="center"
188
- gap={1}
189
- justifyContent="flex-end"
190
- available={!!availability?.isAvailable}
191
- data-not-here-outer
192
- position="absolute"
193
- pointerEvents="none"
194
- right={6}
195
- width="100px"
196
- >
197
- {availability?.isAvailable ? <CheckCircle /> : <ExclamationMarkCircle />}
180
+ onChange(e);
181
+ };
198
182
 
199
- <Typography
200
- textColor={availability.isAvailable ? 'success600' : 'danger600'}
201
- variant="pi"
183
+ return (
184
+ <TextInput
185
+ ref={forwardedRef}
186
+ disabled={disabled}
187
+ error={error}
188
+ endAction={
189
+ <Flex position="relative" gap={1}>
190
+ {availability && !regenerateLabel && (
191
+ <TextValidation
192
+ alignItems="center"
193
+ gap={1}
194
+ justifyContent="flex-end"
195
+ available={!!availability?.isAvailable}
196
+ data-not-here-outer
197
+ position="absolute"
198
+ pointerEvents="none"
199
+ right={6}
200
+ width="100px"
202
201
  >
203
- {formatMessage(
204
- availability.isAvailable
205
- ? {
206
- id: 'content-manager.components.uid.available',
207
- defaultMessage: 'Available',
208
- }
209
- : {
210
- id: 'content-manager.components.uid.unavailable',
211
- defaultMessage: 'Unavailable',
212
- }
213
- )}
214
- </Typography>
215
- </TextValidation>
216
- )}
202
+ {availability?.isAvailable ? <CheckCircle /> : <ExclamationMarkCircle />}
217
203
 
218
- {!disabled && (
219
- <>
220
- {regenerateLabel && (
221
- <TextValidation alignItems="center" justifyContent="flex-end" gap={1}>
222
- <Typography textColor="primary600" variant="pi">
223
- {regenerateLabel}
224
- </Typography>
225
- </TextValidation>
226
- )}
204
+ <Typography
205
+ textColor={availability.isAvailable ? 'success600' : 'danger600'}
206
+ variant="pi"
207
+ >
208
+ {formatMessage(
209
+ availability.isAvailable
210
+ ? {
211
+ id: 'content-manager.components.uid.available',
212
+ defaultMessage: 'Available',
213
+ }
214
+ : {
215
+ id: 'content-manager.components.uid.unavailable',
216
+ defaultMessage: 'Unavailable',
217
+ }
218
+ )}
219
+ </Typography>
220
+ </TextValidation>
221
+ )}
227
222
 
228
- <FieldActionWrapper
229
- onClick={() => generateUid.current()}
230
- label={formatMessage({
231
- id: 'content-manager.components.uid.regenerate',
232
- defaultMessage: 'Regenerate',
233
- })}
234
- onMouseEnter={handleGenerateMouseEnter}
235
- onMouseLeave={handleGenerateMouseLeave}
236
- >
237
- {isLoading ? (
238
- <LoadingWrapper data-testid="loading-wrapper">
239
- <Loader />
240
- </LoadingWrapper>
241
- ) : (
242
- <Refresh />
223
+ {!disabled && (
224
+ <>
225
+ {regenerateLabel && (
226
+ <TextValidation alignItems="center" justifyContent="flex-end" gap={1}>
227
+ <Typography textColor="primary600" variant="pi">
228
+ {regenerateLabel}
229
+ </Typography>
230
+ </TextValidation>
243
231
  )}
244
- </FieldActionWrapper>
245
- </>
246
- )}
247
- </Flex>
248
- }
249
- hint={hint}
250
- label={label}
251
- labelAction={labelAction}
252
- name={name}
253
- onChange={handleChange}
254
- placeholder={formattedPlaceholder}
255
- value={value || ''}
256
- required={required}
257
- />
258
- );
259
- };
232
+
233
+ <FieldActionWrapper
234
+ onClick={() => generateUid.current()}
235
+ label={formatMessage({
236
+ id: 'content-manager.components.uid.regenerate',
237
+ defaultMessage: 'Regenerate',
238
+ })}
239
+ onMouseEnter={handleGenerateMouseEnter}
240
+ onMouseLeave={handleGenerateMouseLeave}
241
+ >
242
+ {isLoading ? (
243
+ <LoadingWrapper data-testid="loading-wrapper">
244
+ <Loader />
245
+ </LoadingWrapper>
246
+ ) : (
247
+ <Refresh />
248
+ )}
249
+ </FieldActionWrapper>
250
+ </>
251
+ )}
252
+ </Flex>
253
+ }
254
+ hint={hint}
255
+ label={label}
256
+ labelAction={labelAction}
257
+ name={name}
258
+ onChange={handleChange}
259
+ placeholder={formattedPlaceholder}
260
+ value={value || ''}
261
+ required={required}
262
+ />
263
+ );
264
+ }
265
+ );
260
266
 
261
267
  InputUID.propTypes = {
262
268
  attribute: PropTypes.shape({
@@ -12,6 +12,7 @@ import {
12
12
  VisuallyHidden,
13
13
  Combobox,
14
14
  } from '@strapi/design-system';
15
+ import { useFocusInputField } from '@strapi/helper-plugin';
15
16
  import { Cross, Refresh } from '@strapi/icons';
16
17
  import PropTypes from 'prop-types';
17
18
  import { FixedSizeList as List } from 'react-window';
@@ -86,6 +87,8 @@ const RelationInput = ({
86
87
  const listRef = useRef();
87
88
  const outerListRef = useRef();
88
89
 
90
+ const fieldRef = useFocusInputField(name);
91
+
89
92
  const { data } = searchResults;
90
93
 
91
94
  const relations = paginatedRelations.data;
@@ -206,6 +209,7 @@ const RelationInput = ({
206
209
  <Flex gap={3} justifyContent="space-between" alignItems="end" wrap="wrap">
207
210
  <Flex direction="column" alignItems="stretch" basis={size <= 6 ? '100%' : '70%'} gap={2}>
208
211
  <Combobox
212
+ ref={fieldRef}
209
213
  autocomplete="list"
210
214
  error={error}
211
215
  name={name}