@strapi/admin 4.10.2 → 4.11.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.
- package/admin/src/content-manager/components/DynamicTable/BulkActionsBar/index.js +307 -0
- package/admin/src/content-manager/components/DynamicTable/index.js +20 -4
- package/admin/src/content-manager/components/EditViewDataManagerProvider/index.js +3 -2
- package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/index.js +0 -1
- package/admin/src/content-manager/components/Wysiwyg/WysiwygNav.js +156 -155
- package/admin/src/content-manager/pages/ListSettingsView/{utils/excludedSortOptions.js → constants.js} +1 -1
- package/admin/src/content-manager/pages/ListSettingsView/index.js +33 -36
- package/admin/src/content-manager/pages/ListView/index.js +118 -2
- package/admin/src/content-manager/utils/index.js +2 -0
- package/admin/src/content-manager/{components/EditViewDataManagerProvider/utils → utils}/schema.js +1 -1
- package/admin/src/injectionZones.js +6 -1
- package/admin/src/pages/Admin/index.js +3 -0
- package/admin/src/pages/AuthPage/components/Register/index.js +1 -1
- package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/FilterSelect.js +1 -1
- package/admin/src/pages/MarketplacePage/components/NpmPackagesFilters/FiltersPopover.js +52 -41
- package/admin/src/pages/MarketplacePage/components/SortSelect/index.js +16 -0
- package/admin/src/pages/SettingsPage/pages/Users/components/SelectRoles/index.js +2 -2
- package/admin/src/translations/en.json +6 -1
- package/admin/src/translations/ru.json +7 -0
- package/build/3562.57745ccd.chunk.js +50 -0
- package/build/6970.5159b068.chunk.js +1 -0
- package/build/{Admin-authenticatedApp.a8373103.chunk.js → Admin-authenticatedApp.27a2329f.chunk.js} +2 -2
- package/build/{Admin_marketplace.a60cde15.chunk.js → Admin_marketplace.5f7b89e5.chunk.js} +32 -8
- package/build/{Admin_settingsPage.5e045f42.chunk.js → Admin_settingsPage.9d0419bc.chunk.js} +1 -1
- package/build/Upload_ConfigureTheView.d429a7fc.chunk.js +1 -0
- package/build/{admin-app.9bfe4ec7.chunk.js → admin-app.8e1f56bc.chunk.js} +2 -2
- package/build/{admin-edit-users.ba27c532.chunk.js → admin-edit-users.2aae89f5.chunk.js} +4 -4
- package/build/{admin-users.ad5dd832.chunk.js → admin-users.04a823ca.chunk.js} +5 -5
- package/build/content-manager.cf467ecc.chunk.js +1123 -0
- package/build/{content-type-builder-translation-en-json.446b611d.chunk.js → content-type-builder-translation-en-json.5e5f8607.chunk.js} +1 -1
- package/build/en-json.7edb00f6.chunk.js +1 -0
- package/build/i18n-translation-en-json.1ec7becf.chunk.js +1 -0
- package/build/i18n-translation-ru-json.401bc498.chunk.js +1 -0
- package/build/index.html +1 -1
- package/build/{main.41970e4c.js → main.14dca275.js} +183 -188
- package/build/review-workflows-settings.6a662ebd.chunk.js +61 -0
- package/build/ru-json.678cd48b.chunk.js +1 -0
- package/build/{runtime~main.c20330f1.js → runtime~main.1074f2bb.js} +1 -1
- package/build/users-permissions-translation-ru-json.8e883c67.chunk.js +1 -0
- package/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/ReviewWorkflowsStageEE.js +12 -1
- package/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js +6 -1
- package/ee/admin/content-manager/pages/EditView/InformationBox/InformationBoxEE.js +4 -1
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js +10 -7
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/components/OptionColor/OptionColor.js +11 -1
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/components/SingleValueColor/SingleValueColor.js +14 -1
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/utils/colors.js +7 -1
- package/ee/server/routes/audit-logs.js +43 -0
- package/ee/server/routes/index.js +5 -226
- package/ee/server/routes/license-limit.js +29 -0
- package/ee/server/routes/review-workflows.js +112 -0
- package/ee/server/routes/sso.js +60 -0
- package/ee/server/routes/utils.js +15 -0
- package/package.json +16 -16
- package/server/routes/index.js +16 -11
- package/webpack.alias.js +0 -1
- package/admin/src/content-manager/components/DynamicTable/ConfirmDialogDeleteAll/index.js +0 -73
- package/admin/src/content-manager/pages/ListSettingsView/utils/api.js +0 -10
- package/build/6858.56d4d528.chunk.js +0 -50
- package/build/9703.e590889d.chunk.js +0 -1
- package/build/Upload_ConfigureTheView.4fc648b5.chunk.js +0 -1
- package/build/content-manager.d28eb183.chunk.js +0 -1111
- package/build/en-json.c7fc79af.chunk.js +0 -1
- package/build/i18n-translation-en-json.60af6722.chunk.js +0 -1
- package/build/review-workflows-settings.7c0b4e73.chunk.js +0 -61
- package/build/ru-json.e0662702.chunk.js +0 -1
- package/build/users-permissions-translation-ru-json.20e177db.chunk.js +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { useRef, useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
3
4
|
import { useIntl } from 'react-intl';
|
|
4
5
|
import {
|
|
5
6
|
FocusTrap,
|
|
6
|
-
Box,
|
|
7
7
|
Button,
|
|
8
8
|
IconButtonGroup,
|
|
9
9
|
Option,
|
|
@@ -33,6 +33,9 @@ import {
|
|
|
33
33
|
CustomLinkIconButton,
|
|
34
34
|
} from './WysiwygStyles';
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* TODO: refactor this mess.
|
|
38
|
+
*/
|
|
36
39
|
const WysiwygNav = ({
|
|
37
40
|
disabled,
|
|
38
41
|
editorRef,
|
|
@@ -56,68 +59,9 @@ const WysiwygNav = ({
|
|
|
56
59
|
|
|
57
60
|
if (disabled || isPreviewMode) {
|
|
58
61
|
return (
|
|
59
|
-
<
|
|
60
|
-
<
|
|
61
|
-
<
|
|
62
|
-
<Select
|
|
63
|
-
disabled
|
|
64
|
-
id="selectTitle"
|
|
65
|
-
placeholder={selectPlaceholder}
|
|
66
|
-
size="S"
|
|
67
|
-
aria-label={selectPlaceholder}
|
|
68
|
-
>
|
|
69
|
-
<Option value="h1">h1</Option>
|
|
70
|
-
<Option value="h2">h2</Option>
|
|
71
|
-
<Option value="h3">h3</Option>
|
|
72
|
-
<Option value="h4">h4</Option>
|
|
73
|
-
<Option value="h5">h5</Option>
|
|
74
|
-
<Option value="h6">h6</Option>
|
|
75
|
-
</Select>
|
|
76
|
-
|
|
77
|
-
<MainButtons>
|
|
78
|
-
<CustomIconButton disabled id="Bold" label="Bold" name="Bold" icon={<Bold />} />
|
|
79
|
-
<CustomIconButton
|
|
80
|
-
disabled
|
|
81
|
-
id="Italic"
|
|
82
|
-
label="Italic"
|
|
83
|
-
name="Italic"
|
|
84
|
-
icon={<Italic />}
|
|
85
|
-
/>
|
|
86
|
-
<CustomIconButton
|
|
87
|
-
disabled
|
|
88
|
-
id="Underline"
|
|
89
|
-
label="Underline"
|
|
90
|
-
name="Underline"
|
|
91
|
-
icon={<Underline />}
|
|
92
|
-
/>
|
|
93
|
-
</MainButtons>
|
|
94
|
-
|
|
95
|
-
<MoreButton disabled id="more" label="More" icon={<More />} />
|
|
96
|
-
</Flex>
|
|
97
|
-
|
|
98
|
-
{!isExpandMode && (
|
|
99
|
-
<Button onClick={onTogglePreviewMode} variant="tertiary" id="preview">
|
|
100
|
-
{formatMessage({
|
|
101
|
-
id: 'components.Wysiwyg.ToggleMode.markdown-mode',
|
|
102
|
-
defaultMessage: 'Markdown mode',
|
|
103
|
-
})}
|
|
104
|
-
</Button>
|
|
105
|
-
)}
|
|
106
|
-
</Flex>
|
|
107
|
-
</Box>
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<Box padding={2} background="neutral100">
|
|
113
|
-
<Flex justifyContent="space-between">
|
|
114
|
-
<Flex>
|
|
115
|
-
<Select
|
|
116
|
-
id="selectTitle"
|
|
117
|
-
placeholder={selectPlaceholder}
|
|
118
|
-
size="S"
|
|
119
|
-
onChange={(value) => onActionClick(value, editorRef)}
|
|
120
|
-
>
|
|
62
|
+
<Flex padding={2} background="neutral100" justifyContent="space-between">
|
|
63
|
+
<StyledFlex>
|
|
64
|
+
<Select disabled placeholder={selectPlaceholder} size="S" label={selectPlaceholder}>
|
|
121
65
|
<Option value="h1">h1</Option>
|
|
122
66
|
<Option value="h2">h2</Option>
|
|
123
67
|
<Option value="h3">h3</Option>
|
|
@@ -127,22 +71,10 @@ const WysiwygNav = ({
|
|
|
127
71
|
</Select>
|
|
128
72
|
|
|
129
73
|
<MainButtons>
|
|
74
|
+
<CustomIconButton disabled id="Bold" label="Bold" name="Bold" icon={<Bold />} />
|
|
75
|
+
<CustomIconButton disabled id="Italic" label="Italic" name="Italic" icon={<Italic />} />
|
|
130
76
|
<CustomIconButton
|
|
131
|
-
|
|
132
|
-
id="Bold"
|
|
133
|
-
label="Bold"
|
|
134
|
-
name="Bold"
|
|
135
|
-
icon={<Bold />}
|
|
136
|
-
/>
|
|
137
|
-
<CustomIconButton
|
|
138
|
-
onClick={() => onActionClick('Italic', editorRef)}
|
|
139
|
-
id="Italic"
|
|
140
|
-
label="Italic"
|
|
141
|
-
name="Italic"
|
|
142
|
-
icon={<Italic />}
|
|
143
|
-
/>
|
|
144
|
-
<CustomIconButton
|
|
145
|
-
onClick={() => onActionClick('Underline', editorRef)}
|
|
77
|
+
disabled
|
|
146
78
|
id="Underline"
|
|
147
79
|
label="Underline"
|
|
148
80
|
name="Underline"
|
|
@@ -150,90 +82,145 @@ const WysiwygNav = ({
|
|
|
150
82
|
/>
|
|
151
83
|
</MainButtons>
|
|
152
84
|
|
|
153
|
-
<MoreButton
|
|
154
|
-
|
|
155
|
-
onClick={handleTogglePopover}
|
|
156
|
-
id="more"
|
|
157
|
-
label="More"
|
|
158
|
-
icon={<More />}
|
|
159
|
-
/>
|
|
160
|
-
{visiblePopover && (
|
|
161
|
-
<Popover centered source={buttonMoreRef} spacing={4} id="popover">
|
|
162
|
-
<FocusTrap onEscape={handleTogglePopover} restoreFocus={false}>
|
|
163
|
-
<Flex>
|
|
164
|
-
<IconButtonGroupMargin>
|
|
165
|
-
<CustomIconButton
|
|
166
|
-
onClick={() => onActionClick('Strikethrough', editorRef, handleTogglePopover)}
|
|
167
|
-
id="Strikethrough"
|
|
168
|
-
label="Strikethrough"
|
|
169
|
-
name="Strikethrough"
|
|
170
|
-
icon={<StrikeThrough />}
|
|
171
|
-
/>
|
|
172
|
-
<CustomIconButton
|
|
173
|
-
onClick={() => onActionClick('BulletList', editorRef, handleTogglePopover)}
|
|
174
|
-
id="BulletList"
|
|
175
|
-
label="BulletList"
|
|
176
|
-
name="BulletList"
|
|
177
|
-
icon={<BulletList />}
|
|
178
|
-
/>
|
|
179
|
-
<CustomIconButton
|
|
180
|
-
onClick={() => onActionClick('NumberList', editorRef, handleTogglePopover)}
|
|
181
|
-
id="NumberList"
|
|
182
|
-
label="NumberList"
|
|
183
|
-
name="NumberList"
|
|
184
|
-
icon={<NumberList />}
|
|
185
|
-
/>
|
|
186
|
-
</IconButtonGroupMargin>
|
|
187
|
-
<IconButtonGroup>
|
|
188
|
-
<CustomIconButton
|
|
189
|
-
onClick={() => onActionClick('Code', editorRef, handleTogglePopover)}
|
|
190
|
-
id="Code"
|
|
191
|
-
label="Code"
|
|
192
|
-
name="Code"
|
|
193
|
-
icon={<Code />}
|
|
194
|
-
/>
|
|
195
|
-
<CustomIconButton
|
|
196
|
-
onClick={() => {
|
|
197
|
-
handleTogglePopover();
|
|
198
|
-
onToggleMediaLib();
|
|
199
|
-
}}
|
|
200
|
-
id="Image"
|
|
201
|
-
label="Image"
|
|
202
|
-
name="Image"
|
|
203
|
-
icon={<Image />}
|
|
204
|
-
/>
|
|
205
|
-
<CustomLinkIconButton
|
|
206
|
-
onClick={() => onActionClick('Link', editorRef, handleTogglePopover)}
|
|
207
|
-
id="Link"
|
|
208
|
-
label="Link"
|
|
209
|
-
name="Link"
|
|
210
|
-
// eslint-disable-next-line jsx-a11y/anchor-is-valid
|
|
211
|
-
icon={<Link />}
|
|
212
|
-
/>
|
|
213
|
-
<CustomIconButton
|
|
214
|
-
onClick={() => onActionClick('Quote', editorRef, handleTogglePopover)}
|
|
215
|
-
id="Quote"
|
|
216
|
-
label="Quote"
|
|
217
|
-
name="Quote"
|
|
218
|
-
icon={<Quote />}
|
|
219
|
-
/>
|
|
220
|
-
</IconButtonGroup>
|
|
221
|
-
</Flex>
|
|
222
|
-
</FocusTrap>
|
|
223
|
-
</Popover>
|
|
224
|
-
)}
|
|
225
|
-
</Flex>
|
|
85
|
+
<MoreButton disabled id="more" label="More" icon={<More />} />
|
|
86
|
+
</StyledFlex>
|
|
226
87
|
|
|
227
|
-
{
|
|
88
|
+
{!isExpandMode && (
|
|
228
89
|
<Button onClick={onTogglePreviewMode} variant="tertiary" id="preview">
|
|
229
90
|
{formatMessage({
|
|
230
|
-
id: 'components.Wysiwyg.ToggleMode.
|
|
231
|
-
defaultMessage: '
|
|
91
|
+
id: 'components.Wysiwyg.ToggleMode.markdown-mode',
|
|
92
|
+
defaultMessage: 'Markdown mode',
|
|
232
93
|
})}
|
|
233
94
|
</Button>
|
|
234
95
|
)}
|
|
235
96
|
</Flex>
|
|
236
|
-
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<Flex padding={2} background="neutral100" justifyContent="space-between">
|
|
102
|
+
<StyledFlex>
|
|
103
|
+
<Select
|
|
104
|
+
placeholder={selectPlaceholder}
|
|
105
|
+
label={selectPlaceholder}
|
|
106
|
+
size="S"
|
|
107
|
+
onChange={(value) => onActionClick(value, editorRef)}
|
|
108
|
+
>
|
|
109
|
+
<Option value="h1">h1</Option>
|
|
110
|
+
<Option value="h2">h2</Option>
|
|
111
|
+
<Option value="h3">h3</Option>
|
|
112
|
+
<Option value="h4">h4</Option>
|
|
113
|
+
<Option value="h5">h5</Option>
|
|
114
|
+
<Option value="h6">h6</Option>
|
|
115
|
+
</Select>
|
|
116
|
+
|
|
117
|
+
<MainButtons>
|
|
118
|
+
<CustomIconButton
|
|
119
|
+
onClick={() => onActionClick('Bold', editorRef)}
|
|
120
|
+
id="Bold"
|
|
121
|
+
label="Bold"
|
|
122
|
+
name="Bold"
|
|
123
|
+
icon={<Bold />}
|
|
124
|
+
/>
|
|
125
|
+
<CustomIconButton
|
|
126
|
+
onClick={() => onActionClick('Italic', editorRef)}
|
|
127
|
+
id="Italic"
|
|
128
|
+
label="Italic"
|
|
129
|
+
name="Italic"
|
|
130
|
+
icon={<Italic />}
|
|
131
|
+
/>
|
|
132
|
+
<CustomIconButton
|
|
133
|
+
onClick={() => onActionClick('Underline', editorRef)}
|
|
134
|
+
id="Underline"
|
|
135
|
+
label="Underline"
|
|
136
|
+
name="Underline"
|
|
137
|
+
icon={<Underline />}
|
|
138
|
+
/>
|
|
139
|
+
</MainButtons>
|
|
140
|
+
|
|
141
|
+
<MoreButton
|
|
142
|
+
ref={buttonMoreRef}
|
|
143
|
+
onClick={handleTogglePopover}
|
|
144
|
+
id="more"
|
|
145
|
+
label="More"
|
|
146
|
+
icon={<More />}
|
|
147
|
+
/>
|
|
148
|
+
{visiblePopover && (
|
|
149
|
+
<Popover centered source={buttonMoreRef} spacing={4} id="popover">
|
|
150
|
+
<FocusTrap onEscape={handleTogglePopover} restoreFocus={false}>
|
|
151
|
+
<Flex>
|
|
152
|
+
<IconButtonGroupMargin>
|
|
153
|
+
<CustomIconButton
|
|
154
|
+
onClick={() => onActionClick('Strikethrough', editorRef, handleTogglePopover)}
|
|
155
|
+
id="Strikethrough"
|
|
156
|
+
label="Strikethrough"
|
|
157
|
+
name="Strikethrough"
|
|
158
|
+
icon={<StrikeThrough />}
|
|
159
|
+
/>
|
|
160
|
+
<CustomIconButton
|
|
161
|
+
onClick={() => onActionClick('BulletList', editorRef, handleTogglePopover)}
|
|
162
|
+
id="BulletList"
|
|
163
|
+
label="BulletList"
|
|
164
|
+
name="BulletList"
|
|
165
|
+
icon={<BulletList />}
|
|
166
|
+
/>
|
|
167
|
+
<CustomIconButton
|
|
168
|
+
onClick={() => onActionClick('NumberList', editorRef, handleTogglePopover)}
|
|
169
|
+
id="NumberList"
|
|
170
|
+
label="NumberList"
|
|
171
|
+
name="NumberList"
|
|
172
|
+
icon={<NumberList />}
|
|
173
|
+
/>
|
|
174
|
+
</IconButtonGroupMargin>
|
|
175
|
+
<IconButtonGroup>
|
|
176
|
+
<CustomIconButton
|
|
177
|
+
onClick={() => onActionClick('Code', editorRef, handleTogglePopover)}
|
|
178
|
+
id="Code"
|
|
179
|
+
label="Code"
|
|
180
|
+
name="Code"
|
|
181
|
+
icon={<Code />}
|
|
182
|
+
/>
|
|
183
|
+
<CustomIconButton
|
|
184
|
+
onClick={() => {
|
|
185
|
+
handleTogglePopover();
|
|
186
|
+
onToggleMediaLib();
|
|
187
|
+
}}
|
|
188
|
+
id="Image"
|
|
189
|
+
label="Image"
|
|
190
|
+
name="Image"
|
|
191
|
+
icon={<Image />}
|
|
192
|
+
/>
|
|
193
|
+
<CustomLinkIconButton
|
|
194
|
+
onClick={() => onActionClick('Link', editorRef, handleTogglePopover)}
|
|
195
|
+
id="Link"
|
|
196
|
+
label="Link"
|
|
197
|
+
name="Link"
|
|
198
|
+
// eslint-disable-next-line jsx-a11y/anchor-is-valid
|
|
199
|
+
icon={<Link />}
|
|
200
|
+
/>
|
|
201
|
+
<CustomIconButton
|
|
202
|
+
onClick={() => onActionClick('Quote', editorRef, handleTogglePopover)}
|
|
203
|
+
id="Quote"
|
|
204
|
+
label="Quote"
|
|
205
|
+
name="Quote"
|
|
206
|
+
icon={<Quote />}
|
|
207
|
+
/>
|
|
208
|
+
</IconButtonGroup>
|
|
209
|
+
</Flex>
|
|
210
|
+
</FocusTrap>
|
|
211
|
+
</Popover>
|
|
212
|
+
)}
|
|
213
|
+
</StyledFlex>
|
|
214
|
+
|
|
215
|
+
{onTogglePreviewMode && (
|
|
216
|
+
<Button onClick={onTogglePreviewMode} variant="tertiary" id="preview">
|
|
217
|
+
{formatMessage({
|
|
218
|
+
id: 'components.Wysiwyg.ToggleMode.preview-mode',
|
|
219
|
+
defaultMessage: 'Preview mode',
|
|
220
|
+
})}
|
|
221
|
+
</Button>
|
|
222
|
+
)}
|
|
223
|
+
</Flex>
|
|
237
224
|
);
|
|
238
225
|
};
|
|
239
226
|
|
|
@@ -255,3 +242,17 @@ WysiwygNav.propTypes = {
|
|
|
255
242
|
};
|
|
256
243
|
|
|
257
244
|
export default WysiwygNav;
|
|
245
|
+
|
|
246
|
+
const StyledFlex = styled(Flex)`
|
|
247
|
+
/* Hide the label, every input needs a label. */
|
|
248
|
+
label {
|
|
249
|
+
border: 0;
|
|
250
|
+
clip: rect(0 0 0 0);
|
|
251
|
+
height: 1px;
|
|
252
|
+
margin: -1px;
|
|
253
|
+
overflow: hidden;
|
|
254
|
+
padding: 0;
|
|
255
|
+
position: absolute;
|
|
256
|
+
width: 1px;
|
|
257
|
+
}
|
|
258
|
+
`;
|
|
@@ -7,7 +7,13 @@ import pick from 'lodash/pick';
|
|
|
7
7
|
import get from 'lodash/get';
|
|
8
8
|
import isEmpty from 'lodash/isEmpty';
|
|
9
9
|
import { stringify } from 'qs';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
useNotification,
|
|
12
|
+
useTracking,
|
|
13
|
+
ConfirmDialog,
|
|
14
|
+
Link,
|
|
15
|
+
useFetchClient,
|
|
16
|
+
} from '@strapi/helper-plugin';
|
|
11
17
|
import { useIntl } from 'react-intl';
|
|
12
18
|
import {
|
|
13
19
|
Box,
|
|
@@ -19,18 +25,19 @@ import {
|
|
|
19
25
|
Button,
|
|
20
26
|
} from '@strapi/design-system';
|
|
21
27
|
import { Check, ArrowLeft } from '@strapi/icons';
|
|
28
|
+
|
|
22
29
|
import { checkIfAttributeIsDisplayable, getTrad } from '../../utils';
|
|
23
30
|
import ModelsContext from '../../contexts/ModelsContext';
|
|
24
31
|
import { usePluginsQueryParams } from '../../hooks';
|
|
25
|
-
import putCMSettingsLV from './utils/api';
|
|
26
32
|
import Settings from './components/Settings';
|
|
27
33
|
import SortDisplayedFields from './components/SortDisplayedFields';
|
|
28
34
|
import EditFieldForm from './components/EditFieldForm';
|
|
29
35
|
import init from './init';
|
|
30
36
|
import reducer, { initialState } from './reducer';
|
|
31
|
-
import {
|
|
37
|
+
import { EXCLUDED_SORT_ATTRIBUTE_TYPES } from './constants';
|
|
32
38
|
|
|
33
39
|
const ListSettingsView = ({ layout, slug }) => {
|
|
40
|
+
const { put } = useFetchClient();
|
|
34
41
|
const { formatMessage } = useIntl();
|
|
35
42
|
const { trackUsage } = useTracking();
|
|
36
43
|
const pluginsQueryParams = usePluginsQueryParams();
|
|
@@ -130,18 +137,21 @@ const ListSettingsView = ({ layout, slug }) => {
|
|
|
130
137
|
handleCloseModal();
|
|
131
138
|
};
|
|
132
139
|
|
|
133
|
-
const submitMutation = useMutation(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
const submitMutation = useMutation(
|
|
141
|
+
(body) => put(`/content-manager/content-types/${slug}/configuration`, body),
|
|
142
|
+
{
|
|
143
|
+
onSuccess() {
|
|
144
|
+
trackUsage('didEditListSettings');
|
|
145
|
+
refetchData();
|
|
146
|
+
},
|
|
147
|
+
onError() {
|
|
148
|
+
toggleNotification({
|
|
149
|
+
type: 'warning',
|
|
150
|
+
message: { id: 'notification.error' },
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
);
|
|
145
155
|
const { isLoading: isSubmittingForm } = submitMutation;
|
|
146
156
|
|
|
147
157
|
const handleChangeEditLabel = ({ target: { name, value } }) => {
|
|
@@ -153,29 +163,16 @@ const ListSettingsView = ({ layout, slug }) => {
|
|
|
153
163
|
};
|
|
154
164
|
|
|
155
165
|
const listRemainingFields = Object.entries(attributes)
|
|
156
|
-
.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (isDisplayable && !isAlreadyDisplayed) {
|
|
163
|
-
acc.push(attrName);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return acc;
|
|
167
|
-
}, [])
|
|
166
|
+
.filter(
|
|
167
|
+
([name, attribute]) =>
|
|
168
|
+
checkIfAttributeIsDisplayable(attribute) && !displayedFields.includes(name)
|
|
169
|
+
)
|
|
170
|
+
.map(([name]) => name)
|
|
168
171
|
.sort();
|
|
169
172
|
|
|
170
|
-
const sortOptions = Object.entries(attributes)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (!EXCLUDED_SORT_OPTIONS.includes(type)) {
|
|
174
|
-
acc.push(name);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return acc;
|
|
178
|
-
}, []);
|
|
173
|
+
const sortOptions = Object.entries(attributes)
|
|
174
|
+
.filter(([, attribute]) => !EXCLUDED_SORT_ATTRIBUTE_TYPES.includes(attribute.type))
|
|
175
|
+
.map(([name]) => name);
|
|
179
176
|
|
|
180
177
|
const move = (originalIndex, atIndex) => {
|
|
181
178
|
dispatch({
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
useTracking,
|
|
22
22
|
Link,
|
|
23
23
|
useAPIErrorHandler,
|
|
24
|
+
getYupInnerErrors,
|
|
24
25
|
} from '@strapi/helper-plugin';
|
|
25
26
|
|
|
26
27
|
import {
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
} from '@strapi/design-system';
|
|
36
37
|
|
|
37
38
|
import { ArrowLeft, Plus, Cog } from '@strapi/icons';
|
|
39
|
+
import { useMutation } from 'react-query';
|
|
38
40
|
|
|
39
41
|
import DynamicTable from '../../components/DynamicTable';
|
|
40
42
|
import AttributeFilter from '../../components/AttributeFilter';
|
|
@@ -42,7 +44,7 @@ import { InjectionZone } from '../../../shared/components';
|
|
|
42
44
|
|
|
43
45
|
import permissions from '../../../permissions';
|
|
44
46
|
|
|
45
|
-
import { getRequestUrl, getTrad } from '../../utils';
|
|
47
|
+
import { createYupSchema, getRequestUrl, getTrad } from '../../utils';
|
|
46
48
|
|
|
47
49
|
import FieldPicker from './FieldPicker';
|
|
48
50
|
import PaginationFooter from './PaginationFooter';
|
|
@@ -64,6 +66,7 @@ function ListView({
|
|
|
64
66
|
canCreate,
|
|
65
67
|
canDelete,
|
|
66
68
|
canRead,
|
|
69
|
+
canPublish,
|
|
67
70
|
data,
|
|
68
71
|
getData,
|
|
69
72
|
getDataSucceeded,
|
|
@@ -100,6 +103,50 @@ function ListView({
|
|
|
100
103
|
const fetchClient = useFetchClient();
|
|
101
104
|
const { post, del } = fetchClient;
|
|
102
105
|
|
|
106
|
+
const bulkPublishMutation = useMutation(
|
|
107
|
+
(data) =>
|
|
108
|
+
post(`/content-manager/collection-types/${contentType.uid}/actions/bulkPublish`, data),
|
|
109
|
+
{
|
|
110
|
+
onSuccess() {
|
|
111
|
+
toggleNotification({
|
|
112
|
+
type: 'success',
|
|
113
|
+
message: { id: 'content-manager.success.record.publish', defaultMessage: 'Published' },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
fetchData(`/content-manager/collection-types/${slug}${params}`);
|
|
117
|
+
},
|
|
118
|
+
onError(error) {
|
|
119
|
+
toggleNotification({
|
|
120
|
+
type: 'warning',
|
|
121
|
+
message: formatAPIError(error),
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
const bulkUnpublishMutation = useMutation(
|
|
127
|
+
(data) =>
|
|
128
|
+
post(`/content-manager/collection-types/${contentType.uid}/actions/bulkUnpublish`, data),
|
|
129
|
+
{
|
|
130
|
+
onSuccess() {
|
|
131
|
+
toggleNotification({
|
|
132
|
+
type: 'success',
|
|
133
|
+
message: {
|
|
134
|
+
id: 'content-manager.success.record.unpublish',
|
|
135
|
+
defaultMessage: 'Unpublished',
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
fetchData(`/content-manager/collection-types/${slug}${params}`);
|
|
140
|
+
},
|
|
141
|
+
onError(error) {
|
|
142
|
+
toggleNotification({
|
|
143
|
+
type: 'warning',
|
|
144
|
+
message: formatAPIError(error),
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
103
150
|
// FIXME
|
|
104
151
|
// Using a ref to avoid requests being fired multiple times on slug on change
|
|
105
152
|
// We need it because the hook as mulitple dependencies so it may run before the permissions have checked
|
|
@@ -200,6 +247,70 @@ function ListView({
|
|
|
200
247
|
[slug, params, fetchData, toggleNotification, formatAPIError, del]
|
|
201
248
|
);
|
|
202
249
|
|
|
250
|
+
/**
|
|
251
|
+
* @param {number[]} selectedEntries - Array of ids to publish
|
|
252
|
+
* @returns {{validIds: number[], errors: Object.<number, string>}} - Returns an object with the valid ids and the errors
|
|
253
|
+
*/
|
|
254
|
+
const validateEntriesToPublish = async (selectedEntries) => {
|
|
255
|
+
const validations = { validIds: [], errors: {} };
|
|
256
|
+
// Create the validation schema based on the contentType
|
|
257
|
+
const schema = createYupSchema(
|
|
258
|
+
contentType,
|
|
259
|
+
{ components: layout.components },
|
|
260
|
+
{ isDraft: false }
|
|
261
|
+
);
|
|
262
|
+
// Get the selected entries
|
|
263
|
+
const entries = data.filter((entry) => {
|
|
264
|
+
return selectedEntries.includes(entry.id);
|
|
265
|
+
});
|
|
266
|
+
// Validate each entry and map the unresolved promises
|
|
267
|
+
const validationPromises = entries.map((entry) =>
|
|
268
|
+
schema.validate(entry, { abortEarly: false })
|
|
269
|
+
);
|
|
270
|
+
// Resolve all the promises in one go
|
|
271
|
+
const resolvedPromises = await Promise.allSettled(validationPromises);
|
|
272
|
+
// Set the validations
|
|
273
|
+
resolvedPromises.forEach((promise) => {
|
|
274
|
+
if (promise.status === 'rejected') {
|
|
275
|
+
const entityId = promise.reason.value.id;
|
|
276
|
+
validations.errors[entityId] = getYupInnerErrors(promise.reason);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (promise.status === 'fulfilled') {
|
|
280
|
+
validations.validIds.push(promise.value.id);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
return validations;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const handleConfirmPublishAllData = async (selectedEntries) => {
|
|
288
|
+
const validations = await validateEntriesToPublish(selectedEntries);
|
|
289
|
+
|
|
290
|
+
if (Object.values(validations.errors).length) {
|
|
291
|
+
toggleNotification({
|
|
292
|
+
type: 'warning',
|
|
293
|
+
title: {
|
|
294
|
+
id: 'content-manager.listView.validation.errors.title',
|
|
295
|
+
defaultMessage: 'Action required',
|
|
296
|
+
},
|
|
297
|
+
message: {
|
|
298
|
+
id: 'content-manager.listView.validation.errors.message',
|
|
299
|
+
defaultMessage:
|
|
300
|
+
'Please make sure all fields are valid before publishing (required field, min/max character limit, etc.)',
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
throw new Error('Validation error');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return bulkPublishMutation.mutateAsync({ ids: selectedEntries });
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const handleConfirmUnpublishAllData = (selectedEntries) => {
|
|
311
|
+
return bulkUnpublishMutation.mutateAsync({ ids: selectedEntries });
|
|
312
|
+
};
|
|
313
|
+
|
|
203
314
|
useEffect(() => {
|
|
204
315
|
const CancelToken = axios.CancelToken;
|
|
205
316
|
const source = CancelToken.source();
|
|
@@ -331,9 +442,12 @@ function ListView({
|
|
|
331
442
|
<DynamicTable
|
|
332
443
|
canCreate={canCreate}
|
|
333
444
|
canDelete={canDelete}
|
|
445
|
+
canPublish={canPublish}
|
|
334
446
|
contentTypeName={headerLayoutTitle}
|
|
335
|
-
onConfirmDeleteAll={handleConfirmDeleteAllData}
|
|
336
447
|
onConfirmDelete={handleConfirmDeleteData}
|
|
448
|
+
onConfirmDeleteAll={handleConfirmDeleteAllData}
|
|
449
|
+
onConfirmPublishAll={handleConfirmPublishAllData}
|
|
450
|
+
onConfirmUnpublishAll={handleConfirmUnpublishAllData}
|
|
337
451
|
isBulkable={isBulkable}
|
|
338
452
|
isLoading={isLoading}
|
|
339
453
|
// FIXME: remove the layout props drilling
|
|
@@ -355,10 +469,12 @@ ListView.propTypes = {
|
|
|
355
469
|
canCreate: PropTypes.bool.isRequired,
|
|
356
470
|
canDelete: PropTypes.bool.isRequired,
|
|
357
471
|
canRead: PropTypes.bool.isRequired,
|
|
472
|
+
canPublish: PropTypes.bool.isRequired,
|
|
358
473
|
data: PropTypes.array.isRequired,
|
|
359
474
|
layout: PropTypes.exact({
|
|
360
475
|
components: PropTypes.object.isRequired,
|
|
361
476
|
contentType: PropTypes.shape({
|
|
477
|
+
uid: PropTypes.string.isRequired,
|
|
362
478
|
attributes: PropTypes.object.isRequired,
|
|
363
479
|
metadatas: PropTypes.object.isRequired,
|
|
364
480
|
info: PropTypes.shape({ displayName: PropTypes.string.isRequired }).isRequired,
|
|
@@ -18,3 +18,5 @@ export { default as mergeMetasWithSchema } from './mergeMetasWithSchema';
|
|
|
18
18
|
|
|
19
19
|
export { default as removeKeyInObject } from './removeKeyInObject';
|
|
20
20
|
export { default as removePasswordFieldsFromData } from './removePasswordFieldsFromData';
|
|
21
|
+
|
|
22
|
+
export { default as createYupSchema } from './schema';
|
package/admin/src/content-manager/{components/EditViewDataManagerProvider/utils → utils}/schema.js
RENAMED
|
@@ -7,7 +7,7 @@ import toNumber from 'lodash/toNumber';
|
|
|
7
7
|
import * as yup from 'yup';
|
|
8
8
|
import { translatedErrors as errorsTrads } from '@strapi/helper-plugin';
|
|
9
9
|
|
|
10
|
-
import isFieldTypeNumber from '
|
|
10
|
+
import isFieldTypeNumber from './isFieldTypeNumber';
|
|
11
11
|
|
|
12
12
|
yup.addMethod(yup.mixed, 'defined', function () {
|
|
13
13
|
return this.test('defined', errorsTrads.required, (value) => value !== undefined);
|
|
@@ -13,7 +13,12 @@ const injectionZones = {
|
|
|
13
13
|
},
|
|
14
14
|
contentManager: {
|
|
15
15
|
editView: { informations: [], 'right-links': [] },
|
|
16
|
-
listView: {
|
|
16
|
+
listView: {
|
|
17
|
+
actions: [],
|
|
18
|
+
deleteModalAdditionalInfos: [],
|
|
19
|
+
publishModalAdditionalInfos: [],
|
|
20
|
+
unpublishModalAdditionalInfos: [],
|
|
21
|
+
},
|
|
17
22
|
},
|
|
18
23
|
};
|
|
19
24
|
|