@strapi/admin 4.12.7 → 4.13.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/admin/src/components/NpsSurvey/hooks/useNpsSurveySettings.js +17 -0
  2. package/admin/src/components/NpsSurvey/index.js +365 -0
  3. package/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumns.js +2 -0
  4. package/admin/src/content-manager/components/DynamicZone/components/DynamicZoneLabel.js +1 -1
  5. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +7 -2
  6. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +6 -0
  7. package/admin/src/content-manager/components/Filter/CustomInputs/AdminUsersFilter.js +42 -0
  8. package/admin/src/content-manager/components/Filter/Filter.js +54 -0
  9. package/admin/src/content-manager/components/Filter/index.js +1 -0
  10. package/admin/src/content-manager/components/InputUID/index.js +222 -216
  11. package/admin/src/content-manager/components/RelationInput/RelationInput.js +4 -0
  12. package/admin/src/content-manager/components/RepeatableComponent/index.js +32 -2
  13. package/admin/src/content-manager/components/Wysiwyg/Editor.js +432 -68
  14. package/admin/src/content-manager/components/Wysiwyg/WysiwygStyles.js +0 -7
  15. package/admin/src/content-manager/components/Wysiwyg/index.js +147 -152
  16. package/admin/src/content-manager/constants/attributes.js +3 -0
  17. package/admin/src/content-manager/hooks/useAllowedAttributes.js +43 -0
  18. package/admin/src/content-manager/pages/EditView/Information/index.js +9 -8
  19. package/admin/src/content-manager/pages/ListSettingsView/components/Settings.js +40 -7
  20. package/admin/src/content-manager/pages/ListSettingsView/index.js +6 -2
  21. package/admin/src/content-manager/pages/ListView/components/FieldPicker/index.js +67 -69
  22. package/admin/src/content-manager/pages/ListView/components/ViewSettingsMenu/index.js +80 -0
  23. package/admin/src/content-manager/pages/ListView/index.js +236 -67
  24. package/admin/src/content-manager/utils/getDisplayName.js +33 -0
  25. package/admin/src/content-manager/utils/index.js +1 -0
  26. package/admin/src/pages/Admin/index.js +3 -1
  27. package/admin/src/pages/AuthPage/components/Register/index.js +5 -0
  28. package/admin/src/pages/SettingsPage/components/SettingsNav/index.js +3 -3
  29. package/admin/src/pages/SettingsPage/index.js +16 -26
  30. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js +69 -35
  31. package/admin/src/plugins.js +7 -8
  32. package/admin/src/translations/en.json +10 -1
  33. package/build/{1049.f76cb14b.chunk.js → 1049.ec69f5e0.chunk.js} +1 -1
  34. package/build/1227.9f37e1dc.chunk.js +1 -0
  35. package/build/{1386.879bcd90.chunk.js → 1386.ea73b677.chunk.js} +1 -1
  36. package/build/1504.eff012f7.chunk.js +95 -0
  37. package/build/{2225.c6244756.chunk.js → 2225.649fb7bc.chunk.js} +11 -11
  38. package/build/2237.b832ae6e.chunk.js +114 -0
  39. package/build/2379.1f98a31a.chunk.js +1 -0
  40. package/build/2395.0e5e8ded.chunk.js +26 -0
  41. package/build/2801.8e1aa82a.chunk.js +1 -0
  42. package/build/{3483.03c24f96.chunk.js → 3483.19381b40.chunk.js} +1 -1
  43. package/build/4174.f1f39e40.chunk.js +1 -0
  44. package/build/4546.a5946d22.chunk.js +1 -0
  45. package/build/4724.aea5c8c1.chunk.js +6 -0
  46. package/build/502.7bba43b1.chunk.js +1 -0
  47. package/build/6158.c3c13c20.chunk.js +1 -0
  48. package/build/7464.eb057bec.chunk.js +1 -0
  49. package/build/78.dcc6df5c.chunk.js +1 -0
  50. package/build/8276.be3ed581.chunk.js +26 -0
  51. package/build/{Admin-authenticatedApp.31497f74.chunk.js → Admin-authenticatedApp.36b0fe22.chunk.js} +2 -2
  52. package/build/{Admin_InternalErrorPage.f45f2462.chunk.js → Admin_InternalErrorPage.38155af3.chunk.js} +1 -1
  53. package/build/{Admin_homePage.ac9dfb86.chunk.js → Admin_homePage.6f128523.chunk.js} +1 -1
  54. package/build/{Admin_marketplace.c94239f6.chunk.js → Admin_marketplace.061a6e5a.chunk.js} +1 -1
  55. package/build/{Admin_pluginsPage.bbe79434.chunk.js → Admin_pluginsPage.16f837b8.chunk.js} +1 -1
  56. package/build/{Admin_profilePage.192edc52.chunk.js → Admin_profilePage.678bce24.chunk.js} +2 -2
  57. package/build/Admin_settingsPage.af7309e4.chunk.js +111 -0
  58. package/build/{Upload_ConfigureTheView.345ac1e0.chunk.js → Upload_ConfigureTheView.3fc1c100.chunk.js} +1 -1
  59. package/build/admin-app.d63bd229.chunk.js +36 -0
  60. package/build/{admin-edit-roles-page.6d567273.chunk.js → admin-edit-roles-page.38a6c863.chunk.js} +3 -3
  61. package/build/{admin-edit-users.acfd4128.chunk.js → admin-edit-users.545fc882.chunk.js} +2 -2
  62. package/build/{admin-roles-list.23ddff26.chunk.js → admin-roles-list.1e2e814d.chunk.js} +1 -1
  63. package/build/{admin-users.00e20017.chunk.js → admin-users.b8ea5677.chunk.js} +2 -2
  64. package/build/{api-tokens-create-page.46c2ea84.chunk.js → api-tokens-create-page.e0c15627.chunk.js} +1 -1
  65. package/build/{api-tokens-edit-page.58139df9.chunk.js → api-tokens-edit-page.9f2dce47.chunk.js} +1 -1
  66. package/build/{api-tokens-list-page.0af7d431.chunk.js → api-tokens-list-page.d747051c.chunk.js} +2 -2
  67. package/build/{audit-logs-settings-page.0f73ccf8.chunk.js → audit-logs-settings-page.96f9d608.chunk.js} +1 -1
  68. package/build/content-manager.2d676432.chunk.js +1097 -0
  69. package/build/{content-type-builder-list-view.bf9be456.chunk.js → content-type-builder-list-view.b71cf240.chunk.js} +1 -1
  70. package/build/{content-type-builder.cd999f6e.chunk.js → content-type-builder.e5669749.chunk.js} +2 -2
  71. package/build/email-settings-page.2809f0bf.chunk.js +11 -0
  72. package/build/en-json.e12fd5fc.chunk.js +1 -0
  73. package/build/{i18n-settings-page.47f78016.chunk.js → i18n-settings-page.5f716172.chunk.js} +1 -1
  74. package/build/index.html +1 -1
  75. package/build/main.c6c9e04c.js +2859 -0
  76. package/build/review-workflows-settings-create-view.4a156a19.chunk.js +1 -0
  77. package/build/review-workflows-settings-edit-view.ce984d1f.chunk.js +1 -0
  78. package/build/review-workflows-settings-list-view.419b8deb.chunk.js +56 -0
  79. package/build/{runtime~main.d515c521.js → runtime~main.5a10b789.js} +2 -2
  80. package/build/{sso-settings-page.12b6d8ae.chunk.js → sso-settings-page.45153df5.chunk.js} +1 -1
  81. package/build/{transfer-tokens-create-page.1597e6ab.chunk.js → transfer-tokens-create-page.ebba16d8.chunk.js} +1 -1
  82. package/build/{transfer-tokens-edit-page.8741529f.chunk.js → transfer-tokens-edit-page.d7bb2b3e.chunk.js} +1 -1
  83. package/build/{transfer-tokens-list-page.d6986b03.chunk.js → transfer-tokens-list-page.cfe1736c.chunk.js} +2 -2
  84. package/build/{upload-settings.7f93d4c0.chunk.js → upload-settings.cc5ad813.chunk.js} +1 -1
  85. package/build/{upload.37488080.chunk.js → upload.756efc28.chunk.js} +1 -1
  86. package/build/users-advanced-settings-page.818d84eb.chunk.js +9 -0
  87. package/build/users-email-settings-page.c1967c09.chunk.js +9 -0
  88. package/build/users-providers-settings-page.11893e08.chunk.js +14 -0
  89. package/build/users-roles-settings-page.2b051e6a.chunk.js +55 -0
  90. package/build/{webhook-edit-page.6cb479ff.chunk.js → webhook-edit-page.de45c635.chunk.js} +2 -2
  91. package/build/{webhook-list-page.65e1b5bb.chunk.js → webhook-list-page.ca91df8b.chunk.js} +1 -1
  92. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/AssigneeFilter.js +42 -0
  93. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/StageFilter.js +70 -0
  94. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/constants.js +71 -0
  95. package/ee/admin/content-manager/pages/EditView/InformationBox/InformationBoxEE.js +9 -217
  96. package/ee/admin/content-manager/pages/EditView/InformationBox/components/AssigneeSelect/AssigneeSelect.js +147 -0
  97. package/ee/admin/content-manager/pages/EditView/InformationBox/components/AssigneeSelect/index.js +1 -0
  98. package/ee/admin/content-manager/pages/EditView/InformationBox/components/StageSelect/StageSelect.js +243 -0
  99. package/ee/admin/content-manager/pages/EditView/InformationBox/components/StageSelect/index.js +1 -0
  100. package/ee/admin/content-manager/pages/EditView/InformationBox/constants.js +2 -0
  101. package/ee/admin/content-manager/pages/ListSettingsView/constants.js +7 -0
  102. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/ReviewWorkflowsAssigneeEE.js +21 -0
  103. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants.js +44 -17
  104. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/index.js +1 -0
  105. package/ee/admin/hooks/useAuthProviders.js +25 -0
  106. package/ee/admin/hooks/{useLicenseLimitNotification/index.js → useLicenseLimitNotification.js} +2 -4
  107. package/ee/admin/hooks/{useLicenseLimits/useLicenseLimits.js → useLicenseLimits.js} +4 -1
  108. package/ee/admin/pages/AuthPage/components/Login/index.js +8 -4
  109. package/ee/admin/pages/AuthPage/components/Providers/index.js +8 -5
  110. package/ee/admin/pages/HomePage/index.js +1 -1
  111. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js +1 -1
  112. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js +1 -1
  113. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js +1 -1
  114. package/ee/admin/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js +1 -1
  115. package/ee/admin/pages/SettingsPage/pages/Users/ListPage/index.js +1 -1
  116. package/ee/server/constants/workflows.js +1 -0
  117. package/ee/server/controllers/index.js +1 -0
  118. package/ee/server/controllers/workflows/assignees/index.js +44 -0
  119. package/ee/server/routes/review-workflows.js +17 -0
  120. package/ee/server/services/index.js +1 -0
  121. package/ee/server/services/review-workflows/assignees.js +54 -0
  122. package/ee/server/services/review-workflows/metrics/index.js +5 -0
  123. package/ee/server/services/review-workflows/review-workflows.js +20 -11
  124. package/ee/server/validation/review-workflows.js +8 -0
  125. package/index.js +2 -6
  126. package/package.json +9 -9
  127. package/scripts/build.js +15 -15
  128. package/scripts/create-dev-plugins-file.js +5 -38
  129. package/server/controllers/role.js +2 -0
  130. package/server/controllers/user.js +2 -0
  131. package/server/services/permission/permissions-manager/index.js +3 -1
  132. package/server/services/permission/permissions-manager/sanitize.js +19 -7
  133. package/server/services/permission/permissions-manager/validate.js +218 -0
  134. package/utils/create-cache-dir.js +62 -16
  135. package/utils/create-plugins-exclude-path.js +3 -23
  136. package/utils/get-plugins.js +110 -0
  137. package/utils/index.js +1 -1
  138. package/webpack.config.js +10 -13
  139. package/admin/src/content-manager/components/AttributeFilter/Filters.js +0 -58
  140. package/admin/src/content-manager/components/AttributeFilter/hooks/useAllowedAttributes.js +0 -42
  141. package/admin/src/content-manager/components/AttributeFilter/index.js +0 -40
  142. package/admin/src/content-manager/components/Wysiwyg/EditorStylesContainer.js +0 -344
  143. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/api.js +0 -23
  144. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/prefixAllUrls.js +0 -17
  145. package/admin/src/pages/SettingsPage/utils/createSectionsRoutes.js +0 -11
  146. package/admin/src/pages/SettingsPage/utils/getSectionsToDisplay.js +0 -5
  147. package/admin/src/pages/SettingsPage/utils/index.js +0 -2
  148. package/build/2379.f1641312.chunk.js +0 -1
  149. package/build/2395.46f8d0c1.chunk.js +0 -26
  150. package/build/2801.5cef5ec8.chunk.js +0 -1
  151. package/build/3929.5632f24d.chunk.js +0 -114
  152. package/build/3984.dda474f7.chunk.js +0 -1
  153. package/build/4546.7a3c0d03.chunk.js +0 -1
  154. package/build/502.8ae8ef60.chunk.js +0 -1
  155. package/build/5483.6dd2e776.chunk.js +0 -6
  156. package/build/5542.2415a393.chunk.js +0 -63
  157. package/build/6158.c974fd83.chunk.js +0 -1
  158. package/build/7464.3e64a1d5.chunk.js +0 -1
  159. package/build/8276.10a3f883.chunk.js +0 -26
  160. package/build/Admin_settingsPage.97cb9d41.chunk.js +0 -111
  161. package/build/admin-app.91898385.chunk.js +0 -36
  162. package/build/content-manager.b40f79c0.chunk.js +0 -1099
  163. package/build/email-settings-page.d494d1eb.chunk.js +0 -11
  164. package/build/en-json.08c05fcf.chunk.js +0 -1
  165. package/build/main.9dbe4579.js +0 -2859
  166. package/build/review-workflows-settings-create-view.cb08cfa2.chunk.js +0 -1
  167. package/build/review-workflows-settings-edit-view.3c7cbe63.chunk.js +0 -1
  168. package/build/review-workflows-settings-list-view.1611dc1f.chunk.js +0 -56
  169. package/build/users-advanced-settings-page.f0760eb8.chunk.js +0 -9
  170. package/build/users-email-settings-page.ff4b32f3.chunk.js +0 -9
  171. package/build/users-providers-settings-page.48de0306.chunk.js +0 -14
  172. package/build/users-roles-settings-page.9d9a1eff.chunk.js +0 -30
  173. package/ee/admin/hooks/index.js +0 -4
  174. package/ee/admin/hooks/useAuthProviders/index.js +0 -50
  175. package/ee/admin/hooks/useAuthProviders/reducer.js +0 -26
  176. package/ee/admin/hooks/useLicenseLimits/index.js +0 -1
  177. package/scripts/create-plugins-file.js +0 -92
  178. package/utils/get-plugins-path.js +0 -41
  179. /package/ee/admin/hooks/{useLicenseLimits/__mocks__/index.js → __mocks__/useLicenseLimits.js} +0 -0
  180. /package/server/services/permission/permissions-manager/{query-builers.js → query-builders.js} +0 -0
