@strapi/admin 4.4.0-rc.1 → 4.5.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 (203) hide show
  1. package/admin/src/StrapiApp.js +12 -4
  2. package/admin/src/components/Providers/index.js +10 -14
  3. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +11 -1
  4. package/admin/src/content-manager/components/DynamicTable/CellContent/RelationMultiple/index.js +11 -7
  5. package/admin/src/content-manager/components/DynamicTable/CellContent/index.js +6 -5
  6. package/admin/src/content-manager/components/DynamicTable/TableRows/index.js +5 -0
  7. package/admin/src/content-manager/components/DynamicTable/index.js +1 -1
  8. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +23 -17
  9. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +123 -24
  10. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +17 -1
  11. package/admin/src/content-manager/components/FieldTypeIcon/index.js +1 -31
  12. package/admin/src/content-manager/components/Inputs/index.js +22 -36
  13. package/admin/src/content-manager/components/RelationInput/RelationInput.js +364 -0
  14. package/admin/src/content-manager/components/{SelectWrapper → RelationInput/components}/Option.js +15 -25
  15. package/admin/src/content-manager/components/RelationInput/components/Relation.js +48 -0
  16. package/admin/src/content-manager/components/RelationInput/components/RelationItem.js +52 -0
  17. package/admin/src/content-manager/components/RelationInput/components/RelationList.js +52 -0
  18. package/admin/src/content-manager/components/RelationInput/constants.js +1 -0
  19. package/admin/src/content-manager/components/RelationInput/index.js +1 -0
  20. package/admin/src/content-manager/components/RelationInputDataManager/RelationInputDataManager.js +250 -0
  21. package/admin/src/content-manager/components/RelationInputDataManager/constants.js +8 -0
  22. package/admin/src/content-manager/components/RelationInputDataManager/index.js +1 -0
  23. package/admin/src/content-manager/components/{SelectWrapper → RelationInputDataManager}/utils/connect.js +0 -1
  24. package/admin/src/content-manager/components/RelationInputDataManager/utils/getRelationLink.js +5 -0
  25. package/admin/src/content-manager/components/{SelectWrapper → RelationInputDataManager}/utils/index.js +1 -0
  26. package/admin/src/content-manager/components/RelationInputDataManager/utils/normalizeRelations.js +58 -0
  27. package/admin/src/content-manager/components/{SelectWrapper → RelationInputDataManager}/utils/select.js +31 -1
  28. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +19 -2
  29. package/admin/src/content-manager/hooks/useFetchContentTypeLayout/utils/formatLayouts.js +7 -69
  30. package/admin/src/content-manager/hooks/useRelation/index.js +1 -0
  31. package/admin/src/content-manager/hooks/useRelation/useRelation.js +73 -0
  32. package/admin/src/content-manager/pages/EditSettingsView/components/DisplayedFields.js +4 -4
  33. package/admin/src/content-manager/pages/EditSettingsView/components/FormModal.js +2 -7
  34. package/admin/src/content-manager/pages/EditSettingsView/index.js +22 -51
  35. package/admin/src/content-manager/pages/EditSettingsView/reducer.js +0 -25
  36. package/admin/src/content-manager/pages/EditView/Header/index.js +3 -90
  37. package/admin/src/content-manager/pages/EditView/Header/utils/index.js +0 -1
  38. package/admin/src/content-manager/pages/EditView/Header/utils/select.js +0 -2
  39. package/admin/src/content-manager/pages/EditView/index.js +93 -155
  40. package/admin/src/content-manager/pages/ListView/FieldPicker/index.js +0 -1
  41. package/admin/src/content-manager/pages/ListView/index.js +0 -1
  42. package/admin/src/content-manager/pages/ListViewLayoutManager/index.js +0 -1
  43. package/admin/src/content-manager/utils/formatLayoutToApi.js +1 -3
  44. package/admin/src/core/apis/index.js +0 -1
  45. package/admin/src/hooks/index.js +0 -1
  46. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +197 -215
  47. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/schema.js +1 -2
  48. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DeleteButton/index.js +0 -1
  49. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/UpdateButton/index.js +36 -3
  50. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/index.js +11 -13
  51. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +2 -3
  52. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/utils/tableHeaders.js +8 -8
  53. package/admin/src/pages/SettingsPage/pages/ApiTokens/ProtectedEditView/index.js +1 -1
  54. package/admin/src/permissions/defaultPermissions.js +6 -2
  55. package/admin/src/translations/ar.json +0 -1
  56. package/admin/src/translations/ca.json +0 -9
  57. package/admin/src/translations/cs.json +0 -2
  58. package/admin/src/translations/de.json +0 -11
  59. package/admin/src/translations/dk.json +0 -11
  60. package/admin/src/translations/en.json +0 -28
  61. package/admin/src/translations/es.json +0 -11
  62. package/admin/src/translations/fr.json +0 -11
  63. package/admin/src/translations/gu.json +0 -11
  64. package/admin/src/translations/hi.json +0 -11
  65. package/admin/src/translations/hu.json +0 -10
  66. package/admin/src/translations/id.json +0 -10
  67. package/admin/src/translations/it.json +0 -10
  68. package/admin/src/translations/ja.json +0 -11
  69. package/admin/src/translations/ko.json +0 -11
  70. package/admin/src/translations/ml.json +0 -11
  71. package/admin/src/translations/ms.json +0 -2
  72. package/admin/src/translations/nl.json +0 -11
  73. package/admin/src/translations/pl.json +0 -11
  74. package/admin/src/translations/pt-BR.json +0 -11
  75. package/admin/src/translations/pt.json +0 -1
  76. package/admin/src/translations/ru.json +0 -10
  77. package/admin/src/translations/sa.json +0 -11
  78. package/admin/src/translations/sk.json +0 -10
  79. package/admin/src/translations/sv.json +0 -9
  80. package/admin/src/translations/th.json +0 -2
  81. package/admin/src/translations/tr.json +0 -1
  82. package/admin/src/translations/uk.json +0 -2
  83. package/admin/src/translations/vi.json +0 -1
  84. package/admin/src/translations/zh-Hans.json +0 -12
  85. package/admin/src/translations/zh.json +0 -11
  86. package/build/7098.40dcd7bf.chunk.js +1 -0
  87. package/build/8851.e4ac62f2.chunk.js +158 -0
  88. package/build/{8773.c06c24c0.chunk.js → 9311.7cc03f29.chunk.js} +291 -108
  89. package/build/{Admin-authenticatedApp.99b4868f.chunk.js → Admin-authenticatedApp.e39f36c9.chunk.js} +3 -3
  90. package/build/{Admin_homePage.6d5e3236.chunk.js → Admin_homePage.118926e0.chunk.js} +1 -1
  91. package/build/{Admin_profilePage.da32abbc.chunk.js → Admin_profilePage.9d50ac44.chunk.js} +1 -1
  92. package/build/{Admin_settingsPage.98e2a62b.chunk.js → Admin_settingsPage.98a711e5.chunk.js} +16 -16
  93. package/build/admin-app.4f7618a9.chunk.js +112 -0
  94. package/build/admin-edit-roles-page.554ba3fa.chunk.js +1 -0
  95. package/build/api-tokens-create-page.4c262d6e.chunk.js +1 -0
  96. package/build/api-tokens-edit-page.10a9d368.chunk.js +1 -0
  97. package/build/api-tokens-list-page.442c9f3c.chunk.js +15 -0
  98. package/build/{ar-json.d4cb26d9.chunk.js → ar-json.3489463d.chunk.js} +1 -1
  99. package/build/{ca-json.d16c1d28.chunk.js → ca-json.a16899ae.chunk.js} +1 -1
  100. package/build/content-manager.7d57c9d1.chunk.js +1200 -0
  101. package/build/content-type-builder-list-view.8cc534e0.chunk.js +194 -0
  102. package/build/content-type-builder-translation-en-json.201bfb78.chunk.js +1 -0
  103. package/build/content-type-builder.684df7a4.chunk.js +142 -0
  104. package/build/{cs-json.c8f28ba8.chunk.js → cs-json.ce49da5c.chunk.js} +1 -1
  105. package/build/{de-json.a9b514dc.chunk.js → de-json.aa6026b3.chunk.js} +1 -1
  106. package/build/{dk-json.09e8d145.chunk.js → dk-json.fac2bcfb.chunk.js} +1 -1
  107. package/build/en-json.0c69c7d7.chunk.js +1 -0
  108. package/build/{es-json.3a9c7c09.chunk.js → es-json.d672e181.chunk.js} +1 -1
  109. package/build/{fr-json.4ed1fc2c.chunk.js → fr-json.71a16175.chunk.js} +1 -1
  110. package/build/{gu-json.d8311297.chunk.js → gu-json.ca345cd1.chunk.js} +1 -1
  111. package/build/{hi-json.0edb8d29.chunk.js → hi-json.50c7e6d4.chunk.js} +1 -1
  112. package/build/{hu-json.7855529a.chunk.js → hu-json.e0521dcc.chunk.js} +1 -1
  113. package/build/{id-json.df9618f2.chunk.js → id-json.4b1ff8d6.chunk.js} +1 -1
  114. package/build/index.html +1 -1
  115. package/build/{it-json.a21bf078.chunk.js → it-json.86bac220.chunk.js} +1 -1
  116. package/build/{ja-json.7b0d9067.chunk.js → ja-json.4e44e36b.chunk.js} +1 -1
  117. package/build/{ko-json.983c1f8f.chunk.js → ko-json.1003756e.chunk.js} +1 -1
  118. package/build/main.b47db1a3.js +9337 -0
  119. package/build/{ml-json.8dd021c8.chunk.js → ml-json.c7774425.chunk.js} +1 -1
  120. package/build/{ms-json.836ed013.chunk.js → ms-json.ed51e902.chunk.js} +1 -1
  121. package/build/{nl-json.29d2eb37.chunk.js → nl-json.f58ea235.chunk.js} +1 -1
  122. package/build/{pl-json.1f04f00c.chunk.js → pl-json.fed96aba.chunk.js} +1 -1
  123. package/build/{pt-BR-json.b4bc8efe.chunk.js → pt-BR-json.073799ab.chunk.js} +1 -1
  124. package/build/{pt-json.c23020ab.chunk.js → pt-json.3161ca22.chunk.js} +1 -1
  125. package/build/{ru-json.7ab40ccf.chunk.js → ru-json.7ad2cbbf.chunk.js} +1 -1
  126. package/build/{runtime~main.4ee06902.js → runtime~main.feeac6d3.js} +1 -1
  127. package/build/{sa-json.c5a9f4ea.chunk.js → sa-json.f0f704f0.chunk.js} +1 -1
  128. package/build/{sk-json.e4c24c4e.chunk.js → sk-json.a848961b.chunk.js} +1 -1
  129. package/build/sso-settings-page.445184e0.chunk.js +1 -0
  130. package/build/{sv-json.c3f471ae.chunk.js → sv-json.b038acbe.chunk.js} +1 -1
  131. package/build/{th-json.a59ffb32.chunk.js → th-json.72e8de3d.chunk.js} +1 -1
  132. package/build/{tr-json.276e59fe.chunk.js → tr-json.9c44ea0c.chunk.js} +1 -1
  133. package/build/{uk-json.5b5b9c27.chunk.js → uk-json.c4cd2e24.chunk.js} +1 -1
  134. package/build/{vi-json.bf3424be.chunk.js → vi-json.f7890025.chunk.js} +1 -1
  135. package/build/{webhook-edit-page.9e46fc3f.chunk.js → webhook-edit-page.d2ea3351.chunk.js} +1 -1
  136. package/build/{zh-Hans-json.9c99f8d4.chunk.js → zh-Hans-json.03d2bda1.chunk.js} +1 -1
  137. package/build/{zh-json.451a0271.chunk.js → zh-json.3d0cc664.chunk.js} +1 -1
  138. package/package.json +7 -8
  139. package/server/bootstrap.js +1 -19
  140. package/server/config/admin-actions.js +0 -20
  141. package/server/content-types/api-token.js +1 -25
  142. package/server/content-types/index.js +0 -1
  143. package/server/controllers/api-token.js +1 -24
  144. package/server/controllers/index.js +0 -1
  145. package/server/routes/api-tokens.js +0 -11
  146. package/server/routes/index.js +0 -2
  147. package/server/services/api-token.js +29 -310
  148. package/server/services/constants.js +0 -10
  149. package/server/services/permission/engine-hooks.js +82 -0
  150. package/server/services/permission/engine.js +226 -36
  151. package/server/services/permission.js +1 -4
  152. package/server/strategies/admin.js +1 -7
  153. package/server/strategies/api-token.js +11 -71
  154. package/server/validation/api-tokens.js +2 -12
  155. package/admin/src/content-manager/components/SelectMany/ListItem.js +0 -102
  156. package/admin/src/content-manager/components/SelectMany/index.js +0 -148
  157. package/admin/src/content-manager/components/SelectOne/SingleValue.js +0 -67
  158. package/admin/src/content-manager/components/SelectOne/index.js +0 -97
  159. package/admin/src/content-manager/components/SelectWrapper/Label.js +0 -60
  160. package/admin/src/content-manager/components/SelectWrapper/index.js +0 -356
  161. package/admin/src/content-manager/pages/EditSettingsView/components/RelationalFieldButton.js +0 -135
  162. package/admin/src/content-manager/pages/EditSettingsView/components/RelationalFields.js +0 -103
  163. package/admin/src/content-manager/pages/EditView/Header/utils/getDraftRelations.js +0 -62
  164. package/admin/src/contexts/ApiTokenPermissions/index.js +0 -24
  165. package/admin/src/core/apis/CustomFields.js +0 -80
  166. package/admin/src/hooks/useRegenerate/index.js +0 -34
  167. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js +0 -56
  168. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/getMethodColor.js +0 -41
  169. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/BoundRoute/index.js +0 -72
  170. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/CheckBoxWrapper.js +0 -30
  171. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js +0 -150
  172. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ContenTypesSection/index.js +0 -37
  173. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormApiTokenContainer/index.js +0 -254
  174. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormBody/index.js +0 -77
  175. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/FormHead/index.js +0 -85
  176. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Permissions/index.js +0 -40
  177. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Regenerate/index.js +0 -68
  178. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/init.js +0 -13
  179. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/reducer.js +0 -72
  180. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/getDateOfExpiration.js +0 -16
  181. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/index.js +0 -5
  182. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/utils/transformPermissionsData.js +0 -36
  183. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/DefaultButton/index.js +0 -63
  184. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/DynamicTable/ReadButton/index.js +0 -19
  185. package/build/1669.d1b29c28.chunk.js +0 -1
  186. package/build/4318.7d167b58.chunk.js +0 -30
  187. package/build/524.40377968.chunk.js +0 -644
  188. package/build/7379.d246dd38.chunk.js +0 -1
  189. package/build/admin-app.a61d5c2e.chunk.js +0 -112
  190. package/build/admin-edit-roles-page.4dd6bcb9.chunk.js +0 -1
  191. package/build/api-tokens-create-page.93dd0689.chunk.js +0 -1
  192. package/build/api-tokens-edit-page.b0adac81.chunk.js +0 -1
  193. package/build/api-tokens-list-page.bb36535f.chunk.js +0 -16
  194. package/build/content-manager.feb0d540.chunk.js +0 -1178
  195. package/build/content-type-builder-list-view.5b3cd768.chunk.js +0 -194
  196. package/build/content-type-builder-translation-en-json.f985c9c4.chunk.js +0 -1
  197. package/build/content-type-builder.a684b2e8.chunk.js +0 -145
  198. package/build/en-json.a9918c93.chunk.js +0 -1
  199. package/build/main.e4065f58.js +0 -9337
  200. package/build/sso-settings-page.9ceb0140.chunk.js +0 -1
  201. package/server/content-types/api-token-permission.js +0 -36
  202. package/server/controllers/content-api.js +0 -15
  203. package/server/routes/content-api.js +0 -20
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import styled from 'styled-components';
4
+ import { Box } from '@strapi/design-system/Box';
5
+ import { Stack } from '@strapi/design-system/Stack';
6
+
7
+ const ShadowBox = styled(Box)`
8
+ position: relative;
9
+ overflow-x: hidden;
10
+ overflow-y: auto;
11
+
12
+ &:before,
13
+ &:after {
14
+ position: absolute;
15
+ width: 100%;
16
+ height: 4px;
17
+ z-index: 1;
18
+ }
19
+
20
+ &:before {
21
+ /* TODO: as for DS Table component we would need this to be handled by the DS theme */
22
+ content: ${({ overflowDirection }) =>
23
+ overflowDirection === 'top-bottom' || overflowDirection === 'top' ? "''" : undefined};
24
+ background: linear-gradient(rgba(33, 33, 52, 0.1) 0%, rgba(0, 0, 0, 0) 100%);
25
+ top: 0;
26
+ }
27
+
28
+ &:after {
29
+ /* TODO: as for DS Table component we would need this to be handled by the DS theme */
30
+ content: ${({ overflowDirection }) =>
31
+ overflowDirection === 'top-bottom' || overflowDirection === 'bottom' ? "''" : undefined};
32
+ background: linear-gradient(rgba(0, 0, 0, 0) 0%, rgba(33, 33, 52, 0.1) 100%);
33
+ bottom: 0;
34
+ }
35
+ `;
36
+
37
+ export const RelationList = ({ children, overflow, ...props }) => {
38
+ return (
39
+ <ShadowBox overflowDirection={overflow} {...props}>
40
+ <Stack spacing={1}>{children}</Stack>
41
+ </ShadowBox>
42
+ );
43
+ };
44
+
45
+ RelationList.defaultProps = {
46
+ overflow: '',
47
+ };
48
+
49
+ RelationList.propTypes = {
50
+ children: PropTypes.node.isRequired,
51
+ overflow: PropTypes.oneOf(['top-bottom', 'bottom', 'top', '']),
52
+ };
@@ -0,0 +1 @@
1
+ export const RELATION_ITEM_HEIGHT = 58;
@@ -0,0 +1 @@
1
+ export { default as RelationInput } from './RelationInput';
@@ -0,0 +1,250 @@
1
+ import PropTypes from 'prop-types';
2
+ import React, { memo, useEffect, useMemo } from 'react';
3
+ import { useIntl } from 'react-intl';
4
+
5
+ import { useCMEditViewDataManager, NotAllowedInput, useQueryParams } from '@strapi/helper-plugin';
6
+
7
+ import { RelationInput } from '../RelationInput';
8
+ import { useRelation } from '../../hooks/useRelation';
9
+ import { connect, select, normalizeRelations } from './utils';
10
+ import { PUBLICATION_STATES, RELATIONS_TO_DISPLAY, SEARCH_RESULTS_TO_DISPLAY } from './constants';
11
+ import { getTrad } from '../../utils';
12
+
13
+ export const RelationInputDataManger = ({
14
+ editable,
15
+ description,
16
+ intlLabel,
17
+ isCreatingEntry,
18
+ isFieldAllowed,
19
+ isFieldReadable,
20
+ labelAction,
21
+ mainField,
22
+ name,
23
+ queryInfos: { endpoints, defaultParams, shouldDisplayRelationLink },
24
+ placeholder,
25
+ required,
26
+ relationType,
27
+ size,
28
+ targetModel,
29
+ }) => {
30
+ const { formatMessage } = useIntl();
31
+ const { connectRelation, disconnectRelation, loadRelation, modifiedData, slug, initialData } =
32
+ useCMEditViewDataManager();
33
+ const [{ query }] = useQueryParams();
34
+
35
+ const { relations, search, searchFor } = useRelation(`${slug}-${name}-${initialData?.id ?? ''}`, {
36
+ relation: {
37
+ enabled: initialData[name]?.count !== 0 && !!endpoints.relation,
38
+ endpoint: endpoints.relation,
39
+ pageParams: {
40
+ ...defaultParams,
41
+ locale: query?.plugins?.i18n?.locale,
42
+ pageSize: RELATIONS_TO_DISPLAY,
43
+ },
44
+ },
45
+
46
+ search: {
47
+ endpoint: endpoints.search,
48
+ pageParams: {
49
+ ...defaultParams,
50
+ entityId: isCreatingEntry ? undefined : initialData.id,
51
+ locale: query?.plugins?.i18n?.locale,
52
+ pageSize: SEARCH_RESULTS_TO_DISPLAY,
53
+ },
54
+ },
55
+ });
56
+
57
+ const stringifiedRelations = JSON.stringify(relations);
58
+ const normalizedRelations = useMemo(
59
+ () =>
60
+ normalizeRelations(relations, {
61
+ modifiedData: modifiedData?.[name],
62
+ mainFieldName: mainField.name,
63
+ shouldAddLink: shouldDisplayRelationLink,
64
+ targetModel,
65
+ }),
66
+ // eslint-disable-next-line react-hooks/exhaustive-deps
67
+ [
68
+ stringifiedRelations,
69
+ modifiedData,
70
+ name,
71
+ mainField.name,
72
+ shouldDisplayRelationLink,
73
+ targetModel,
74
+ ]
75
+ );
76
+
77
+ useEffect(() => {
78
+ if (relations.status === 'success') {
79
+ loadRelation({
80
+ target: { name, value: normalizedRelations.data.pages.flat() },
81
+ });
82
+ }
83
+ // eslint-disable-next-line react-hooks/exhaustive-deps
84
+ }, [loadRelation, relations.status, stringifiedRelations, name]);
85
+
86
+ const isMorph = useMemo(() => relationType.toLowerCase().includes('morph'), [relationType]);
87
+ const isSingleRelation = [
88
+ 'oneWay',
89
+ 'oneToOne',
90
+ 'manyToOne',
91
+ 'oneToManyMorph',
92
+ 'oneToOneMorph',
93
+ ].includes(relationType);
94
+
95
+ const isDisabled = useMemo(() => {
96
+ if (isMorph) {
97
+ return true;
98
+ }
99
+
100
+ if (!isCreatingEntry) {
101
+ return (!isFieldAllowed && isFieldReadable) || !editable;
102
+ }
103
+
104
+ return !editable;
105
+ }, [isMorph, isCreatingEntry, editable, isFieldAllowed, isFieldReadable]);
106
+
107
+ const handleRelationAdd = (relation) => {
108
+ connectRelation({ target: { name, value: relation, replace: isSingleRelation } });
109
+ };
110
+
111
+ const handleRelationRemove = (relation) => {
112
+ disconnectRelation({ target: { name, value: relation } });
113
+ };
114
+
115
+ const handleRelationLoadMore = () => {
116
+ relations.fetchNextPage();
117
+ };
118
+
119
+ const handleSearch = (term) => {
120
+ searchFor(term, { idsToOmit: modifiedData?.[name]?.connect?.map((relation) => relation.id) });
121
+ };
122
+
123
+ const handleOpenSearch = () => {
124
+ searchFor('', { idsToOmit: modifiedData?.[name]?.connect?.map((relation) => relation.id) });
125
+ };
126
+
127
+ const handleSearchMore = () => {
128
+ search.fetchNextPage();
129
+ };
130
+
131
+ if (
132
+ (!isFieldAllowed && isCreatingEntry) ||
133
+ (!isCreatingEntry && !isFieldAllowed && !isFieldReadable)
134
+ ) {
135
+ return <NotAllowedInput intlLabel={intlLabel} labelAction={labelAction} />;
136
+ }
137
+
138
+ return (
139
+ <RelationInput
140
+ description={description}
141
+ disabled={isDisabled}
142
+ id={name}
143
+ label={`${formatMessage({
144
+ id: intlLabel.id,
145
+ defaultMessage: intlLabel.defaultMessage,
146
+ })} ${initialData[name]?.count !== undefined ? `(${initialData[name].count})` : ''}`}
147
+ labelAction={labelAction}
148
+ labelLoadMore={
149
+ // TODO: only display if there are more; derive from count
150
+ !isCreatingEntry &&
151
+ formatMessage({
152
+ id: getTrad('relation.loadMore'),
153
+ defaultMessage: 'Load More',
154
+ })
155
+ }
156
+ listHeight={320}
157
+ loadingMessage={() =>
158
+ formatMessage({
159
+ id: getTrad('relation.isLoading'),
160
+ defaultMessage: 'Relations are loading',
161
+ })
162
+ }
163
+ name={name}
164
+ numberOfRelationsToDisplay={RELATIONS_TO_DISPLAY}
165
+ onRelationAdd={(relation) => handleRelationAdd(relation)}
166
+ onRelationRemove={(relation) => handleRelationRemove(relation)}
167
+ onRelationLoadMore={() => handleRelationLoadMore()}
168
+ onSearch={(term) => handleSearch(term)}
169
+ onSearchNextPage={() => handleSearchMore()}
170
+ onSearchClose={() => {}}
171
+ onSearchOpen={handleOpenSearch}
172
+ placeholder={formatMessage(
173
+ placeholder || {
174
+ id: getTrad('relation.add'),
175
+ defaultMessage: 'Add relation',
176
+ }
177
+ )}
178
+ publicationStateTranslations={{
179
+ [PUBLICATION_STATES.DRAFT]: formatMessage({
180
+ id: getTrad('relation.publicationState.draft'),
181
+ defaultMessage: 'Draft',
182
+ }),
183
+
184
+ [PUBLICATION_STATES.PUBLISHED]: formatMessage({
185
+ id: getTrad('relation.publicationState.published'),
186
+ defaultMessage: 'Published',
187
+ }),
188
+ }}
189
+ relations={normalizedRelations}
190
+ required={required}
191
+ searchResults={normalizeRelations(search, {
192
+ mainFieldName: mainField.name,
193
+ })}
194
+ size={size}
195
+ />
196
+ );
197
+ };
198
+
199
+ RelationInputDataManger.defaultProps = {
200
+ editable: true,
201
+ description: '',
202
+ labelAction: null,
203
+ isFieldAllowed: true,
204
+ placeholder: null,
205
+ required: false,
206
+ };
207
+
208
+ RelationInputDataManger.propTypes = {
209
+ editable: PropTypes.bool,
210
+ description: PropTypes.string,
211
+ intlLabel: PropTypes.shape({
212
+ id: PropTypes.string.isRequired,
213
+ defaultMessage: PropTypes.string.isRequired,
214
+ values: PropTypes.object,
215
+ }).isRequired,
216
+ labelAction: PropTypes.element,
217
+ isCreatingEntry: PropTypes.bool.isRequired,
218
+ isFieldAllowed: PropTypes.bool,
219
+ isFieldReadable: PropTypes.bool.isRequired,
220
+ mainField: PropTypes.shape({
221
+ name: PropTypes.string.isRequired,
222
+ schema: PropTypes.shape({
223
+ type: PropTypes.string.isRequired,
224
+ }).isRequired,
225
+ }).isRequired,
226
+ name: PropTypes.string.isRequired,
227
+ placeholder: PropTypes.shape({
228
+ id: PropTypes.string.isRequired,
229
+ defaultMessage: PropTypes.string.isRequired,
230
+ values: PropTypes.object,
231
+ }),
232
+ required: PropTypes.bool,
233
+ relationType: PropTypes.string.isRequired,
234
+ size: PropTypes.number.isRequired,
235
+ targetModel: PropTypes.string.isRequired,
236
+ queryInfos: PropTypes.shape({
237
+ defaultParams: PropTypes.shape({
238
+ _component: PropTypes.string,
239
+ }),
240
+ endpoints: PropTypes.shape({
241
+ relation: PropTypes.string,
242
+ search: PropTypes.string.isRequired,
243
+ }).isRequired,
244
+ shouldDisplayRelationLink: PropTypes.bool.isRequired,
245
+ }).isRequired,
246
+ };
247
+
248
+ const Memoized = memo(RelationInputDataManger);
249
+
250
+ export default connect(Memoized, select);
@@ -0,0 +1,8 @@
1
+ export const PUBLICATION_STATES = {
2
+ DRAFT: 'draft',
3
+ PUBLISHED: 'published',
4
+ };
5
+
6
+ export const RELATIONS_TO_DISPLAY = 5;
7
+
8
+ export const SEARCH_RESULTS_TO_DISPLAY = 10;
@@ -0,0 +1 @@
1
+ export { default as RelationInputDataManager } from './RelationInputDataManager';
@@ -2,7 +2,6 @@ import React from 'react';
2
2
 
