@strapi/admin 4.11.0-beta.1 → 4.11.0-exp.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/admin/src/content-manager/components/ComponentIcon/ComponentIcon.js +16 -26
  2. package/admin/src/content-manager/components/ComponentIcon/constants.js +133 -0
  3. package/admin/src/content-manager/components/DynamicZone/components/AddComponentButton.js +32 -95
  4. package/admin/src/content-manager/components/DynamicZone/components/ComponentCard.js +10 -2
  5. package/admin/src/content-manager/components/DynamicZone/components/ComponentCategory.js +63 -15
  6. package/admin/src/content-manager/components/DynamicZone/components/ComponentPicker.js +50 -63
  7. package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +132 -58
  8. package/admin/src/content-manager/components/DynamicZone/components/DynamicZoneLabel.js +29 -37
  9. package/admin/src/content-manager/components/DynamicZone/index.js +131 -83
  10. package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +28 -6
  11. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +18 -6
  12. package/admin/src/content-manager/pages/EditSettingsView/components/DynamicZoneList.js +7 -1
  13. package/admin/src/content-manager/pages/EditView/index.js +1 -1
  14. package/admin/src/hooks/useContentTypes/useContentTypes.js +0 -2
  15. package/admin/src/index.js +4 -3
  16. package/admin/src/pages/SettingsPage/pages/Users/ListPage/index.js +1 -1
  17. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventTable/EventTableCE.js +13 -0
  18. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventTable/index.js +3 -0
  19. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/Events/index.js +331 -0
  20. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/HeadersInput/Combobox.js +54 -4
  21. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/HeadersInput/index.js +12 -23
  22. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/WebhookForm/index.js +129 -116
  23. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/WebhookForm/utils/makeWebhookValidationSchema.js +62 -0
  24. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/index.js +59 -64
  25. package/admin/src/translations/en.json +6 -0
  26. package/build/1887.6f71e8b2.chunk.js +39 -0
  27. package/build/3081.7e9329cb.chunk.js +105 -0
  28. package/build/371.6e4e2c1f.chunk.js +71 -0
  29. package/build/5542.64b623c9.chunk.js +63 -0
  30. package/build/{5563.79950369.chunk.js → 5563.badbffde.chunk.js} +2 -2
  31. package/build/{6970.7ea35fbd.chunk.js → 6970.d456705f.chunk.js} +1 -1
  32. package/build/{7259.5cc67413.chunk.js → 7259.5d0de931.chunk.js} +1 -1
  33. package/build/8976.a85384ce.chunk.js +155 -0
  34. package/build/{1657.ca8562dd.chunk.js → 9932.5ef475c5.chunk.js} +54 -62
  35. package/build/{Admin-authenticatedApp.990df65d.chunk.js → Admin-authenticatedApp.9b88039c.chunk.js} +2 -2
  36. package/build/{Admin_InternalErrorPage.96ceaae1.chunk.js → Admin_InternalErrorPage.f25f04f3.chunk.js} +1 -1
  37. package/build/Admin_homePage.05063e43.chunk.js +73 -0
  38. package/build/Admin_marketplace.23ea289f.chunk.js +55 -0
  39. package/build/Admin_pluginsPage.b1031a00.chunk.js +6 -0
  40. package/build/{Admin_profilePage.75bc083a.chunk.js → Admin_profilePage.e7ccee9f.chunk.js} +2 -2
  41. package/build/Admin_settingsPage.07a6a5f0.chunk.js +79 -0
  42. package/build/{Upload_ConfigureTheView.aa64ed9a.chunk.js → Upload_ConfigureTheView.121deffb.chunk.js} +1 -1
  43. package/build/admin-app.8644c322.chunk.js +63 -0
  44. package/build/{admin-edit-roles-page.0d12b741.chunk.js → admin-edit-roles-page.bfe3304d.chunk.js} +3 -3
  45. package/build/{admin-edit-users.f9ce7844.chunk.js → admin-edit-users.6efe0382.chunk.js} +2 -2
  46. package/build/admin-roles-list.b2577370.chunk.js +23 -0
  47. package/build/admin-users.4af49ccf.chunk.js +26 -0
  48. package/build/{api-tokens-create-page.973d2816.chunk.js → api-tokens-create-page.65411a36.chunk.js} +1 -1
  49. package/build/{api-tokens-edit-page.29725c5e.chunk.js → api-tokens-edit-page.60312cb6.chunk.js} +1 -1
  50. package/build/{api-tokens-list-page.66c4fbdd.chunk.js → api-tokens-list-page.01a4d5cd.chunk.js} +2 -2
  51. package/build/audit-logs-settings-page.09c75037.chunk.js +121 -0
  52. package/build/content-manager.04fa9c14.chunk.js +1094 -0
  53. package/build/content-type-builder-list-view.58f9ed20.chunk.js +211 -0
  54. package/build/{content-type-builder-translation-en-json.af293c9e.chunk.js → content-type-builder-translation-en-json.f592325b.chunk.js} +1 -1
  55. package/build/content-type-builder.baeb0413.chunk.js +132 -0
  56. package/build/email-settings-page.85b71afc.chunk.js +11 -0
  57. package/build/en-json.a8f34002.chunk.js +1 -0
  58. package/build/i18n-settings-page.c0da2362.chunk.js +114 -0
  59. package/build/index.html +1 -1
  60. package/build/main.4a51b662.js +2633 -0
  61. package/build/{review-workflows-settings.4b39b837.chunk.js → review-workflows-settings.3a7bae25.chunk.js} +1 -1
  62. package/build/{runtime~main.55d43bd7.js → runtime~main.9b9e21e3.js} +2 -2
  63. package/build/{sso-settings-page.265e3d72.chunk.js → sso-settings-page.4bb073e0.chunk.js} +1 -1
  64. package/build/{transfer-tokens-create-page.170acee6.chunk.js → transfer-tokens-create-page.9ec277d7.chunk.js} +1 -1
  65. package/build/{transfer-tokens-edit-page.6cf23295.chunk.js → transfer-tokens-edit-page.fa5ade14.chunk.js} +1 -1
  66. package/build/{transfer-tokens-list-page.c3fec4c1.chunk.js → transfer-tokens-list-page.5d68d590.chunk.js} +2 -2
  67. package/build/upload-settings.2c1565d6.chunk.js +14 -0
  68. package/build/upload.257b2aef.chunk.js +26 -0
  69. package/build/users-advanced-settings-page.dda58320.chunk.js +9 -0
  70. package/build/users-email-settings-page.a0c08594.chunk.js +24 -0
  71. package/build/users-providers-settings-page.14a82632.chunk.js +29 -0
  72. package/build/{users-roles-settings-page.c773086b.chunk.js → users-roles-settings-page.1f408276.chunk.js} +1 -1
  73. package/build/webhook-edit-page.b9a13be7.chunk.js +136 -0
  74. package/build/webhook-list-page.84e5abc9.chunk.js +63 -0
  75. package/ee/admin/pages/SettingsPage/pages/Webhooks/EditView/components/EventTable/EventTableEE.js +23 -0
  76. package/ee/admin/pages/SettingsPage/pages/Webhooks/EditView/components/EventTable/index.js +3 -0
  77. package/ee/server/services/review-workflows/review-workflows.js +4 -0
  78. package/package.json +19 -21
  79. package/server/controllers/webhooks.js +6 -6
  80. package/admin/src/content-manager/components/DynamicZone/utils/connect.js +0 -12
  81. package/admin/src/content-manager/components/DynamicZone/utils/select.js +0 -53
  82. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventInput/EventRow.js +0 -70
  83. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventInput/index.js +0 -174
  84. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/HeadersInput/keys.js +0 -39
  85. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/utils/fieldsRegex.js +0 -4
  86. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/utils/schema.js +0 -35
  87. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/reducer.js +0 -100
  88. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/utils/formatData.js +0 -20
  89. package/build/3081.c2cdfac8.chunk.js +0 -108
  90. package/build/456.9b85d4c6.chunk.js +0 -39
  91. package/build/462.a073ff1f.chunk.js +0 -71
  92. package/build/5542.002522eb.chunk.js +0 -71
  93. package/build/617.87b2fe96.chunk.js +0 -155
  94. package/build/Admin_homePage.107a9fe0.chunk.js +0 -73
  95. package/build/Admin_marketplace.1436fc2b.chunk.js +0 -55
  96. package/build/Admin_pluginsPage.e1afd5ed.chunk.js +0 -6
  97. package/build/Admin_settingsPage.bd715ed3.chunk.js +0 -79
  98. package/build/admin-app.8b102fe2.chunk.js +0 -63
  99. package/build/admin-roles-list.e8bf9685.chunk.js +0 -31
  100. package/build/admin-users.751b28b2.chunk.js +0 -34
  101. package/build/audit-logs-settings-page.3c6cea81.chunk.js +0 -129
  102. package/build/content-manager.89099707.chunk.js +0 -1123
  103. package/build/content-type-builder-list-view.1e821eb9.chunk.js +0 -215
  104. package/build/content-type-builder.b10576e7.chunk.js +0 -126
  105. package/build/email-settings-page.4368689f.chunk.js +0 -11
  106. package/build/en-json.0f5cc115.chunk.js +0 -1
  107. package/build/i18n-settings-page.7988d872.chunk.js +0 -114
  108. package/build/main.5a232c3d.js +0 -2630
  109. package/build/upload-settings.63d99bf5.chunk.js +0 -14
  110. package/build/upload.c50d8c7a.chunk.js +0 -34
  111. package/build/users-advanced-settings-page.2cfb5d24.chunk.js +0 -9
  112. package/build/users-email-settings-page.bd6c774a.chunk.js +0 -24
  113. package/build/users-providers-settings-page.528f0036.chunk.js +0 -29
  114. package/build/webhook-edit-page.ddd5963d.chunk.js +0 -128
  115. package/build/webhook-list-page.b0f5a02c.chunk.js +0 -71