@@ -0,0 +1,17 @@
1
+ import { usePersistentState } from '@strapi/helper-plugin';
2
+
3
+ // Exported to make it available during admin user registration.
4
+ // Because we only enable the NPS for users who subscribe to the newsletter when signing up
5
+ export function useNpsSurveySettings() {
6
+ const [npsSurveySettings, setNpsSurveySettings] = usePersistentState(
7
+ 'STRAPI_NPS_SURVEY_SETTINGS',
8
+ {
9
+ enabled: true,
10
+ lastResponseDate: null,
11
+ firstDismissalDate: null,
12
+ lastDismissalDate: null,
13
+ }
14
+ );
15
+
16
+ return { npsSurveySettings, setNpsSurveySettings };
17
+ }
@@ -0,0 +1,365 @@
1
+ import * as React from 'react';
2
+
3
+ import {
4
+ Box,
5
+ Flex,
6
+ IconButton,
7
+ Button,
8
+ Typography,
9
+ Textarea,
10
+ Portal,
11
+ Field,
12
+ FieldLabel,
13
+ FieldInput,
14
+ VisuallyHidden,
15
+ } from '@strapi/design-system';
16
+ import { auth, useNotification, useAppInfo } from '@strapi/helper-plugin';
17
+ import { Cross } from '@strapi/icons';
18
+ import { Formik, Form } from 'formik';
19
+ import { useIntl } from 'react-intl';
20
+ import { useMutation } from 'react-query';
21
+ import styled, { useTheme } from 'styled-components';
22
+ import * as yup from 'yup';
23
+
24
+ import { useNpsSurveySettings } from './hooks/useNpsSurveySettings';
25
+
26
+ const FieldWrapper = styled(Field)`
27
+ height: ${32 / 16}rem;
28
+ width: ${32 / 16}rem;
29
+
30
+ > label,
31
+ ~ input {
32
+ display: block;
33
+ position: absolute;
34
+ top: 0;
35
+ left: 0;
36
+ right: 0;
37
+ bottom: 0;
38
+ }
39
+
40
+ > label {
41
+ color: inherit;
42
+ cursor: pointer;
43
+ padding: ${({ theme }) => theme.spaces[2]};
44
+ text-align: center;
45
+ vertical-align: middle;
46
+ }
47
+
48
+ &:hover,
49
+ &:focus-within {
50
+ background-color: ${({ theme }) => theme.colors.neutral0};
51
+ }
52
+
53
+ &:active,
54
+ &.selected {
55
+ color: ${({ theme }) => theme.colors.primary700};
56
+ background-color: ${({ theme }) => theme.colors.neutral0};
57
+ border-color: ${({ theme }) => theme.colors.primary700};
58
+ }
59
+ `;
60
+
61
+ const delays = {
62
+ postResponse: 90 * 24 * 60 * 60 * 1000, // 90 days in ms
63
+ postFirstDismissal: 7 * 24 * 60 * 60 * 1000, // 7 days in ms
64
+ postSubsequentDismissal: 90 * 24 * 60 * 60 * 1000, // 90 days in ms
65
+ display: 5 * 60 * 1000, // 5 minutes in ms
66
+ };
67
+
68
+ const ratingArray = [...Array(11).keys()];
69
+
70
+ const checkIfShouldShowSurvey = (settings) => {
71
+ const { enabled, lastResponseDate, firstDismissalDate, lastDismissalDate } = settings;
72
+
73
+ // This function goes through all the cases where we'd want to not show the survey:
74
+ // 1. If the survey is disabled, abort mission, don't bother checking the other settings.
75
+ // 2. If the user has already responded to the survey, check if enough time has passed since the last response.
76
+ // 3. If the user has dismissed the survey twice or more before, check if enough time has passed since the last dismissal.
77
+ // 4. If the user has only dismissed the survey once before, check if enough time has passed since the first dismissal.
78
+ // If none of these cases check out, then we show the survey.
79
+ // Note that submitting a response resets the dismissal counts.
80
+ // Checks 3 and 4 should not be reversed, since the first dismissal will also exist if the user has dismissed the survey twice or more before.
81
+
82
+ // User hasn't enabled NPS feature
83
+ if (!enabled) {
84
+ return false;
85
+ }
86
+
87
+ // The user has already responded to the survey
88
+ if (lastResponseDate) {
89
+ const timeSinceLastResponse = Date.now() - new Date(lastResponseDate).getTime();
90
+
91
+ if (timeSinceLastResponse >= delays.postResponse) {
92
+ return true;
93
+ }
94
+
95
+ return false;
96
+ }
97
+
98
+ // The user has dismissed the survey twice or more before
99
+ if (lastDismissalDate) {
100
+ const timeSinceLastDismissal = Date.now() - new Date(lastDismissalDate).getTime();
101
+
102
+ if (timeSinceLastDismissal >= delays.postSubsequentDismissal) {
103
+ return true;
104
+ }
105
+
106
+ return false;
107
+ }
108
+
109
+ // The user has only dismissed the survey once before
110
+ if (firstDismissalDate) {
111
+ const timeSinceFirstDismissal = Date.now() - new Date(firstDismissalDate).getTime();
112
+
113
+ if (timeSinceFirstDismissal >= delays.postFirstDismissal) {
114
+ return true;
115
+ }
116
+
117
+ return false;
118
+ }
119
+
120
+ // The user has not interacted with the survey before
121
+ return true;
122
+ };
123
+
124
+ const NpsSurvey = () => {
125
+ const theme = useTheme();
126
+ const { formatMessage } = useIntl();
127
+ const { npsSurveySettings, setNpsSurveySettings } = useNpsSurveySettings();
128
+ const [isFeedbackResponse, setIsFeedbackResponse] = React.useState(false);
129
+ const toggleNotification = useNotification();
130
+ const { currentEnvironment, strapiVersion } = useAppInfo();
131
+
132
+ const { mutate, isLoading } = useMutation(
133
+ async (form) => {
134
+ const res = await fetch('https://analytics.strapi.io/submit-nps', {
135
+ method: 'POST',
136
+ headers: {
137
+ 'Content-Type': 'application/json',
138
+ },
139
+ body: JSON.stringify(form),
140
+ });
141
+
142
+ if (!res.ok) {
143
+ throw new Error('Failed to submit NPS survey');
144
+ }
145
+
146
+ return res;
147
+ },
148
+ {
149
+ onSuccess() {
150
+ setNpsSurveySettings((settings) => ({
151
+ ...settings,
152
+ lastResponseDate: new Date(),
153
+ firstDismissalDate: null,
154
+ lastDismissalDate: null,
155
+ }));
156
+ setIsFeedbackResponse(true);
157
+ // Thank you message displayed in the banner should disappear after few seconds.
158
+ setTimeout(() => {
159
+ setSurveyIsShown(false);
160
+ }, 3000);
161
+ },
162
+ onError() {
163
+ toggleNotification({
164
+ type: 'warning',
165
+ message: formatMessage({ id: 'notification.error' }),
166
+ });
167
+ },
168
+ }
169
+ );
170
+
171
+ // Only check on first render if the survey should be shown
172
+ const [surveyIsShown, setSurveyIsShown] = React.useState(
173
+ checkIfShouldShowSurvey(npsSurveySettings)
174
+ );
175
+
176
+ // Set a cooldown to show the survey when session begins
177
+ const [displaySurvey, setDisplaySurvey] = React.useState(false);
178
+
179
+ React.useEffect(() => {
180
+ const displayTime = setTimeout(() => {
181
+ setDisplaySurvey(true);
182
+ }, delays.display);
183
+
184
+ return () => {
185
+ clearTimeout(displayTime);
186
+ };
187
+ }, []);
188
+
189
+ if (!displaySurvey) {
190
+ return null;
191
+ }
192
+
193
+ if (!surveyIsShown) {
194
+ return null;
195
+ }
196
+
197
+ const handleSubmitResponse = ({ npsSurveyRating: rating, npsSurveyFeedback: comment }) => {
198
+ const { email } = auth.getUserInfo();
199
+ mutate({
200
+ email,
201
+ rating,
202
+ comment,
203
+ environment: currentEnvironment,
204
+ version: strapiVersion,
205
+ license: window.strapi.projectType,
206
+ });
207
+ };
208
+
209
+ const handleDismiss = () => {
210
+ setNpsSurveySettings((settings) => {
211
+ const nextSettings = {
212
+ ...settings,
213
+ lastResponseDate: null,
214
+ };
215
+
216
+ if (settings.firstDismissalDate) {
217
+ // If the user dismisses the survey for the second time
218
+ nextSettings.lastDismissalDate = new Date();
219
+ } else {
220
+ // If the user dismisses the survey for the first time
221
+ nextSettings.firstDismissalDate = new Date();
222
+ }
223
+
224
+ return nextSettings;
225
+ });
226
+
227
+ setSurveyIsShown(false);
228
+ };
229
+
230
+ return (
231
+ <Portal>
232
+ <Formik
233
+ initialValues={{ npsSurveyFeedback: '', npsSurveyRating: null }}
234
+ onSubmit={handleSubmitResponse}
235
+ validationSchema={yup.object({
236
+ npsSurveyFeedback: yup.string(),
237
+ npsSurveyRating: yup.number().required(),
238
+ })}
239
+ >
240
+ {({ values, handleChange, setFieldValue }) => (
241
+ <Form name="npsSurveyForm">
242
+ <Flex
243
+ hasRadius
244
+ direction="column"
245
+ padding={4}
246
+ borderColor="primary200"
247
+ background="neutral0"
248
+ shadow="popupShadow"
249
+ position="fixed"
250
+ bottom={0}
251
+ left="50%"
252
+ transform="translateX(-50%)"
253
+ zIndex={theme.zIndices[2]}
254
+ width="50%"
255
+ >
256
+ {isFeedbackResponse ? (
257
+ <Typography fontWeight="semiBold">
258
+ {formatMessage({
259
+ id: 'app.components.NpsSurvey.feedback-response',
260
+ defaultMessage: 'Thank you very much for your feedback!',
261
+ })}
262
+ </Typography>
263
+ ) : (
264
+ <Box as="fieldset" width="100%">
265
+ <Flex justifyContent="space-between" width="100%">
266
+ <Box marginLeft="auto" marginRight="auto">
267
+ <Typography fontWeight="semiBold" as="legend">
268
+ {formatMessage({
269
+ id: 'app.components.NpsSurvey.banner-title',
270
+ defaultMessage:
271
+ 'How likely are you to recommend Strapi to a friend or colleague?',
272
+ })}
273
+ </Typography>
274
+ </Box>
275
+ <IconButton
276
+ onClick={handleDismiss}
277
+ aria-label={formatMessage({
278
+ id: 'app.components.NpsSurvey.dismiss-survey-label',
279
+ defaultMessage: 'Dismiss survey',
280
+ })}
281
+ icon={<Cross />}
282
+ />
283
+ </Flex>
284
+ <Flex gap={2} marginTop={2} marginBottom={2} justifyContent="center">
285
+ <Typography variant="pi" textColor="neutral600">
286
+ {formatMessage({
287
+ id: 'app.components.NpsSurvey.no-recommendation',
288
+ defaultMessage: 'Not at all likely',
289
+ })}
290
+ </Typography>
291
+ {ratingArray.map((number) => {
292
+ return (
293
+ <FieldWrapper
294
+ key={number}
295
+ className={values.npsSurveyRating === number ? 'selected' : null} // "selected" class added when child radio button is checked
296
+ hasRadius
297
+ background="primary100"
298
+ borderColor="primary200"
299
+ color="primary600"
300
+ position="relative"
301
+ cursor="pointer"
302
+ >
303
+ <FieldLabel htmlFor={`nps-survey-rating-${number}-input`}>
304
+ <VisuallyHidden>
305
+ <FieldInput
306
+ type="radio"
307
+ id={`nps-survey-rating-${number}-input`}
308
+ name="npsSurveyRating"
309
+ checked={values.npsSurveyRating === number}
310
+ onChange={(e) =>
311
+ setFieldValue('npsSurveyRating', parseInt(e.target.value, 10))
312
+ }
313
+ value={number}
314
+ />
315
+ </VisuallyHidden>
316
+ {number}
317
+ </FieldLabel>
318
+ </FieldWrapper>
319
+ );
320
+ })}
321
+ <Typography variant="pi" textColor="neutral600">
322
+ {formatMessage({
323
+ id: 'app.components.NpsSurvey.happy-to-recommend',
324
+ defaultMessage: 'Extremely likely',
325
+ })}
326
+ </Typography>
327
+ </Flex>
328
+ {values.npsSurveyRating !== null && (
329
+ <Flex direction="column">
330
+ <Box marginTop={2}>
331
+ <FieldLabel htmlFor="npsSurveyFeedback" fontWeight="semiBold" fontSize={2}>
332
+ {formatMessage({
333
+ id: 'app.components.NpsSurvey.feedback-question',
334
+ defaultMessage: 'Do you have any suggestion for improvements?',
335
+ })}
336
+ </FieldLabel>
337
+ </Box>
338
+ <Box width="62%" marginTop={3} marginBottom={4}>
339
+ <Textarea
340
+ id="npsSurveyFeedback" // formik element attribute "id" should be same as the values key to work
341
+ width="100%"
342
+ onChange={handleChange}
343
+ >
344
+ {values.npsSurveyFeedback}
345
+ </Textarea>
346
+ </Box>
347
+ <Button marginBottom={2} type="submit" loading={isLoading}>
348
+ {formatMessage({
349
+ id: 'app.components.NpsSurvey.submit-feedback',
350
+ defaultMessage: 'Submit Feedback',
351
+ })}
352
+ </Button>
353
+ </Flex>
354
+ )}
355
+ </Box>
356
+ )}
357
+ </Flex>
358
+ </Form>
359
+ )}
360
+ </Formik>
361
+ </Portal>
362
+ );
363
+ };
364
+
365
+ export default NpsSurvey;
@@ -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"
@@ -7,6 +7,7 @@ import take from 'lodash/take';
7
7
  import uniqBy from 'lodash/uniqBy';