3
3
  function connect(WrappedComponent, select) {
4
4
  return (props) => {
5
- // eslint-disable-next-line react/prop-types
6
5
  const selectors = select(props);
7
6
 
8
7
  return <WrappedComponent {...props} {...selectors} />;
@@ -0,0 +1,5 @@
1
+ import { getRequestUrl } from '../../../utils';
2
+
3
+ export function getRelationLink(targetModel, id) {
4
+ return `${getRequestUrl(`collectionType/${targetModel}/${id ?? ''}`)}`;
5
+ }
@@ -1,2 +1,3 @@
1
1
  export { default as connect } from './connect';
2
2
  export { default as select } from './select';
3
+ export { normalizeRelations } from './normalizeRelations';
@@ -0,0 +1,58 @@
1
+ import { getRelationLink } from './getRelationLink';
2
+
3
+ import { PUBLICATION_STATES } from '../constants';
4
+
5
+ const normalizeRelation = (relation, { shouldAddLink, mainFieldName, targetModel }) => {
6
+ const nextRelation = { ...relation };
7
+
8
+ if (shouldAddLink) {
9
+ nextRelation.href = getRelationLink(targetModel, nextRelation.id);
10
+ }
11
+
12
+ nextRelation.publicationState = false;
13
+
14
+ if (nextRelation?.publishedAt !== undefined) {
15
+ nextRelation.publicationState = nextRelation.publishedAt
16
+ ? PUBLICATION_STATES.PUBLISHED
17
+ : PUBLICATION_STATES.DRAFT;
18
+ }
19
+
20
+ nextRelation.mainField = nextRelation[mainFieldName];
21
+
22
+ return nextRelation;
23
+ };
24
+
25
+ export const normalizeRelations = (
26
+ relations,
27
+ { modifiedData = {}, shouldAddLink = false, mainFieldName, targetModel }
28
+ ) => {
29
+ return {
30
+ ...relations,
31
+ data: {
32
+ pages:
33
+ [
34
+ ...(relations?.data?.pages ?? []),
35
+ ...(modifiedData?.connect ? [{ results: modifiedData.connect }] : []),
36
+ ]
37
+ ?.map((page) =>
38
+ page?.results
39
+ .filter(
40
+ (relation) =>
41
+ !modifiedData?.disconnect?.find(
42
+ (disconnectRelation) => disconnectRelation.id === relation.id
43
+ )
44
+ )
45
+ .map((relation) =>
46
+ normalizeRelation(relation, {
47
+ modifiedData,
48
+ shouldAddLink,
49
+ mainFieldName,
50
+ targetModel,
51
+ })
52
+ )
53
+ .filter(Boolean)
54
+ )
55
+ ?.filter((page) => page.length > 0) ?? [],
56
+ },
57
+ };
58
+ };
@@ -1,12 +1,17 @@
1
1
  import { useMemo } from 'react';
2
2
  import { useCMEditViewDataManager } from '@strapi/helper-plugin';
3
3
 
4
- function useSelect({ isUserAllowedToEditField, isUserAllowedToReadField, name }) {
4
+ import { getRequestUrl } from '../../../utils';
5
+
6
+ function useSelect({ isUserAllowedToEditField, isUserAllowedToReadField, name, queryInfos }) {
5
7
  const {
6
8
  isCreatingEntry,
7
9
  createActionAllowedFields,
8
10
  readActionAllowedFields,
9
11
  updateActionAllowedFields,
12
+ slug,
13
+ initialData,
14
+ isSingleType,
10
15
  } = useCMEditViewDataManager();
11
16
 
12
17
  const isFieldAllowed = useMemo(() => {
@@ -35,7 +40,32 @@ function useSelect({ isUserAllowedToEditField, isUserAllowedToReadField, name })
35
40
  return allowedFields.includes(name);
36
41
  }, [isCreatingEntry, isUserAllowedToReadField, name, readActionAllowedFields]);
37
42
 
43
+ // /content-manager/[collection-type]/[content-type]/[id]/[field-name]
44
+ const relationFetchEndpoint = useMemo(() => {
45
+ const collectionTypePrefix = isSingleType ? 'single-types' : 'collection-types';
46
+
47
+ if (isCreatingEntry) {
48
+ return null;
49
+ }
50
+
51
+ return getRequestUrl(
52
+ `${collectionTypePrefix}/${slug}/${initialData.id}/${name.split('.').at(-1)}`
53
+ );
54
+ }, [isCreatingEntry, slug, initialData, name, isSingleType]);
55
+
56
+ // /content-manager/relations/[content-type]/[field-name]
57
+ const relationSearchEndpoint = useMemo(() => {
58
+ return getRequestUrl(`relations/${slug}/${name.split('.').at(-1)}`);
59
+ }, [slug, name]);
60
+
38
61
  return {
62
+ queryInfos: {
63
+ ...queryInfos,
64
+ endpoints: {
65
+ search: relationSearchEndpoint,
66
+ relation: relationFetchEndpoint,
67
+ },
68
+ },
39
69
  isCreatingEntry,
40
70
  isFieldAllowed,
41
71
  isFieldReadable,
@@ -1,5 +1,6 @@
1
1
  import { memo, useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { useHistory } from 'react-router-dom';
3
+ import { useQueryClient } from 'react-query';
3
4
  import get from 'lodash/get';
4
5
  import {
5
6
  useTracking,
@@ -28,6 +29,7 @@ import buildQueryString from '../../pages/ListView/utils/buildQueryString';
28
29
 
29
30
  // This container is used to handle the CRUD
30
31
  const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
32
+ const queryClient = useQueryClient();
31
33
  const { trackUsage } = useTracking();
32
34
  const { push } = useHistory();
33
35
  const { setCurrentStep } = useGuidedTour();
@@ -202,6 +204,9 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
202
204
 
203
205
  setCurrentStep('contentManager.success');
204
206
 
207
+ // TODO: need to find a better place, or a better abstraction
208
+ queryClient.invalidateQueries(['relation']);
209
+
205
210
  dispatch(submitSucceeded(cleanReceivedData(data)));
206
211
  setIsCreatingEntry(false);
207
212
 
@@ -218,7 +223,16 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
218
223
  return Promise.reject(err);
219
224
  }
220
225
  },
221
- [cleanReceivedData, displayErrors, slug, dispatch, rawQuery, toggleNotification, setCurrentStep]
226
+ [
227
+ cleanReceivedData,
228
+ displayErrors,
229
+ slug,
230
+ dispatch,
231
+ rawQuery,
232
+ toggleNotification,
233
+ setCurrentStep,
234
+ queryClient,
235
+ ]
222
236
  );
223
237
  const onPublish = useCallback(async () => {
224
238
  try {
@@ -267,6 +281,9 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
267
281
 
268
282
  trackUsageRef.current('didEditEntry', { trackerProperty });
269
283
 
284
+ // TODO: need to find a better place, or a better abstraction
285
+ queryClient.invalidateQueries(['relation']);
286
+
270
287
  dispatch(submitSucceeded(cleanReceivedData(data)));
271
288
 
272
289
  dispatch(setStatus('resolved'));
@@ -282,7 +299,7 @@ const SingleTypeFormWrapper = ({ allLayoutData, children, slug }) => {
282
299
  return Promise.reject(err);
283
300
  }
284
301
  },
285
- [cleanReceivedData, displayErrors, slug, dispatch, rawQuery, toggleNotification]
302
+ [cleanReceivedData, displayErrors, slug, dispatch, rawQuery, toggleNotification, queryClient]
286
303
  );
287
304
 
288
305
  // The publish and unpublish method could be refactored but let's leave the duplication for now
@@ -1,49 +1,16 @@
1
1
  import { cloneDeep, get, set } from 'lodash';
2
- import { getRequestUrl, mergeMetasWithSchema } from '../../../utils';
2
+ import { mergeMetasWithSchema } from '../../../utils';
3
3
 
4
4
  const getRelationModel = (targetModel, models) => models.find((model) => model.uid === targetModel);
5
5
 
6
- // editRelations is an array of strings...
7
- const formatEditRelationsLayoutWithMetas = (contentTypeConfiguration, models) => {
8
- const formatted = contentTypeConfiguration.layouts.editRelations.reduce((acc, current) => {
9
- const fieldSchema = get(contentTypeConfiguration, ['attributes', current], {});
10
- const targetModelUID = get(
11
- contentTypeConfiguration,
12
- ['attributes', current, 'targetModel'],
13
- null
14
- );
15
- const targetModelSchema = getRelationModel(targetModelUID, models);
16
- const targetModelPluginOptions = targetModelSchema.pluginOptions || {};
17
- const metadatas = get(contentTypeConfiguration, ['metadatas', current, 'edit'], {});
18
- const size = 6;
19
-
20
- const queryInfos = generateRelationQueryInfos(contentTypeConfiguration, current, models);
21
-
22
- acc.push({
23
- name: current,
24
- size,
25
- fieldSchema,
26
- metadatas,
27
- queryInfos,
28
- targetModelPluginOptions,
29
- });
30
-
31
- return acc;
32
- }, []);
33
-
34
- return formatted;
35
- };
36
-
37
6
  const formatLayouts = (initialData, models) => {
38
7
  const data = createMetasSchema(initialData, models);
39
8
 
40
9
  const formattedCTEditLayout = formatLayoutWithMetas(data.contentType, null, models);
41
10
  const ctUid = data.contentType.uid;
42
- const formattedEditRelationsLayout = formatEditRelationsLayoutWithMetas(data.contentType, models);
43
11
  const formattedListLayout = formatListLayoutWithMetas(data.contentType, data.components);
44
12
 
45
13
  set(data, ['contentType', 'layouts', 'edit'], formattedCTEditLayout);
46
- set(data, ['contentType', 'layouts', 'editRelations'], formattedEditRelationsLayout);
47
14
  set(data, ['contentType', 'layouts', 'list'], formattedListLayout);
48
15
 
49
16
  Object.keys(data.components).forEach((compoUID) => {
@@ -126,7 +93,6 @@ const formatLayoutWithMetas = (contentTypeConfiguration, ctUid, models) => {
126
93
  ? generateRelationQueryInfosForComponents(
127
94
  contentTypeConfiguration,
128
95
  attribute.name,
129
- ctUid,
130
96
  models
131
97
  )
132
98
  : generateRelationQueryInfos(contentTypeConfiguration, attribute.name, models);
@@ -155,7 +121,6 @@ const formatListLayoutWithMetas = (contentTypeConfiguration, components) => {
155
121
 
156
122
  if (type === 'relation') {
157
123
  const queryInfos = {
158
- endPoint: `collection-types/${contentTypeConfiguration.uid}`,
159
124
  defaultParams: {},
160
125
  };
161
126
 
@@ -194,51 +159,25 @@ const formatListLayoutWithMetas = (contentTypeConfiguration, components) => {
194
159
  };
195
160
 
196
161
  const generateRelationQueryInfos = (contentTypeConfiguration, fieldName, models) => {
197
- const uid = contentTypeConfiguration.uid;
198
- const endPoint = getRequestUrl(`relations/${uid}/${fieldName}`);
199
- const mainField = get(
200
- contentTypeConfiguration,
201
- ['metadatas', fieldName, 'edit', 'mainField', 'name'],
202
- ''
203
- );
204
162
  const targetModel = get(contentTypeConfiguration, ['attributes', fieldName, 'targetModel'], '');
205
- const shouldDisplayRelationLink = getDisplayedModels(models).indexOf(targetModel) !== -1;
163
+ const shouldDisplayRelationLink = getDisplayedModels(models).includes(targetModel);
206
164
 
207
- const queryInfos = {
208
- endPoint,
209
- containsKey: `${mainField}`,
165
+ return {
210
166
  defaultParams: {},
211
167
  shouldDisplayRelationLink,
212
168
  };
213
-
214
- return queryInfos;
215
169
  };
216
170
 
217
- const generateRelationQueryInfosForComponents = (
218
- contentTypeConfiguration,
219
- fieldName,
220
- ctUid,
221
- models
222
- ) => {
223
- const endPoint = getRequestUrl(`relations/${ctUid}/${fieldName}`);
224
- const mainField = get(
225
- contentTypeConfiguration,
226
- ['metadatas', fieldName, 'edit', 'mainField', 'name'],
227
- ''
228
- );
171
+ const generateRelationQueryInfosForComponents = (contentTypeConfiguration, fieldName, models) => {
229
172
  const targetModel = get(contentTypeConfiguration, ['attributes', fieldName, 'targetModel'], '');
230
- const shouldDisplayRelationLink = getDisplayedModels(models).indexOf(targetModel) !== -1;
173
+ const shouldDisplayRelationLink = getDisplayedModels(models).includes(targetModel);
231
174
 
232
- const queryInfos = {
233
- endPoint,
234
- containsKey: `${mainField}`,
175
+ return {
235
176
  defaultParams: {
236
- _component: contentTypeConfiguration.uid,
177
+ component: contentTypeConfiguration.uid,
237
178
  },
238
179
  shouldDisplayRelationLink,
239
180
  };
240
-
241
- return queryInfos;
242
181
  };
243
182
 
244
183
  const getDisplayedModels = (models) =>
@@ -246,7 +185,6 @@ const getDisplayedModels = (models) =>
246
185
 
247
186
  export default formatLayouts;
248
187
  export {
249
- formatEditRelationsLayoutWithMetas,
250
188
  formatLayoutWithMetas,
251
189
  formatListLayoutWithMetas,
252
190
  generateRelationQueryInfos,
@@ -0,0 +1 @@
1
+ export { useRelation } from './useRelation';