@@ -8,6 +8,7 @@ import set from 'lodash/set';
8
8
  import PropTypes from 'prop-types';
9
9
  import { useIntl } from 'react-intl';
10
10
  import { Prompt, Redirect } from 'react-router-dom';
11
+ import { flushSync } from 'react-dom';
11
12
  import { useDispatch, useSelector } from 'react-redux';
12
13
 
13
14
  import { Main } from '@strapi/design-system';
@@ -55,6 +56,7 @@ const EditViewDataManagerProvider = ({
55
56
  status,
56
57
  updateActionAllowedFields,
57
58
  }) => {
59
+ const [isSaving, setIsSaving] = React.useState(false);
58
60
  /**
59
61
  * TODO: this should be moved into the global reducer
60
62
  * to match ever other reducer in the CM.
@@ -194,14 +196,21 @@ const EditViewDataManagerProvider = ({
194
196
 
195
197
  const dispatchAddComponent = useCallback(
196
198
  (type) =>
197
- (keys, componentLayoutData, components, shouldCheckErrors = false) => {
199
+ (
200
+ keys,
201
+ componentLayoutData,
202
+ allComponents,
203
+ shouldCheckErrors = false,
204
+ position = undefined
205
+ ) => {
198
206
  trackUsageRef.current('didAddComponentToDynamicZone');
199
207
 
200
208
  dispatch({
201
209
  type,
202
210
  keys: keys.split('.'),
211
+ position,
203
212
  componentLayoutData,
204
- allComponents: components,
213
+ allComponents,
205
214
  shouldCheckErrors,
206
215
  });
207
216
  },
@@ -376,14 +385,20 @@ const EditViewDataManagerProvider = ({
376
385
  try {
377
386
  if (isEmpty(errors)) {
378
387
  const formData = createFormData(modifiedData, initialData);
388
+ flushSync(() => {
389
+ setIsSaving(true);
390
+ });
379
391
 
380
392
  if (isCreatingEntry) {
381
393
  await onPost(formData, trackerProperty);
382
394
  } else {
383
395
  await onPut(formData, trackerProperty);
384
396
  }
397
+
398
+ setIsSaving(false);
385
399
  }
386
400
  } catch (err) {
401
+ setIsSaving(false);
387
402
  errors = {
388
403
  ...errors,
389
404
  ...getAPIInnerErrors(err, { getTrad }),
@@ -445,9 +460,14 @@ const EditViewDataManagerProvider = ({
445
460
 
446
461
  try {
447
462
  if (isEmpty(errors)) {
463
+ flushSync(() => {
464
+ setIsSaving(true);
465
+ });
448
466
  await onPublish();
467
+ setIsSaving(false);
449
468
  }
450
469
  } catch (err) {
470
+ setIsSaving(false);
451
471
  errors = {
452
472
  ...errors,
453
473
  ...getAPIInnerErrors(err, { getTrad }),
@@ -639,10 +659,12 @@ const EditViewDataManagerProvider = ({
639
659
  </Main>
640
660
  ) : (
641
661
  <>
642
- <Prompt
643
- when={!isEqual(modifiedData, initialData)}
644
- message={formatMessage({ id: 'global.prompt.unsaved' })}
645
- />
662
+ {!isSaving ? (
663
+ <Prompt
664
+ when={!isEqual(modifiedData, initialData)}
665
+ message={formatMessage({ id: 'global.prompt.unsaved' })}
666
+ />
667
+ ) : null}
646
668
  <form noValidate onSubmit={handleSubmit}>
647
669
  {children}
648
670
  </form>
@@ -52,7 +52,13 @@ const reducer = (state, action) =>
52
52
  }
53
53
  case 'ADD_COMPONENT_TO_DYNAMIC_ZONE':
54
54
  case 'ADD_REPEATABLE_COMPONENT_TO_FIELD': {
55
- const { keys, allComponents, componentLayoutData, shouldCheckErrors } = action;
55
+ const {
56
+ keys,
57
+ allComponents,
58
+ componentLayoutData,
59
+ shouldCheckErrors,
60
+ position = undefined,
61
+ } = action;
56
62
 
57
63
  if (shouldCheckErrors) {
58
64
  draftState.shouldCheckErrors = !state.shouldCheckErrors;
@@ -62,7 +68,15 @@ const reducer = (state, action) =>
62
68
  draftState.modifiedDZName = keys[0];
63
69
  }
64
70
 
65
- const currentValue = get(state, ['modifiedData', ...keys], []);
71
+ const currentValue = [...get(state, ['modifiedData', ...keys], [])];
72
+
73
+ let actualPosition = position;
74
+
75
+ if (actualPosition === undefined) {
76
+ actualPosition = currentValue.length;
77
+ } else if (actualPosition < 0) {
78
+ actualPosition = 0;
79
+ }
66
80
 
67
81
  const defaultDataStructure =
68
82
  action.type === 'ADD_COMPONENT_TO_DYNAMIC_ZONE'
@@ -87,11 +101,9 @@ const reducer = (state, action) =>
87
101
  componentLayoutData.attributes
88
102
  );
89
103
 
90
- const newValue = Array.isArray(currentValue)
91
- ? [...currentValue, componentDataStructure]
92
- : [componentDataStructure];
104
+ currentValue.splice(actualPosition, 0, componentDataStructure);
93
105
 
94
- set(draftState, ['modifiedData', ...keys], newValue);
106
+ set(draftState, ['modifiedData', ...keys], currentValue);
95
107
 
96
108
  break;
97
109
  }
@@ -25,6 +25,12 @@ const CustomLink = styled(Flex)`
25
25
  > div:first-child {
26
26
  background: ${({ theme }) => theme.colors.primary200};
27
27
  color: ${({ theme }) => theme.colors.primary600};
28
+
29
+ svg {
30
+ path {
31
+ fill: ${({ theme }) => theme.colors.primary600};
32
+ }
33
+ }
28
34
  }
29
35
  }
30
36
  `;
@@ -49,7 +55,7 @@ const DynamicZoneList = ({ components }) => {
49
55
  as={Link}
50
56
  to={`/content-manager/components/${componentUid}/configurations/edit`}
51
57
  >
52
- <ComponentIcon />
58
+ <ComponentIcon icon={componentLayouts?.[componentUid]?.info?.icon} />
53
59
 
54
60
  <Box paddingTop={1}>
55
61
  <Typography fontSize={1} textColor="neutral600" fontWeight="bold">
@@ -13,7 +13,7 @@ import { Pencil, Layer } from '@strapi/icons';
13
13
  import InformationBox from 'ee_else_ce/content-manager/pages/EditView/InformationBox';
14
14
  import { InjectionZone } from '../../../shared/components';
15
15
  import permissions from '../../../permissions';
16
- import DynamicZone from '../../components/DynamicZone';
16
+ import { DynamicZone } from '../../components/DynamicZone';
17
17
  import CollectionTypeFormWrapper from '../../components/CollectionTypeFormWrapper';
18
18
  import EditViewDataManagerProvider from '../../components/EditViewDataManagerProvider';
19
19
  import SingleTypeFormWrapper from '../../components/SingleTypeFormWrapper';
@@ -2,8 +2,6 @@ import { useAPIErrorHandler, useFetchClient, useNotification } from '@strapi/hel
2
2
  import { useQueries } from 'react-query';
3
3
 
4
4
  export function useContentTypes() {
5
- console.log('----> read');
6
-
7
5
  const { get } = useFetchClient();
8
6
  const { formatAPIError } = useAPIErrorHandler();
9
7
  const toggleNotification = useNotification();
@@ -1,7 +1,7 @@
1
- import ReactDOM from 'react-dom';
2
1
  import { getFetchClient } from '@strapi/helper-plugin';
3
- import { Components, Fields, Middlewares, Reducers } from './core/apis';
2
+ import { createRoot } from 'react-dom/client';
4
3
  import appCustomisations from './app';
4
+ import { Components, Fields, Middlewares, Reducers } from './core/apis';
5
5
  // eslint-disable-next-line import/extensions
6
6
  import plugins from './plugins';
7
7
  import appReducers from './reducers';
@@ -68,7 +68,8 @@ const run = async () => {
68
68
 
69
69
  await app.loadTrads();
70
70
 
71
- ReactDOM.render(app.render(), MOUNT_NODE);
71
+ const root = createRoot(MOUNT_NODE);
72
+ root.render(app.render());
72
73
  };
73
74
 
74
75
  run();
@@ -47,7 +47,7 @@ const ListPage = () => {
47
47
  pagination,
48
48
  isError,
49
49
  isLoading,
50
- refetchQueries: refetchAdminUsers,
50
+ refetch: refetchAdminUsers,
51
51
  } = useAdminUsers(qs.parse(search, { ignoreQueryPrefix: true }), {
52
52
  enabled: canRead,
53
53
  });
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+
3
+ import EventTable from '../Events';
4
+
5
+ // This component is overwritten by the EE counterpart
6
+ export function EventTableCE() {
7
+ return (
8
+ <EventTable.Root>
9
+ <EventTable.Headers />
10
+ <EventTable.Body />
11
+ </EventTable.Root>
12
+ );
13
+ }
@@ -0,0 +1,3 @@
1
+ import { EventTableCE } from './EventTableCE';
2
+
3
+ export default EventTableCE;
@@ -0,0 +1,331 @@
1
+ import * as React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ import { useFormikContext } from 'formik';
5
+ import { useIntl } from 'react-intl';
6
+ import styled from 'styled-components';
7
+ import {
8
+ FieldLabel,
9
+ Flex,
10
+ Typography,
11
+ BaseCheckbox,
12
+ Checkbox,
13
+ Loader,
14
+ RawTable as Table,
15
+ RawTh as Th,
16
+ RawTd as Td,
17
+ RawTr as Tr,
18
+ RawThead as Thead,
19
+ RawTbody as Tbody,
20
+ VisuallyHidden,
21
+ } from '@strapi/design-system';
22
+
23
+ import { useContentTypes } from '../../../../../../../hooks/useContentTypes';
24
+
25
+ export const formatValue = (value) =>
26
+ value.reduce((acc, curr) => {
27
+ const key = curr.split('.')[0];
28
+
29
+ if (!acc[key]) {
30
+ acc[key] = [];
31
+ }
32
+ acc[key].push(curr);
33
+
34
+ return acc;
35
+ }, {});
36
+
37
+ // TODO check whether we want to move alternating background colour tables to the design system
38
+ const StyledTable = styled(Table)`
39
+ tbody tr:nth-child(odd) {
40
+ background: ${({ theme }) => theme.colors.neutral100};
41
+ }
42
+
43
+ thead th span {
44
+ color: ${({ theme }) => theme.colors.neutral500};
45
+ }
46
+
47
+ td,
48
+ th {
49
+ padding-block-start: ${({ theme }) => theme.spaces[3]};
50
+ padding-block-end: ${({ theme }) => theme.spaces[3]};
51
+ width: 10%;
52
+ vertical-align: middle;
53
+ text-align: center;
54
+ }
55
+
56
+ tbody tr td:first-child {
57
+ // Add padding to the start of the first column to avoid the checkbox appearing
58
+ // too close to the edge of the table
59
+ padding-inline-start: ${({ theme }) => theme.spaces[2]};
60
+ }
61
+ `;
62
+
63
+ const getCEHeaders = (isDraftAndPublish) => {
64
+ const headers = [
65
+ { id: 'Settings.webhooks.events.create', defaultMessage: 'Create' },
66
+ { id: 'Settings.webhooks.events.update', defaultMessage: 'Update' },
67
+ { id: 'app.utils.delete', defaultMessage: 'Delete' },
68
+ ];
69
+
70
+ if (isDraftAndPublish) {
71
+ headers.push({ id: 'app.utils.publish', defaultMessage: 'Publish' });
72
+ headers.push({ id: 'app.utils.unpublish', defaultMessage: 'Unpublish' });
73
+ }
74
+
75
+ return headers;
76
+ };
77
+
78
+ const getCEEvents = (isDraftAndPublish) => {
79
+ const entryEvents = ['entry.create', 'entry.update', 'entry.delete'];
80
+
81
+ if (isDraftAndPublish) {
82
+ entryEvents.push('entry.publish', 'entry.unpublish');
83
+ }
84
+
85
+ return {
86
+ entry: entryEvents,
87
+ media: ['media.create', 'media.update', 'media.delete'],
88
+ };
89
+ };
90
+
91
+ const WebhookEventContext = React.createContext();
92
+
93
+ const Root = ({ children }) => {
94
+ const { formatMessage } = useIntl();
95
+ const { collectionTypes, isLoading } = useContentTypes();
96
+
97
+ const isDraftAndPublish = React.useMemo(
98
+ () => collectionTypes.some((ct) => ct.options.draftAndPublish === true),
99
+ [collectionTypes]
100
+ );
101
+
102
+ const label = formatMessage({
103
+ id: 'Settings.webhooks.form.events',
104
+ defaultMessage: 'Events',
105
+ });
106
+
107
+ return (
108
+ <WebhookEventContext.Provider value={{ isDraftAndPublish }}>
109
+ <Flex direction="column" alignItems="stretch" gap={1}>
110
+ <FieldLabel aria-hidden>{label}</FieldLabel>
111
+ {isLoading && (
112
+ <Loader>
113
+ {formatMessage({
114
+ id: 'Settings.webhooks.events.isLoading',
115
+ defaultMessage: 'Events loading',
116
+ })}
117
+ </Loader>
118
+ )}
119
+ <StyledTable aria-label={label}>{children}</StyledTable>
120
+ </Flex>
121
+ </WebhookEventContext.Provider>
122
+ );
123
+ };
124
+
125
+ Root.propTypes = {
126
+ children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
127
+ };
128
+
129
+ const Headers = ({ getHeaders = getCEHeaders }) => {
130
+ const { isDraftAndPublish } = React.useContext(WebhookEventContext);
131
+
132
+ const { formatMessage } = useIntl();
133
+ const headers = getHeaders(isDraftAndPublish);
134
+
135
+ return (
136
+ <Thead>
137
+ <Tr>
138
+ <Th>
139
+ <VisuallyHidden>
140
+ {formatMessage({
141
+ id: 'Settings.webhooks.event.select',
142
+ defaultMessage: 'Select event',
143
+ })}
144
+ </VisuallyHidden>
145
+ </Th>
146
+ {headers.map((header) => {
147
+ if (['app.utils.publish', 'app.utils.unpublish'].includes(header.id)) {
148
+ return (
149
+ <Th
150
+ key={header.id}
151
+ title={formatMessage({
152
+ id: 'Settings.webhooks.event.publish-tooltip',
153
+ defaultMessage: 'This event only exists for content with draft & publish enabled',
154
+ })}
155
+ >
156
+ <Typography variant="sigma" textColor="neutral600">
157
+ {formatMessage(header)}
158
+ </Typography>
159
+ </Th>
160
+ );
161
+ }
162
+
163
+ return (
164
+ <Th key={header.id}>
165
+ <Typography variant="sigma" textColor="neutral600">
166
+ {formatMessage(header)}
167
+ </Typography>
168
+ </Th>
169
+ );
170
+ })}
171
+ </Tr>
172
+ </Thead>
173
+ );
174
+ };
175
+
176
+ Headers.defaultProps = {
177
+ getHeaders: getCEHeaders,
178
+ };
179
+
180
+ Headers.propTypes = {
181
+ getHeaders: PropTypes.func,
182
+ };
183
+
184
+ const Body = ({ providedEvents }) => {
185
+ const { isDraftAndPublish } = React.useContext(WebhookEventContext);
186
+
187
+ const events = providedEvents || getCEEvents(isDraftAndPublish);
188
+ const { values, handleChange: onChange } = useFormikContext();
189
+
190
+ const inputName = 'events';
191
+ const inputValue = values.events;
192
+ const disabledEvents = [];
193
+
194
+ const formattedValue = formatValue(inputValue);
195
+
196
+ const handleSelect = ({ target: { name, value } }) => {
197
+ let set = new Set(inputValue);
198
+
199
+ if (value) {
200
+ set.add(name);
201
+ } else {
202
+ set.delete(name);
203
+ }
204
+ onChange({ target: { name: inputName, value: Array.from(set) } });
205
+ };
206
+
207
+ const handleSelectAll = ({ target: { name, value } }) => {
208
+ let set = new Set(inputValue);
209
+
210
+ if (value) {
211
+ events[name].forEach((event) => {
212
+ if (!disabledEvents.includes(event)) {
213
+ set.add(event);
214
+ }
215
+ });
216
+ } else {
217
+ events[name].forEach((event) => set.delete(event));
218
+ }
219
+ onChange({ target: { name: inputName, value: Array.from(set) } });
220
+ };
221
+
222
+ return (
223
+ <Tbody>
224
+ {Object.entries(events).map(([event, value]) => {
225
+ return (
226
+ <EventRow
227
+ disabledEvents={disabledEvents}
228
+ key={event}
229
+ name={event}
230
+ events={value}
231
+ inputValue={formattedValue[event]}
232
+ handleSelect={handleSelect}
233
+ handleSelectAll={handleSelectAll}
234
+ />
235
+ );
236
+ })}
237
+ </Tbody>
238
+ );
239
+ };
240
+
241
+ Body.defaultProps = {
242
+ providedEvents: null,
243
+ };
244
+
245
+ Body.propTypes = {
246
+ providedEvents: PropTypes.object,
247
+ };
248
+
249
+ /**
250
+ * Converts a string to title case and removes hyphens.
251
+ *
252
+ * @param {string} str - The string to convert.
253
+ * @returns {string} The converted string.
254
+ */
255
+ const removeHyphensAndTitleCase = (str) =>
256
+ str
257
+ .replace(/-/g, ' ')
258
+ .split(' ')
259
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
260
+ .join(' ');
261
+
262
+ const EventRow = ({ disabledEvents, name, events, inputValue, handleSelect, handleSelectAll }) => {
263
+ const { formatMessage } = useIntl();
264
+ const enabledCheckboxes = events.filter((event) => !disabledEvents.includes(event));
265
+
266
+ const hasSomeCheckboxSelected = inputValue.length > 0;
267
+ const areAllCheckboxesSelected = inputValue.length === enabledCheckboxes.length;
268
+
269
+ const onChangeAll = ({ target: { name } }) => {
270
+ const valueToSet = !areAllCheckboxesSelected;
271
+
272
+ handleSelectAll({
273
+ target: { name, value: valueToSet },
274
+ });
275
+ };
276
+
277
+ const targetColumns = 5;
278
+
279
+ return (
280
+ <Tr>
281
+ <Td>
282
+ <Checkbox
283
+ indeterminate={hasSomeCheckboxSelected && !areAllCheckboxesSelected}
284
+ aria-label={formatMessage({
285
+ id: 'global.select-all-entries',
286
+ defaultMessage: 'Select all entries',
287
+ })}
288
+ name={name}
289
+ onChange={onChangeAll}
290
+ value={areAllCheckboxesSelected}
291
+ >
292
+ {removeHyphensAndTitleCase(name)}
293
+ </Checkbox>
294
+ </Td>
295
+
296
+ {events.map((event) => {
297
+ return (
298
+ <Td key={event}>
299
+ <BaseCheckbox
300
+ disabled={disabledEvents.includes(event)}
301
+ aria-label={event}
302
+ name={event}
303
+ value={inputValue.includes(event)}
304
+ onValueChange={(value) => handleSelect({ target: { name: event, value } })}
305
+ />
306
+ </Td>
307
+ );
308
+ })}
309
+ {events.length < targetColumns && <Td colSpan={`${targetColumns - events.length}`} />}
310
+ </Tr>
311
+ );
312
+ };
313
+
314
+ EventRow.defaultProps = {
315
+ disabledEvents: [],
316
+ events: [],
317
+ inputValue: [],
318
+ handleSelect() {},
319
+ handleSelectAll() {},
320
+ };
321
+
322
+ EventRow.propTypes = {
323
+ disabledEvents: PropTypes.array,
324
+ events: PropTypes.array,
325
+ inputValue: PropTypes.array,
326
+ handleSelect: PropTypes.func,
327
+ handleSelectAll: PropTypes.func,
328
+ name: PropTypes.string.isRequired,
329
+ };
330
+
331
+ export default { Root, Headers, Body, EventRow };
@@ -1,10 +1,59 @@
1
- import React, { useState } from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { useFormikContext } from 'formik';
3
4
  import { ComboboxOption, CreatableCombobox } from '@strapi/design-system';
4
- import keys from './keys';
5
+
6
+ const HTTP_HEADERS = [
7
+ 'A-IM',
8
+ 'Accept',
9
+ 'Accept-Charset',
10
+ 'Accept-Encoding',
11
+ 'Accept-Language',
12
+ 'Accept-Datetime',
13
+ 'Access-Control-Request-Method',
14
+ 'Access-Control-Request-Headers',
15
+ 'Authorization',
16
+ 'Cache-Control',
17
+ 'Connection',
18
+ 'Content-Length',
19
+ 'Content-Type',
20
+ 'Cookie',
21
+ 'Date',
22
+ 'Expect',
23
+ 'Forwarded',
24
+ 'From',
25
+ 'Host',
26
+ 'If-Match',
27
+ 'If-Modified-Since',
28
+ 'If-None-Match',
29
+ 'If-Range',
30
+ 'If-Unmodified-Since',
31
+ 'Max-Forwards',
32
+ 'Origin',
33
+ 'Pragma',
34
+ 'Proxy-Authorization',
35
+ 'Range',
36
+ 'Referer',
37
+ 'TE',
38
+ 'User-Agent',
39
+ 'Upgrade',
40
+ 'Via',
41
+ 'Warning',
42
+ ];
5
43
 
6
44
  const Combobox = ({ name, onChange, value, ...props }) => {
7
- const [options, setOptions] = useState(value ? [...keys, value] : keys);
45
+ const {
46
+ values: { headers },
47
+ } = useFormikContext();
48
+ const [options, setOptions] = useState(HTTP_HEADERS);
49
+
50
+ useEffect(() => {
51
+ setOptions(
52
+ HTTP_HEADERS.filter(
53
+ (key) => !headers?.some((header) => header.key !== value && header.key === key)
54
+ )
55
+ );
56
+ }, [headers, value]);
8
57
 
9
58
  const handleChange = (value) => {
10
59
  onChange({ target: { name, value } });
@@ -13,12 +62,13 @@ const Combobox = ({ name, onChange, value, ...props }) => {
13
62
  const handleCreateOption = (value) => {
14
63
  setOptions((prev) => [...prev, value]);
15
64
 
16
- onChange({ target: { name, value } });
65
+ handleChange(value);
17
66
  };
18
67
 
19
68
  return (
20
69
  <CreatableCombobox
21
70
  {...props}
71
+ onClear={() => handleChange('')}
22
72
  onChange={handleChange}
23
73
  onCreateOption={handleCreateOption}
24
74
  placeholder=""