8
8
  import unset from 'lodash/unset';
9
9
 
10
+ import { CREATOR_FIELDS } from '../../constants/attributes';
10
11
  import { getMaxTempKey } from '../../utils';
11
12
 
12
13
  import { findAllAndReplace, moveFields } from './utils';
@@ -236,8 +237,12 @@ const reducer = (state, action) =>
236
237
 
237
238
  const findAllRelationsAndReplaceWithEmptyArray = findAllAndReplace(
238
239
  components,
239
- (value) => {
240
- return value.type === 'relation';
240
+ (value, { path }) => {
241
+ const fieldName = path[path.length - 1];
242
+ // We don't replace creator fields because we already return them without need to populate them separately
243
+ const isCreatorField = CREATOR_FIELDS.includes(fieldName);
244
+
245
+ return value.type === 'relation' && !isCreatorField;
241
246
  },
242
247
  (_, { path }) => {
243
248
  if (state.modifiedData?.id === data.id && get(state.modifiedData, path)) {
@@ -2,6 +2,7 @@ import get from 'lodash/get';
2
2
  import isArray from 'lodash/isArray';
3
3
  import isObject from 'lodash/isObject';
4
4
 
5
+ import { CREATOR_FIELDS } from '../../../constants/attributes';
5
6
  import { getInitialDataPathUsingTempKeys } from '../../../utils/paths';
6
7
 
7
8
  /* eslint-disable indent */
@@ -29,6 +30,11 @@ const cleanData = ({ browserState, serverState }, currentSchema, componentsSchem
29
30
  */
30
31
  const recursiveCleanData = (browserState, serverState, schema, pathToParent) => {
31
32
  return Object.keys(browserState).reduce((acc, current) => {
33
+ // Creator attributes can be safely ignored because they are handle on the backend
34
+ if (CREATOR_FIELDS.includes(current)) {
35
+ return acc;
36
+ }
37
+
32
38
  const path = pathToParent ? `${pathToParent}.${current}` : current;
33
39
  const attrType = getType(schema, current);
34
40
 
@@ -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';