@strapi/content-releases 5.9.0 → 5.10.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 (69) hide show
  1. package/dist/admin/chunks/App-B9yCdSLJ.js +1866 -0
  2. package/dist/admin/chunks/App-B9yCdSLJ.js.map +1 -0
  3. package/dist/admin/chunks/App-CuOosufQ.mjs +1845 -0
  4. package/dist/admin/chunks/App-CuOosufQ.mjs.map +1 -0
  5. package/dist/admin/chunks/PurchaseContentReleases-BCME5SQU.js +55 -0
  6. package/dist/admin/chunks/PurchaseContentReleases-BCME5SQU.js.map +1 -0
  7. package/dist/admin/chunks/PurchaseContentReleases-S1ccDSwp.mjs +53 -0
  8. package/dist/admin/chunks/PurchaseContentReleases-S1ccDSwp.mjs.map +1 -0
  9. package/dist/admin/chunks/ReleasesSettingsPage-BzdLEfxa.mjs +206 -0
  10. package/dist/admin/chunks/ReleasesSettingsPage-BzdLEfxa.mjs.map +1 -0
  11. package/dist/admin/chunks/ReleasesSettingsPage-DFVGppsl.js +208 -0
  12. package/dist/admin/chunks/ReleasesSettingsPage-DFVGppsl.js.map +1 -0
  13. package/dist/admin/chunks/en-B2EeDoOz.mjs +101 -0
  14. package/dist/{_chunks/en-D9Q4YW03.mjs.map → admin/chunks/en-B2EeDoOz.mjs.map} +1 -1
  15. package/dist/admin/chunks/en-BzpFfVeO.js +103 -0
  16. package/dist/{_chunks/en-BWPPsSH-.js.map → admin/chunks/en-BzpFfVeO.js.map} +1 -1
  17. package/dist/admin/chunks/index-1nn-zHX-.js +1657 -0
  18. package/dist/admin/chunks/index-1nn-zHX-.js.map +1 -0
  19. package/dist/admin/chunks/index-CmAFGQWf.mjs +1618 -0
  20. package/dist/admin/chunks/index-CmAFGQWf.mjs.map +1 -0
  21. package/dist/admin/chunks/schemas-DMt8h1z-.mjs +43 -0
  22. package/dist/admin/chunks/schemas-DMt8h1z-.mjs.map +1 -0
  23. package/dist/admin/chunks/schemas-DS7NeFDN.js +65 -0
  24. package/dist/admin/chunks/schemas-DS7NeFDN.js.map +1 -0
  25. package/dist/admin/index.js +18 -3
  26. package/dist/admin/index.js.map +1 -1
  27. package/dist/admin/index.mjs +13 -5
  28. package/dist/admin/index.mjs.map +1 -1
  29. package/dist/admin/src/components/ReleaseListCell.d.ts +1 -1
  30. package/dist/server/index.js +2166 -1870
  31. package/dist/server/index.js.map +1 -1
  32. package/dist/server/index.mjs +2156 -1861
  33. package/dist/server/index.mjs.map +1 -1
  34. package/dist/server/src/destroy.d.ts +1 -1
  35. package/dist/server/src/destroy.d.ts.map +1 -1
  36. package/dist/server/src/middlewares/documents.d.ts +1 -1
  37. package/dist/server/src/middlewares/documents.d.ts.map +1 -1
  38. package/dist/server/src/services/scheduling.d.ts +1 -1
  39. package/dist/server/src/services/scheduling.d.ts.map +1 -1
  40. package/dist/server/src/services/validation.d.ts +1 -1
  41. package/dist/server/src/services/validation.d.ts.map +1 -1
  42. package/dist/shared/contracts/release-actions.d.ts +0 -1
  43. package/dist/shared/contracts/releases.d.ts +0 -1
  44. package/dist/shared/contracts/settings.d.ts +1 -2
  45. package/dist/shared/contracts/settings.d.ts.map +1 -1
  46. package/dist/shared/types.d.ts +0 -1
  47. package/package.json +16 -13
  48. package/dist/_chunks/App-CiZCkScI.mjs +0 -1558
  49. package/dist/_chunks/App-CiZCkScI.mjs.map +0 -1
  50. package/dist/_chunks/App-SGjO5UPV.js +0 -1578
  51. package/dist/_chunks/App-SGjO5UPV.js.map +0 -1
  52. package/dist/_chunks/PurchaseContentReleases--qQepXpP.js +0 -52
  53. package/dist/_chunks/PurchaseContentReleases--qQepXpP.js.map +0 -1
  54. package/dist/_chunks/PurchaseContentReleases-D-n-w-st.mjs +0 -52
  55. package/dist/_chunks/PurchaseContentReleases-D-n-w-st.mjs.map +0 -1
  56. package/dist/_chunks/ReleasesSettingsPage-Cto_NLUd.js +0 -178
  57. package/dist/_chunks/ReleasesSettingsPage-Cto_NLUd.js.map +0 -1
  58. package/dist/_chunks/ReleasesSettingsPage-DQT8N3A-.mjs +0 -178
  59. package/dist/_chunks/ReleasesSettingsPage-DQT8N3A-.mjs.map +0 -1
  60. package/dist/_chunks/en-BWPPsSH-.js +0 -102
  61. package/dist/_chunks/en-D9Q4YW03.mjs +0 -102
  62. package/dist/_chunks/index-BjvFfTtA.mjs +0 -1386
  63. package/dist/_chunks/index-BjvFfTtA.mjs.map +0 -1
  64. package/dist/_chunks/index-CyU534vL.js +0 -1404
  65. package/dist/_chunks/index-CyU534vL.js.map +0 -1
  66. package/dist/_chunks/schemas-DBYv9gK8.js +0 -61
  67. package/dist/_chunks/schemas-DBYv9gK8.js.map +0 -1
  68. package/dist/_chunks/schemas-DdA2ic2U.mjs +0 -44
  69. package/dist/_chunks/schemas-DdA2ic2U.mjs.map +0 -1
@@ -0,0 +1,1866 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var strapiAdmin$1 = require('@strapi/admin/strapi-admin');
5
+ var reactRouterDom = require('react-router-dom');
6
+ var index = require('./index-1nn-zHX-.js');
7
+ var React = require('react');
8
+ var designSystem = require('@strapi/design-system');
9
+ var icons = require('@strapi/icons');
10
+ var symbols = require('@strapi/icons/symbols');
11
+ var format = require('date-fns/format');
12
+ var dateFnsTz = require('date-fns-tz');
13
+ var reactIntl = require('react-intl');
14
+ var styledComponents = require('styled-components');
15
+ var strapiAdmin = require('@strapi/content-manager/strapi-admin');
16
+ var qs = require('qs');
17
+ var dateFns = require('date-fns');
18
+ var formik = require('formik');
19
+ var schemas = require('./schemas-DS7NeFDN.js');
20
+ var reactRedux = require('react-redux');
21
+ var ee = require('@strapi/admin/strapi-admin/ee');
22
+ require('yup');
23
+
24
+ function _interopNamespaceDefault(e) {
25
+ var n = Object.create(null);
26
+ if (e) {
27
+ Object.keys(e).forEach(function (k) {
28
+ if (k !== 'default') {
29
+ var d = Object.getOwnPropertyDescriptor(e, k);
30
+ Object.defineProperty(n, k, d.get ? d : {
31
+ enumerable: true,
32
+ get: function () { return e[k]; }
33
+ });
34
+ }
35
+ });
36
+ }
37
+ n.default = e;
38
+ return Object.freeze(n);
39
+ }
40
+
41
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
42
+
43
+ const StyledPopoverFlex = styledComponents.styled(designSystem.Flex)`
44
+ width: 100%;
45
+ max-width: 256px;
46
+
47
+ & > * {
48
+ border-bottom: 1px solid ${({ theme })=>theme.colors.neutral150};
49
+ }
50
+
51
+ & > *:last-child {
52
+ border-bottom: none;
53
+ }
54
+ `;
55
+ const EntryStatusTrigger = ({ action, status, hasErrors, requiredStage, entryStage })=>{
56
+ const { formatMessage } = reactIntl.useIntl();
57
+ if (action === 'publish') {
58
+ if (hasErrors || requiredStage && requiredStage.id !== entryStage?.id) {
59
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
60
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
61
+ variant: "ghost",
62
+ startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CrossCircle, {
63
+ fill: "danger600"
64
+ }),
65
+ endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
66
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
67
+ textColor: "danger600",
68
+ variant: "omega",
69
+ fontWeight: "bold",
70
+ children: formatMessage({
71
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.not-ready',
72
+ defaultMessage: 'Not ready to publish'
73
+ })
74
+ })
75
+ })
76
+ });
77
+ }
78
+ if (status === 'draft') {
79
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
80
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
81
+ variant: "ghost",
82
+ startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
83
+ fill: "success600"
84
+ }),
85
+ endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
86
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
87
+ textColor: "success600",
88
+ variant: "omega",
89
+ fontWeight: "bold",
90
+ children: formatMessage({
91
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish',
92
+ defaultMessage: 'Ready to publish'
93
+ })
94
+ })
95
+ })
96
+ });
97
+ }
98
+ if (status === 'modified') {
99
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
100
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
101
+ variant: "ghost",
102
+ startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.ArrowsCounterClockwise, {
103
+ fill: "alternative600"
104
+ }),
105
+ endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
106
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
107
+ variant: "omega",
108
+ fontWeight: "bold",
109
+ textColor: "alternative600",
110
+ children: formatMessage({
111
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.modified',
112
+ defaultMessage: 'Ready to publish changes'
113
+ })
114
+ })
115
+ })
116
+ });
117
+ }
118
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
119
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
120
+ variant: "ghost",
121
+ startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
122
+ fill: "success600"
123
+ }),
124
+ endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
125
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
126
+ textColor: "success600",
127
+ variant: "omega",
128
+ fontWeight: "bold",
129
+ children: formatMessage({
130
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.already-published',
131
+ defaultMessage: 'Already published'
132
+ })
133
+ })
134
+ })
135
+ });
136
+ }
137
+ if (status === 'published') {
138
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
139
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
140
+ variant: "ghost",
141
+ startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
142
+ fill: "success600"
143
+ }),
144
+ endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
145
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
146
+ textColor: "success600",
147
+ variant: "omega",
148
+ fontWeight: "bold",
149
+ children: formatMessage({
150
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish',
151
+ defaultMessage: 'Ready to unpublish'
152
+ })
153
+ })
154
+ })
155
+ });
156
+ }
157
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Trigger, {
158
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
159
+ variant: "ghost",
160
+ startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
161
+ fill: "success600"
162
+ }),
163
+ endIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.CaretDown, {}),
164
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
165
+ textColor: "success600",
166
+ variant: "omega",
167
+ fontWeight: "bold",
168
+ children: formatMessage({
169
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.already-unpublished',
170
+ defaultMessage: 'Already unpublished'
171
+ })
172
+ })
173
+ })
174
+ });
175
+ };
176
+ const FieldsValidation = ({ hasErrors, errors, kind, contentTypeUid, documentId, locale })=>{
177
+ const { formatMessage } = reactIntl.useIntl();
178
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
179
+ direction: "column",
180
+ gap: 1,
181
+ width: "100%",
182
+ padding: 5,
183
+ children: [
184
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
185
+ gap: 2,
186
+ width: "100%",
187
+ children: [
188
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
189
+ fontWeight: "bold",
190
+ children: formatMessage({
191
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.fields',
192
+ defaultMessage: 'Fields'
193
+ })
194
+ }),
195
+ hasErrors ? /*#__PURE__*/ jsxRuntime.jsx(icons.CrossCircle, {
196
+ fill: "danger600"
197
+ }) : /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
198
+ fill: "success600"
199
+ })
200
+ ]
201
+ }),
202
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
203
+ width: "100%",
204
+ textColor: "neutral600",
205
+ children: hasErrors ? formatMessage({
206
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.fields.error',
207
+ defaultMessage: '{errors} errors on fields.'
208
+ }, {
209
+ errors: errors ? Object.keys(errors).length : 0
210
+ }) : formatMessage({
211
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.fields.success',
212
+ defaultMessage: 'All fields are filled correctly.'
213
+ })
214
+ }),
215
+ hasErrors && /*#__PURE__*/ jsxRuntime.jsx(designSystem.LinkButton, {
216
+ tag: reactRouterDom.Link,
217
+ to: {
218
+ pathname: `/content-manager/${kind === 'collectionType' ? 'collection-types' : 'single-types'}/${contentTypeUid}/${documentId}`,
219
+ search: locale ? qs.stringify({
220
+ plugins: {
221
+ i18n: {
222
+ locale
223
+ }
224
+ }
225
+ }) : ''
226
+ },
227
+ variant: "secondary",
228
+ fullWidth: true,
229
+ state: {
230
+ forceValidation: true
231
+ },
232
+ children: formatMessage({
233
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.fields.see-errors',
234
+ defaultMessage: 'See errors'
235
+ })
236
+ })
237
+ ]
238
+ });
239
+ };
240
+ const getReviewStageIcon = ({ contentTypeHasReviewWorkflow, requiredStage, entryStage })=>{
241
+ if (!contentTypeHasReviewWorkflow) {
242
+ return /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
243
+ fill: "neutral200"
244
+ });
245
+ }
246
+ if (requiredStage && requiredStage.id !== entryStage?.id) {
247
+ return /*#__PURE__*/ jsxRuntime.jsx(icons.CrossCircle, {
248
+ fill: "danger600"
249
+ });
250
+ }
251
+ return /*#__PURE__*/ jsxRuntime.jsx(icons.CheckCircle, {
252
+ fill: "success600"
253
+ });
254
+ };
255
+ const getReviewStageMessage = ({ contentTypeHasReviewWorkflow, requiredStage, entryStage, formatMessage })=>{
256
+ if (!contentTypeHasReviewWorkflow) {
257
+ return formatMessage({
258
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.not-enabled',
259
+ defaultMessage: 'This entry is not associated to any workflow.'
260
+ });
261
+ }
262
+ if (requiredStage && requiredStage.id !== entryStage?.id) {
263
+ return formatMessage({
264
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.not-ready',
265
+ defaultMessage: 'This entry is not at the required stage for publishing. ({stageName})'
266
+ }, {
267
+ stageName: requiredStage?.name ?? ''
268
+ });
269
+ }
270
+ if (requiredStage && requiredStage.id === entryStage?.id) {
271
+ return formatMessage({
272
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.ready',
273
+ defaultMessage: 'This entry is at the required stage for publishing. ({stageName})'
274
+ }, {
275
+ stageName: requiredStage?.name ?? ''
276
+ });
277
+ }
278
+ return formatMessage({
279
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage.stage-not-required',
280
+ defaultMessage: 'No required stage for publication'
281
+ });
282
+ };
283
+ const ReviewStageValidation = ({ contentTypeHasReviewWorkflow, requiredStage, entryStage })=>{
284
+ const { formatMessage } = reactIntl.useIntl();
285
+ const Icon = getReviewStageIcon({
286
+ contentTypeHasReviewWorkflow,
287
+ requiredStage,
288
+ entryStage
289
+ });
290
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
291
+ direction: "column",
292
+ gap: 1,
293
+ width: "100%",
294
+ padding: 5,
295
+ children: [
296
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
297
+ gap: 2,
298
+ width: "100%",
299
+ children: [
300
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
301
+ fontWeight: "bold",
302
+ children: formatMessage({
303
+ id: 'content-releases.pages.ReleaseDetails.entry-validation.review-stage',
304
+ defaultMessage: 'Review stage'
305
+ })
306
+ }),
307
+ Icon
308
+ ]
309
+ }),
310
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
311
+ textColor: "neutral600",
312
+ children: getReviewStageMessage({
313
+ contentTypeHasReviewWorkflow,
314
+ requiredStage,
315
+ entryStage,
316
+ formatMessage
317
+ })
318
+ })
319
+ ]
320
+ });
321
+ };
322
+ const EntryValidationPopover = ({ schema, entry, status, action })=>{
323
+ const { validate, isLoading } = strapiAdmin.unstable_useDocument({
324
+ collectionType: schema?.kind ?? '',
325
+ model: schema?.uid ?? ''
326
+ }, {
327
+ // useDocument makes a request to get more data about the entry, but we only want to have the validation function so we skip the request
328
+ skip: true
329
+ });
330
+ // Validation errors
331
+ const errors = isLoading ? null : validate(entry);
332
+ const hasErrors = errors ? Object.keys(errors).length > 0 : false;
333
+ // Entry stage
334
+ const contentTypeHasReviewWorkflow = schema?.hasReviewWorkflow ?? false;
335
+ const requiredStage = schema?.stageRequiredToPublish;
336
+ const entryStage = entry.strapi_stage;
337
+ if (isLoading) {
338
+ return null;
339
+ }
340
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Popover.Root, {
341
+ children: [
342
+ /*#__PURE__*/ jsxRuntime.jsx(EntryStatusTrigger, {
343
+ action: action,
344
+ status: status,
345
+ hasErrors: hasErrors,
346
+ requiredStage: requiredStage,
347
+ entryStage: entryStage
348
+ }),
349
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Popover.Content, {
350
+ children: /*#__PURE__*/ jsxRuntime.jsxs(StyledPopoverFlex, {
351
+ direction: "column",
352
+ children: [
353
+ /*#__PURE__*/ jsxRuntime.jsx(FieldsValidation, {
354
+ hasErrors: hasErrors,
355
+ errors: errors,
356
+ contentTypeUid: schema?.uid,
357
+ kind: schema?.kind,
358
+ documentId: entry.documentId,
359
+ locale: entry.locale
360
+ }),
361
+ /*#__PURE__*/ jsxRuntime.jsx(ReviewStageValidation, {
362
+ contentTypeHasReviewWorkflow: contentTypeHasReviewWorkflow,
363
+ requiredStage: requiredStage,
364
+ entryStage: entryStage
365
+ })
366
+ ]
367
+ })
368
+ })
369
+ ]
370
+ });
371
+ };
372
+
373
+ const intervals = [
374
+ 'years',
375
+ 'months',
376
+ 'days',
377
+ 'hours',
378
+ 'minutes',
379
+ 'seconds'
380
+ ];
381
+ /**
382
+ * Displays the relative time between a given timestamp and the current time.
383
+ * You can display a custom message for given time intervals by passing an array of custom intervals.
384
+ *
385
+ * @example
386
+ * ```jsx
387
+ * <caption>Display "last hour" if the timestamp is less than an hour ago</caption>
388
+ * <RelativeTime
389
+ * timestamp={new Date('2021-01-01')}
390
+ * customIntervals={[
391
+ * { unit: 'hours', threshold: 1, text: 'last hour' },
392
+ * ]}
393
+ * ```
394
+ */ const RelativeTime$1 = /*#__PURE__*/ React__namespace.forwardRef(({ timestamp, customIntervals = [], ...restProps }, forwardedRef)=>{
395
+ const { formatRelativeTime, formatDate, formatTime } = reactIntl.useIntl();
396
+ /**
397
+ * TODO: make this auto-update, like a clock.
398
+ */ const interval = dateFns.intervalToDuration({
399
+ start: timestamp,
400
+ end: Date.now()
401
+ });
402
+ const unit = intervals.find((intervalUnit)=>{
403
+ return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
404
+ });
405
+ const relativeTime = dateFns.isPast(timestamp) ? -interval[unit] : interval[unit];
406
+ // Display custom text if interval is less than the threshold
407
+ const customInterval = customIntervals.find((custom)=>interval[custom.unit] < custom.threshold);
408
+ const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, {
409
+ numeric: 'auto'
410
+ });
411
+ return /*#__PURE__*/ jsxRuntime.jsx("time", {
412
+ ref: forwardedRef,
413
+ dateTime: timestamp.toISOString(),
414
+ role: "time",
415
+ title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
416
+ ...restProps,
417
+ children: displayText
418
+ });
419
+ });
420
+
421
+ const ReleaseModal = ({ handleClose, open, handleSubmit, initialValues, isLoading = false })=>{
422
+ const { formatMessage } = reactIntl.useIntl();
423
+ const { pathname } = reactRouterDom.useLocation();
424
+ const isCreatingRelease = pathname === `/plugins/${index.pluginId}`;
425
+ // Set default first timezone from the list if no system timezone detected
426
+ const { timezoneList, systemTimezone = {
427
+ value: 'UTC+00:00-Africa/Abidjan '
428
+ } } = index.getTimezones(initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : new Date());
429
+ /**
430
+ * Generate scheduled time using selected date, time and timezone
431
+ */ const getScheduledTimestamp = (values)=>{
432
+ const { date, time, timezone } = values;
433
+ if (!date || !time || !timezone) return null;
434
+ const timezoneWithoutOffset = timezone.split('&')[1];
435
+ return dateFnsTz.zonedTimeToUtc(`${date} ${time}`, timezoneWithoutOffset);
436
+ };
437
+ /**
438
+ * Get timezone with offset to show the selected value in the dropdown
439
+ */ const getTimezoneWithOffset = ()=>{
440
+ const currentTimezone = timezoneList.find((timezone)=>timezone.value.split('&')[1] === initialValues.timezone);
441
+ return currentTimezone?.value || systemTimezone.value;
442
+ };
443
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Root, {
444
+ open: open,
445
+ onOpenChange: handleClose,
446
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Modal.Content, {
447
+ children: [
448
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Header, {
449
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Title, {
450
+ children: formatMessage({
451
+ id: 'content-releases.modal.title',
452
+ defaultMessage: '{isCreatingRelease, select, true {New release} other {Edit release}}'
453
+ }, {
454
+ isCreatingRelease: isCreatingRelease
455
+ })
456
+ })
457
+ }),
458
+ /*#__PURE__*/ jsxRuntime.jsx(formik.Formik, {
459
+ onSubmit: (values)=>{
460
+ handleSubmit({
461
+ ...values,
462
+ timezone: values.timezone ? values.timezone.split('&')[1] : null,
463
+ scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
464
+ });
465
+ },
466
+ initialValues: {
467
+ ...initialValues,
468
+ timezone: initialValues.timezone ? getTimezoneWithOffset() : systemTimezone.value
469
+ },
470
+ validationSchema: schemas.RELEASE_SCHEMA,
471
+ validateOnChange: false,
472
+ children: ({ values, errors, handleChange, setFieldValue })=>{
473
+ return /*#__PURE__*/ jsxRuntime.jsxs(formik.Form, {
474
+ children: [
475
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Body, {
476
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
477
+ direction: "column",
478
+ alignItems: "stretch",
479
+ gap: 6,
480
+ children: [
481
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
482
+ name: "name",
483
+ error: errors.name && formatMessage({
484
+ id: errors.name,
485
+ defaultMessage: errors.name
486
+ }),
487
+ required: true,
488
+ children: [
489
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
490
+ children: formatMessage({
491
+ id: 'content-releases.modal.form.input.label.release-name',
492
+ defaultMessage: 'Name'
493
+ })
494
+ }),
495
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.TextInput, {
496
+ value: values.name,
497
+ onChange: handleChange
498
+ }),
499
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
500
+ ]
501
+ }),
502
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
503
+ width: "max-content",
504
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Checkbox, {
505
+ name: "isScheduled",
506
+ checked: values.isScheduled,
507
+ onCheckedChange: (checked)=>{
508
+ setFieldValue('isScheduled', checked);
509
+ if (!checked) {
510
+ // Clear scheduling info from a release on unchecking schedule release, which reset scheduling info in DB
511
+ setFieldValue('date', null);
512
+ setFieldValue('time', '');
513
+ setFieldValue('timezone', null);
514
+ } else {
515
+ // On ticking back schedule release date, time and timezone should be restored to the initial state
516
+ setFieldValue('date', initialValues.date);
517
+ setFieldValue('time', initialValues.time);
518
+ setFieldValue('timezone', initialValues.timezone ?? systemTimezone?.value);
519
+ }
520
+ },
521
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
522
+ textColor: values.isScheduled ? 'primary600' : 'neutral800',
523
+ fontWeight: values.isScheduled ? 'semiBold' : 'regular',
524
+ children: formatMessage({
525
+ id: 'modal.form.input.label.schedule-release',
526
+ defaultMessage: 'Schedule release'
527
+ })
528
+ })
529
+ })
530
+ }),
531
+ values.isScheduled && /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
532
+ children: [
533
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
534
+ gap: 4,
535
+ alignItems: "start",
536
+ children: [
537
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
538
+ width: "100%",
539
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
540
+ name: "date",
541
+ error: errors.date && formatMessage({
542
+ id: errors.date,
543
+ defaultMessage: errors.date
544
+ }),
545
+ required: true,
546
+ children: [
547
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
548
+ children: formatMessage({
549
+ id: 'content-releases.modal.form.input.label.date',
550
+ defaultMessage: 'Date'
551
+ })
552
+ }),
553
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.DatePicker, {
554
+ onChange: (date)=>{
555
+ const isoFormatDate = date ? dateFns.formatISO(date, {
556
+ representation: 'date'
557
+ }) : null;
558
+ setFieldValue('date', isoFormatDate);
559
+ },
560
+ clearLabel: formatMessage({
561
+ id: 'content-releases.modal.form.input.clearLabel',
562
+ defaultMessage: 'Clear'
563
+ }),
564
+ onClear: ()=>{
565
+ setFieldValue('date', null);
566
+ },
567
+ value: values.date ? new Date(values.date) : new Date(),
568
+ minDate: dateFnsTz.utcToZonedTime(new Date(), values.timezone.split('&')[1])
569
+ }),
570
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
571
+ ]
572
+ })
573
+ }),
574
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
575
+ width: "100%",
576
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
577
+ name: "time",
578
+ error: errors.time && formatMessage({
579
+ id: errors.time,
580
+ defaultMessage: errors.time
581
+ }),
582
+ required: true,
583
+ children: [
584
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
585
+ children: formatMessage({
586
+ id: 'content-releases.modal.form.input.label.time',
587
+ defaultMessage: 'Time'
588
+ })
589
+ }),
590
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.TimePicker, {
591
+ onChange: (time)=>{
592
+ setFieldValue('time', time);
593
+ },
594
+ clearLabel: formatMessage({
595
+ id: 'content-releases.modal.form.input.clearLabel',
596
+ defaultMessage: 'Clear'
597
+ }),
598
+ onClear: ()=>{
599
+ setFieldValue('time', '');
600
+ },
601
+ value: values.time || undefined
602
+ }),
603
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
604
+ ]
605
+ })
606
+ })
607
+ ]
608
+ }),
609
+ /*#__PURE__*/ jsxRuntime.jsx(TimezoneComponent, {
610
+ timezoneOptions: timezoneList
611
+ })
612
+ ]
613
+ })
614
+ ]
615
+ })
616
+ }),
617
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Modal.Footer, {
618
+ children: [
619
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Modal.Close, {
620
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
621
+ variant: "tertiary",
622
+ name: "cancel",
623
+ children: formatMessage({
624
+ id: 'cancel',
625
+ defaultMessage: 'Cancel'
626
+ })
627
+ })
628
+ }),
629
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
630
+ name: "submit",
631
+ loading: isLoading,
632
+ type: "submit",
633
+ children: formatMessage({
634
+ id: 'content-releases.modal.form.button.submit',
635
+ defaultMessage: '{isCreatingRelease, select, true {Continue} other {Save}}'
636
+ }, {
637
+ isCreatingRelease: isCreatingRelease
638
+ })
639
+ })
640
+ ]
641
+ })
642
+ ]
643
+ });
644
+ }
645
+ })
646
+ ]
647
+ })
648
+ });
649
+ };
650
+ const TimezoneComponent = ({ timezoneOptions })=>{
651
+ const { values, errors, setFieldValue } = formik.useFormikContext();
652
+ const { formatMessage } = reactIntl.useIntl();
653
+ const [timezoneList, setTimezoneList] = React__namespace.useState(timezoneOptions);
654
+ React__namespace.useEffect(()=>{
655
+ if (values.date) {
656
+ // Update the timezone offset which varies with DST based on the date selected
657
+ const { timezoneList } = index.getTimezones(new Date(values.date));
658
+ setTimezoneList(timezoneList);
659
+ const updatedTimezone = values.timezone && timezoneList.find((tz)=>tz.value.split('&')[1] === values.timezone.split('&')[1]);
660
+ if (updatedTimezone) {
661
+ setFieldValue('timezone', updatedTimezone.value);
662
+ }
663
+ }
664
+ }, [
665
+ setFieldValue,
666
+ values.date,
667
+ values.timezone
668
+ ]);
669
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Field.Root, {
670
+ name: "timezone",
671
+ error: errors.timezone && formatMessage({
672
+ id: errors.timezone,
673
+ defaultMessage: errors.timezone
674
+ }),
675
+ required: true,
676
+ children: [
677
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
678
+ children: formatMessage({
679
+ id: 'content-releases.modal.form.input.label.timezone',
680
+ defaultMessage: 'Timezone'
681
+ })
682
+ }),
683
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Combobox, {
684
+ autocomplete: {
685
+ type: 'list',
686
+ filter: 'contains'
687
+ },
688
+ value: values.timezone || undefined,
689
+ textValue: values.timezone ? values.timezone.replace(/&/, ' ') : undefined,
690
+ onChange: (timezone)=>{
691
+ setFieldValue('timezone', timezone);
692
+ },
693
+ onTextValueChange: (timezone)=>{
694
+ setFieldValue('timezone', timezone);
695
+ },
696
+ onClear: ()=>{
697
+ setFieldValue('timezone', '');
698
+ },
699
+ children: timezoneList.map((timezone)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.ComboboxOption, {
700
+ value: timezone.value,
701
+ children: timezone.value.replace(/&/, ' ')
702
+ }, timezone.value))
703
+ }),
704
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Error, {})
705
+ ]
706
+ });
707
+ };
708
+
709
+ const useTypedDispatch = reactRedux.useDispatch;
710
+
711
+ const isBaseQueryError = (error)=>{
712
+ return typeof error !== 'undefined' && error.name !== undefined;
713
+ };
714
+
715
+ const LinkCard = styledComponents.styled(designSystem.Link)`
716
+ display: block;
717
+ `;
718
+ const RelativeTime = styledComponents.styled(RelativeTime$1)`
719
+ display: inline-block;
720
+ &::first-letter {
721
+ text-transform: uppercase;
722
+ }
723
+ `;
724
+ const getBadgeProps = (status)=>{
725
+ let color;
726
+ switch(status){
727
+ case 'ready':
728
+ color = 'success';
729
+ break;
730
+ case 'blocked':
731
+ color = 'warning';
732
+ break;
733
+ case 'failed':
734
+ color = 'danger';
735
+ break;
736
+ case 'done':
737
+ color = 'primary';
738
+ break;
739
+ case 'empty':
740
+ default:
741
+ color = 'neutral';
742
+ }
743
+ return {
744
+ textColor: `${color}600`,
745
+ backgroundColor: `${color}100`,
746
+ borderColor: `${color}200`
747
+ };
748
+ };
749
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false })=>{
750
+ const { formatMessage } = reactIntl.useIntl();
751
+ if (isError) {
752
+ return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Page.Error, {});
753
+ }
754
+ if (releases?.length === 0) {
755
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.EmptyStateLayout, {
756
+ content: formatMessage({
757
+ id: 'content-releases.page.Releases.tab.emptyEntries',
758
+ defaultMessage: 'No releases'
759
+ }, {
760
+ target: sectionTitle
761
+ }),
762
+ icon: /*#__PURE__*/ jsxRuntime.jsx(symbols.EmptyDocuments, {
763
+ width: "16rem"
764
+ })
765
+ });
766
+ }
767
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Root, {
768
+ gap: 4,
769
+ children: releases.map(({ id, name, scheduledAt, status })=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
770
+ col: 3,
771
+ s: 6,
772
+ xs: 12,
773
+ direction: "column",
774
+ alignItems: "stretch",
775
+ children: /*#__PURE__*/ jsxRuntime.jsx(LinkCard, {
776
+ tag: reactRouterDom.NavLink,
777
+ to: `${id}`,
778
+ isExternal: false,
779
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
780
+ direction: "column",
781
+ justifyContent: "space-between",
782
+ padding: 4,
783
+ hasRadius: true,
784
+ background: "neutral0",
785
+ shadow: "tableShadow",
786
+ height: "100%",
787
+ width: "100%",
788
+ alignItems: "start",
789
+ gap: 4,
790
+ children: [
791
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
792
+ direction: "column",
793
+ alignItems: "start",
794
+ gap: 1,
795
+ children: [
796
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
797
+ textColor: "neutral800",
798
+ tag: "h3",
799
+ variant: "delta",
800
+ fontWeight: "bold",
801
+ children: name
802
+ }),
803
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
804
+ variant: "pi",
805
+ textColor: "neutral600",
806
+ children: scheduledAt ? /*#__PURE__*/ jsxRuntime.jsx(RelativeTime, {
807
+ timestamp: new Date(scheduledAt)
808
+ }) : formatMessage({
809
+ id: 'content-releases.pages.Releases.not-scheduled',
810
+ defaultMessage: 'Not scheduled'
811
+ })
812
+ })
813
+ ]
814
+ }),
815
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Badge, {
816
+ ...getBadgeProps(status),
817
+ children: status
818
+ })
819
+ ]
820
+ })
821
+ })
822
+ }, id))
823
+ });
824
+ };
825
+ /* -------------------------------------------------------------------------------------------------
826
+ * ReleasesPage
827
+ * -----------------------------------------------------------------------------------------------*/ const StyledAlert = styledComponents.styled(designSystem.Alert)`
828
+ button {
829
+ display: none;
830
+ }
831
+ p + div {
832
+ margin-left: auto;
833
+ }
834
+ `;
835
+ const INITIAL_FORM_VALUES = {
836
+ name: '',
837
+ date: dateFns.format(new Date(), 'yyyy-MM-dd'),
838
+ time: '',
839
+ isScheduled: true,
840
+ scheduledAt: null,
841
+ timezone: null
842
+ };
843
+ const ReleasesPage = ()=>{
844
+ const location = reactRouterDom.useLocation();
845
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
846
+ const { toggleNotification } = strapiAdmin$1.useNotification();
847
+ const { formatMessage } = reactIntl.useIntl();
848
+ const navigate = reactRouterDom.useNavigate();
849
+ const { formatAPIError } = strapiAdmin$1.useAPIErrorHandler();
850
+ const [{ query }, setQuery] = strapiAdmin$1.useQueryParams();
851
+ const response = index.useGetReleasesQuery(query);
852
+ const { data, isLoading: isLoadingSettings } = index.useGetReleaseSettingsQuery();
853
+ const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
854
+ const { getFeature } = ee.useLicenseLimits();
855
+ const { maximumReleases = 3 } = getFeature('cms-content-releases');
856
+ const { trackUsage } = strapiAdmin$1.useTracking();
857
+ const { allowedActions: { canCreate } } = strapiAdmin$1.useRBAC(index.PERMISSIONS);
858
+ const { isLoading: isLoadingReleases, isSuccess, isError } = response;
859
+ const activeTab = response?.currentData?.meta?.activeTab || 'pending';
860
+ // Check if we have some errors and show a notification to the user to explain the error
861
+ React__namespace.useEffect(()=>{
862
+ if (location?.state?.errors) {
863
+ toggleNotification({
864
+ type: 'danger',
865
+ title: formatMessage({
866
+ id: 'content-releases.pages.Releases.notification.error.title',
867
+ defaultMessage: 'Your request could not be processed.'
868
+ }),
869
+ message: formatMessage({
870
+ id: 'content-releases.pages.Releases.notification.error.message',
871
+ defaultMessage: 'Please try again or open another release.'
872
+ })
873
+ });
874
+ navigate('', {
875
+ replace: true,
876
+ state: null
877
+ });
878
+ }
879
+ }, [
880
+ formatMessage,
881
+ location?.state?.errors,
882
+ navigate,
883
+ toggleNotification
884
+ ]);
885
+ const toggleAddReleaseModal = ()=>{
886
+ setReleaseModalShown((prev)=>!prev);
887
+ };
888
+ if (isLoadingReleases || isLoadingSettings) {
889
+ return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Page.Loading, {});
890
+ }
891
+ const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
892
+ const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
893
+ const handleTabChange = (tabValue)=>{
894
+ setQuery({
895
+ ...query,
896
+ page: 1,
897
+ pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
898
+ filters: {
899
+ releasedAt: {
900
+ $notNull: tabValue !== 'pending'
901
+ }
902
+ }
903
+ });
904
+ };
905
+ const handleAddRelease = async ({ name, scheduledAt, timezone })=>{
906
+ const response = await createRelease({
907
+ name,
908
+ scheduledAt,
909
+ timezone
910
+ });
911
+ if ('data' in response) {
912
+ // When the response returns an object with 'data', handle success
913
+ toggleNotification({
914
+ type: 'success',
915
+ message: formatMessage({
916
+ id: 'content-releases.modal.release-created-notification-success',
917
+ defaultMessage: 'Release created.'
918
+ })
919
+ });
920
+ trackUsage('didCreateRelease');
921
+ navigate(response.data.data.id.toString());
922
+ } else if (strapiAdmin$1.isFetchError(response.error)) {
923
+ // When the response returns an object with 'error', handle fetch error
924
+ toggleNotification({
925
+ type: 'danger',
926
+ message: formatAPIError(response.error)
927
+ });
928
+ } else {
929
+ // Otherwise, the response returns an object with 'error', handle a generic error
930
+ toggleNotification({
931
+ type: 'danger',
932
+ message: formatMessage({
933
+ id: 'notification.error',
934
+ defaultMessage: 'An error occurred'
935
+ })
936
+ });
937
+ }
938
+ };
939
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Main, {
940
+ "aria-busy": isLoadingReleases || isLoadingSettings,
941
+ children: [
942
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Layouts.Header, {
943
+ title: formatMessage({
944
+ id: 'content-releases.pages.Releases.title',
945
+ defaultMessage: 'Releases'
946
+ }),
947
+ subtitle: formatMessage({
948
+ id: 'content-releases.pages.Releases.header-subtitle',
949
+ defaultMessage: 'Create and manage content updates'
950
+ }),
951
+ primaryAction: canCreate ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
952
+ startIcon: /*#__PURE__*/ jsxRuntime.jsx(icons.Plus, {}),
953
+ onClick: toggleAddReleaseModal,
954
+ disabled: hasReachedMaximumPendingReleases,
955
+ children: formatMessage({
956
+ id: 'content-releases.header.actions.add-release',
957
+ defaultMessage: 'New release'
958
+ })
959
+ }) : null
960
+ }),
961
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Layouts.Content, {
962
+ children: /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
963
+ children: [
964
+ hasReachedMaximumPendingReleases && /*#__PURE__*/ jsxRuntime.jsx(StyledAlert, {
965
+ marginBottom: 6,
966
+ action: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Link, {
967
+ href: "https://strapi.io/pricing-cloud",
968
+ isExternal: true,
969
+ children: formatMessage({
970
+ id: 'content-releases.pages.Releases.max-limit-reached.action',
971
+ defaultMessage: 'Explore plans'
972
+ })
973
+ }),
974
+ title: formatMessage({
975
+ id: 'content-releases.pages.Releases.max-limit-reached.title',
976
+ defaultMessage: 'You have reached the {number} pending {number, plural, one {release} other {releases}} limit.'
977
+ }, {
978
+ number: maximumReleases
979
+ }),
980
+ onClose: ()=>{},
981
+ closeLabel: "",
982
+ children: formatMessage({
983
+ id: 'content-releases.pages.Releases.max-limit-reached.message',
984
+ defaultMessage: 'Upgrade to manage an unlimited number of releases.'
985
+ })
986
+ }),
987
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Tabs.Root, {
988
+ variant: "simple",
989
+ onValueChange: handleTabChange,
990
+ value: activeTab,
991
+ children: [
992
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Box, {
993
+ paddingBottom: 8,
994
+ children: [
995
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Tabs.List, {
996
+ "aria-label": formatMessage({
997
+ id: 'content-releases.pages.Releases.tab-group.label',
998
+ defaultMessage: 'Releases list'
999
+ }),
1000
+ children: [
1001
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tabs.Trigger, {
1002
+ value: "pending",
1003
+ children: formatMessage({
1004
+ id: 'content-releases.pages.Releases.tab.pending',
1005
+ defaultMessage: 'Pending ({count})'
1006
+ }, {
1007
+ count: totalPendingReleases
1008
+ })
1009
+ }),
1010
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tabs.Trigger, {
1011
+ value: "done",
1012
+ children: formatMessage({
1013
+ id: 'content-releases.pages.Releases.tab.done',
1014
+ defaultMessage: 'Done'
1015
+ })
1016
+ })
1017
+ ]
1018
+ }),
1019
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Divider, {})
1020
+ ]
1021
+ }),
1022
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tabs.Content, {
1023
+ value: "pending",
1024
+ children: /*#__PURE__*/ jsxRuntime.jsx(ReleasesGrid, {
1025
+ sectionTitle: "pending",
1026
+ releases: response?.currentData?.data,
1027
+ isError: isError
1028
+ })
1029
+ }),
1030
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tabs.Content, {
1031
+ value: "done",
1032
+ children: /*#__PURE__*/ jsxRuntime.jsx(ReleasesGrid, {
1033
+ sectionTitle: "done",
1034
+ releases: response?.currentData?.data,
1035
+ isError: isError
1036
+ })
1037
+ })
1038
+ ]
1039
+ }),
1040
+ /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin$1.Pagination.Root, {
1041
+ ...response?.currentData?.meta?.pagination,
1042
+ defaultPageSize: response?.currentData?.meta?.pagination?.pageSize,
1043
+ children: [
1044
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Pagination.PageSize, {
1045
+ options: [
1046
+ '8',
1047
+ '16',
1048
+ '32',
1049
+ '64'
1050
+ ]
1051
+ }),
1052
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Pagination.Links, {})
1053
+ ]
1054
+ })
1055
+ ]
1056
+ })
1057
+ }),
1058
+ /*#__PURE__*/ jsxRuntime.jsx(ReleaseModal, {
1059
+ open: releaseModalShown,
1060
+ handleClose: toggleAddReleaseModal,
1061
+ handleSubmit: handleAddRelease,
1062
+ isLoading: isSubmittingForm,
1063
+ initialValues: {
1064
+ ...INITIAL_FORM_VALUES,
1065
+ timezone: data?.data.defaultTimezone ? data.data.defaultTimezone.split('&')[1] : null
1066
+ }
1067
+ })
1068
+ ]
1069
+ });
1070
+ };
1071
+
1072
+ /* -------------------------------------------------------------------------------------------------
1073
+ * ReleaseDetailsLayout
1074
+ * -----------------------------------------------------------------------------------------------*/ const ReleaseInfoWrapper = styledComponents.styled(designSystem.Flex)`
1075
+ align-self: stretch;
1076
+ border-bottom-right-radius: ${({ theme })=>theme.borderRadius};
1077
+ border-bottom-left-radius: ${({ theme })=>theme.borderRadius};
1078
+ border-top: 1px solid ${({ theme })=>theme.colors.neutral150};
1079
+ `;
1080
+ const StyledMenuItem = styledComponents.styled(designSystem.MenuItem)`
1081
+ svg path {
1082
+ fill: ${({ theme, disabled })=>disabled && theme.colors.neutral500};
1083
+ }
1084
+ span {
1085
+ color: ${({ theme, disabled })=>disabled && theme.colors.neutral500};
1086
+ }
1087
+
1088
+ &:hover {
1089
+ background: ${({ theme, $variant = 'neutral' })=>theme.colors[`${$variant}100`]};
1090
+ }
1091
+ `;
1092
+ const PencilIcon = styledComponents.styled(icons.Pencil)`
1093
+ width: ${({ theme })=>theme.spaces[4]};
1094
+ height: ${({ theme })=>theme.spaces[4]};
1095
+ path {
1096
+ fill: ${({ theme })=>theme.colors.neutral600};
1097
+ }
1098
+ `;
1099
+ const TrashIcon = styledComponents.styled(icons.Trash)`
1100
+ width: ${({ theme })=>theme.spaces[4]};
1101
+ height: ${({ theme })=>theme.spaces[4]};
1102
+ path {
1103
+ fill: ${({ theme })=>theme.colors.danger600};
1104
+ }
1105
+ `;
1106
+ const ReleaseDetailsLayout = ({ toggleEditReleaseModal, toggleWarningSubmit, children })=>{
1107
+ const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
1108
+ const { releaseId } = reactRouterDom.useParams();
1109
+ const { data, isLoading: isLoadingDetails, error } = index.useGetReleaseQuery({
1110
+ id: releaseId
1111
+ }, {
1112
+ skip: !releaseId
1113
+ });
1114
+ const [publishRelease, { isLoading: isPublishing }] = index.usePublishReleaseMutation();
1115
+ const { toggleNotification } = strapiAdmin$1.useNotification();
1116
+ const { formatAPIError } = strapiAdmin$1.useAPIErrorHandler();
1117
+ const { allowedActions } = strapiAdmin$1.useRBAC(index.PERMISSIONS);
1118
+ const { canUpdate, canDelete, canPublish } = allowedActions;
1119
+ const dispatch = useTypedDispatch();
1120
+ const { trackUsage } = strapiAdmin$1.useTracking();
1121
+ const release = data?.data;
1122
+ const handlePublishRelease = (id)=>async ()=>{
1123
+ const response = await publishRelease({
1124
+ id
1125
+ });
1126
+ if ('data' in response) {
1127
+ // When the response returns an object with 'data', handle success
1128
+ toggleNotification({
1129
+ type: 'success',
1130
+ message: formatMessage({
1131
+ id: 'content-releases.pages.ReleaseDetails.publish-notification-success',
1132
+ defaultMessage: 'Release was published successfully.'
1133
+ })
1134
+ });
1135
+ const { totalEntries, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
1136
+ trackUsage('didPublishRelease', {
1137
+ totalEntries,
1138
+ totalPublishedEntries,
1139
+ totalUnpublishedEntries
1140
+ });
1141
+ } else if (strapiAdmin$1.isFetchError(response.error)) {
1142
+ // When the response returns an object with 'error', handle fetch error
1143
+ toggleNotification({
1144
+ type: 'danger',
1145
+ message: formatAPIError(response.error)
1146
+ });
1147
+ } else {
1148
+ // Otherwise, the response returns an object with 'error', handle a generic error
1149
+ toggleNotification({
1150
+ type: 'danger',
1151
+ message: formatMessage({
1152
+ id: 'notification.error',
1153
+ defaultMessage: 'An error occurred'
1154
+ })
1155
+ });
1156
+ }
1157
+ };
1158
+ const handleRefresh = ()=>{
1159
+ dispatch(index.releaseApi.util.invalidateTags([
1160
+ {
1161
+ type: 'ReleaseAction',
1162
+ id: 'LIST'
1163
+ },
1164
+ {
1165
+ type: 'Release',
1166
+ id: releaseId
1167
+ }
1168
+ ]));
1169
+ };
1170
+ const getCreatedByUser = ()=>{
1171
+ if (!release?.createdBy) {
1172
+ return null;
1173
+ }
1174
+ // Favor the username
1175
+ if (release.createdBy.username) {
1176
+ return release.createdBy.username;
1177
+ }
1178
+ // Firstname may not exist if created with SSO
1179
+ if (release.createdBy.firstname) {
1180
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ''}`.trim();
1181
+ }
1182
+ // All users must have at least an email
1183
+ return release.createdBy.email;
1184
+ };
1185
+ if (isLoadingDetails) {
1186
+ return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Page.Loading, {});
1187
+ }
1188
+ if (isBaseQueryError(error) && 'code' in error || !release) {
1189
+ return /*#__PURE__*/ jsxRuntime.jsx(reactRouterDom.Navigate, {
1190
+ to: "..",
1191
+ state: {
1192
+ errors: [
1193
+ {
1194
+ // @ts-expect-error – TODO: fix this weird error flow
1195
+ code: error?.code
1196
+ }
1197
+ ]
1198
+ }
1199
+ });
1200
+ }
1201
+ const totalEntries = release.actions.meta.count || 0;
1202
+ const hasCreatedByUser = Boolean(getCreatedByUser());
1203
+ const isScheduled = release.scheduledAt && release.timezone;
1204
+ const numberOfEntriesText = formatMessage({
1205
+ id: 'content-releases.pages.Details.header-subtitle',
1206
+ defaultMessage: '{number, plural, =0 {No entries} one {# entry} other {# entries}}'
1207
+ }, {
1208
+ number: totalEntries
1209
+ });
1210
+ const scheduledText = isScheduled ? formatMessage({
1211
+ id: 'content-releases.pages.ReleaseDetails.header-subtitle.scheduled',
1212
+ defaultMessage: 'Scheduled for {date} at {time} ({offset})'
1213
+ }, {
1214
+ date: formatDate(new Date(release.scheduledAt), {
1215
+ weekday: 'long',
1216
+ day: 'numeric',
1217
+ month: 'long',
1218
+ year: 'numeric',
1219
+ timeZone: release.timezone
1220
+ }),
1221
+ time: formatTime(new Date(release.scheduledAt), {
1222
+ timeZone: release.timezone,
1223
+ hourCycle: 'h23'
1224
+ }),
1225
+ offset: index.getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
1226
+ }) : '';
1227
+ return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Main, {
1228
+ "aria-busy": isLoadingDetails,
1229
+ children: [
1230
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Layouts.Header, {
1231
+ title: release.name,
1232
+ subtitle: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
1233
+ gap: 2,
1234
+ lineHeight: 6,
1235
+ children: [
1236
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1237
+ textColor: "neutral600",
1238
+ variant: "epsilon",
1239
+ children: numberOfEntriesText + (isScheduled ? ` - ${scheduledText}` : '')
1240
+ }),
1241
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Badge, {
1242
+ ...getBadgeProps(release.status),
1243
+ children: release.status
1244
+ })
1245
+ ]
1246
+ }),
1247
+ navigationAction: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.BackButton, {
1248
+ fallback: ".."
1249
+ }),
1250
+ primaryAction: !release.releasedAt && /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
1251
+ gap: 2,
1252
+ children: [
1253
+ /*#__PURE__*/ jsxRuntime.jsxs(SimpleMenuButton, {
1254
+ label: /*#__PURE__*/ jsxRuntime.jsx(icons.More, {}),
1255
+ variant: "tertiary",
1256
+ endIcon: null,
1257
+ paddingLeft: "7px",
1258
+ paddingRight: "7px",
1259
+ "aria-label": formatMessage({
1260
+ id: 'content-releases.header.actions.open-release-actions',
1261
+ defaultMessage: 'Release edit and delete menu'
1262
+ }),
1263
+ popoverPlacement: "bottom-end",
1264
+ children: [
1265
+ /*#__PURE__*/ jsxRuntime.jsx(StyledMenuItem, {
1266
+ disabled: !canUpdate,
1267
+ onSelect: toggleEditReleaseModal,
1268
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
1269
+ alignItems: "center",
1270
+ gap: 2,
1271
+ hasRadius: true,
1272
+ width: "100%",
1273
+ children: [
1274
+ /*#__PURE__*/ jsxRuntime.jsx(PencilIcon, {}),
1275
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1276
+ ellipsis: true,
1277
+ children: formatMessage({
1278
+ id: 'content-releases.header.actions.edit',
1279
+ defaultMessage: 'Edit'
1280
+ })
1281
+ })
1282
+ ]
1283
+ })
1284
+ }),
1285
+ /*#__PURE__*/ jsxRuntime.jsx(StyledMenuItem, {
1286
+ disabled: !canDelete,
1287
+ onSelect: toggleWarningSubmit,
1288
+ $variant: "danger",
1289
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
1290
+ alignItems: "center",
1291
+ gap: 2,
1292
+ hasRadius: true,
1293
+ width: "100%",
1294
+ children: [
1295
+ /*#__PURE__*/ jsxRuntime.jsx(TrashIcon, {}),
1296
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1297
+ ellipsis: true,
1298
+ textColor: "danger600",
1299
+ children: formatMessage({
1300
+ id: 'content-releases.header.actions.delete',
1301
+ defaultMessage: 'Delete'
1302
+ })
1303
+ })
1304
+ ]
1305
+ })
1306
+ }),
1307
+ /*#__PURE__*/ jsxRuntime.jsxs(ReleaseInfoWrapper, {
1308
+ direction: "column",
1309
+ justifyContent: "center",
1310
+ alignItems: "flex-start",
1311
+ gap: 1,
1312
+ padding: 4,
1313
+ children: [
1314
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1315
+ variant: "pi",
1316
+ fontWeight: "bold",
1317
+ children: formatMessage({
1318
+ id: 'content-releases.header.actions.created',
1319
+ defaultMessage: 'Created'
1320
+ })
1321
+ }),
1322
+ /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Typography, {
1323
+ variant: "pi",
1324
+ color: "neutral300",
1325
+ children: [
1326
+ /*#__PURE__*/ jsxRuntime.jsx(RelativeTime$1, {
1327
+ timestamp: new Date(release.createdAt)
1328
+ }),
1329
+ formatMessage({
1330
+ id: 'content-releases.header.actions.created.description',
1331
+ defaultMessage: '{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}'
1332
+ }, {
1333
+ createdBy: getCreatedByUser(),
1334
+ hasCreatedByUser
1335
+ })
1336
+ ]
1337
+ })
1338
+ ]
1339
+ })
1340
+ ]
1341
+ }),
1342
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
1343
+ size: "S",
1344
+ variant: "tertiary",
1345
+ onClick: handleRefresh,
1346
+ children: formatMessage({
1347
+ id: 'content-releases.header.actions.refresh',
1348
+ defaultMessage: 'Refresh'
1349
+ })
1350
+ }),
1351
+ canPublish ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
1352
+ size: "S",
1353
+ variant: "default",
1354
+ onClick: handlePublishRelease(release.id.toString()),
1355
+ loading: isPublishing,
1356
+ disabled: release.actions.meta.count === 0,
1357
+ children: formatMessage({
1358
+ id: 'content-releases.header.actions.publish',
1359
+ defaultMessage: 'Publish'
1360
+ })
1361
+ }) : null
1362
+ ]
1363
+ })
1364
+ }),
1365
+ children
1366
+ ]
1367
+ });
1368
+ };
1369
+ const SimpleMenuButton = styledComponents.styled(designSystem.SimpleMenu)`
1370
+ & > span {
1371
+ display: flex;
1372
+ }
1373
+ `;
1374
+ /* -------------------------------------------------------------------------------------------------
1375
+ * ReleaseDetailsBody
1376
+ * -----------------------------------------------------------------------------------------------*/ const GROUP_BY_OPTIONS = [
1377
+ 'contentType',
1378
+ 'locale',
1379
+ 'action'
1380
+ ];
1381
+ const GROUP_BY_OPTIONS_NO_LOCALE = [
1382
+ 'contentType',
1383
+ 'action'
1384
+ ];
1385
+ const getGroupByOptionLabel = (value)=>{
1386
+ if (value === 'locale') {
1387
+ return {
1388
+ id: 'content-releases.pages.ReleaseDetails.groupBy.option.locales',
1389
+ defaultMessage: 'Locales'
1390
+ };
1391
+ }
1392
+ if (value === 'action') {
1393
+ return {
1394
+ id: 'content-releases.pages.ReleaseDetails.groupBy.option.actions',
1395
+ defaultMessage: 'Actions'
1396
+ };
1397
+ }
1398
+ return {
1399
+ id: 'content-releases.pages.ReleaseDetails.groupBy.option.content-type',
1400
+ defaultMessage: 'Content-Types'
1401
+ };
1402
+ };
1403
+ const ReleaseDetailsBody = ({ releaseId })=>{
1404
+ const { formatMessage } = reactIntl.useIntl();
1405
+ const [{ query }, setQuery] = strapiAdmin$1.useQueryParams();
1406
+ const { toggleNotification } = strapiAdmin$1.useNotification();
1407
+ const { formatAPIError } = strapiAdmin$1.useAPIErrorHandler();
1408
+ const { data: releaseData, isLoading: isReleaseLoading, error: releaseError } = index.useGetReleaseQuery({
1409
+ id: releaseId
1410
+ });
1411
+ const { allowedActions: { canUpdate } } = strapiAdmin$1.useRBAC(index.PERMISSIONS);
1412
+ const runHookWaterfall = strapiAdmin$1.useStrapiApp('ReleaseDetailsPage', (state)=>state.runHookWaterfall);
1413
+ // TODO: Migrated displayedHeader to v5
1414
+ const { displayedHeaders, hasI18nEnabled } = runHookWaterfall('ContentReleases/pages/ReleaseDetails/add-locale-in-releases', {
1415
+ displayedHeaders: [
1416
+ {
1417
+ label: {
1418
+ id: 'content-releases.page.ReleaseDetails.table.header.label.name',
1419
+ defaultMessage: 'name'
1420
+ },
1421
+ name: 'name'
1422
+ }
1423
+ ],
1424
+ hasI18nEnabled: false
1425
+ });
1426
+ const release = releaseData?.data;
1427
+ const selectedGroupBy = query?.groupBy || 'contentType';
1428
+ const { isLoading, isFetching, isError, data, error: releaseActionsError } = index.useGetReleaseActionsQuery({
1429
+ ...query,
1430
+ releaseId
1431
+ });
1432
+ const [updateReleaseAction] = index.useUpdateReleaseActionMutation();
1433
+ const handleChangeType = async (e, actionId, actionPath)=>{
1434
+ const response = await updateReleaseAction({
1435
+ params: {
1436
+ releaseId,
1437
+ actionId
1438
+ },
1439
+ body: {
1440
+ type: e.target.value
1441
+ },
1442
+ query,
1443
+ actionPath
1444
+ });
1445
+ if ('error' in response) {
1446
+ if (strapiAdmin$1.isFetchError(response.error)) {
1447
+ // When the response returns an object with 'error', handle fetch error
1448
+ toggleNotification({
1449
+ type: 'danger',
1450
+ message: formatAPIError(response.error)
1451
+ });
1452
+ } else {
1453
+ // Otherwise, the response returns an object with 'error', handle a generic error
1454
+ toggleNotification({
1455
+ type: 'danger',
1456
+ message: formatMessage({
1457
+ id: 'notification.error',
1458
+ defaultMessage: 'An error occurred'
1459
+ })
1460
+ });
1461
+ }
1462
+ }
1463
+ };
1464
+ if (isLoading || isReleaseLoading) {
1465
+ return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Page.Loading, {});
1466
+ }
1467
+ const releaseActions = data?.data;
1468
+ const releaseMeta = data?.meta;
1469
+ const contentTypes = releaseMeta?.contentTypes || {};
1470
+ releaseMeta?.components || {};
1471
+ if (isBaseQueryError(releaseError) || !release) {
1472
+ const errorsArray = [];
1473
+ if (releaseError && 'code' in releaseError) {
1474
+ errorsArray.push({
1475
+ code: releaseError.code
1476
+ });
1477
+ }
1478
+ if (releaseActionsError && 'code' in releaseActionsError) {
1479
+ errorsArray.push({
1480
+ code: releaseActionsError.code
1481
+ });
1482
+ }
1483
+ return /*#__PURE__*/ jsxRuntime.jsx(reactRouterDom.Navigate, {
1484
+ to: "..",
1485
+ state: {
1486
+ errors: errorsArray
1487
+ }
1488
+ });
1489
+ }
1490
+ if (isError || !releaseActions) {
1491
+ return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Page.Error, {});
1492
+ }
1493
+ if (Object.keys(releaseActions).length === 0) {
1494
+ return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Layouts.Content, {
1495
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.EmptyStateLayout, {
1496
+ action: /*#__PURE__*/ jsxRuntime.jsx(designSystem.LinkButton, {
1497
+ tag: reactRouterDom.Link,
1498
+ to: {
1499
+ pathname: '/content-manager'
1500
+ },
1501
+ style: {
1502
+ textDecoration: 'none'
1503
+ },
1504
+ variant: "secondary",
1505
+ children: formatMessage({
1506
+ id: 'content-releases.page.Details.button.openContentManager',
1507
+ defaultMessage: 'Open the Content Manager'
1508
+ })
1509
+ }),
1510
+ icon: /*#__PURE__*/ jsxRuntime.jsx(symbols.EmptyDocuments, {
1511
+ width: "16rem"
1512
+ }),
1513
+ content: formatMessage({
1514
+ id: 'content-releases.pages.Details.tab.emptyEntries',
1515
+ defaultMessage: 'This release is empty. Open the Content Manager, select an entry and add it to the release.'
1516
+ })
1517
+ })
1518
+ });
1519
+ }
1520
+ const groupByLabel = formatMessage({
1521
+ id: 'content-releases.pages.ReleaseDetails.groupBy.aria-label',
1522
+ defaultMessage: 'Group by'
1523
+ });
1524
+ const headers = [
1525
+ ...displayedHeaders,
1526
+ {
1527
+ label: {
1528
+ id: 'content-releases.page.ReleaseDetails.table.header.label.content-type',
1529
+ defaultMessage: 'content-type'
1530
+ },
1531
+ name: 'content-type'
1532
+ },
1533
+ {
1534
+ label: {
1535
+ id: 'content-releases.page.ReleaseDetails.table.header.label.action',
1536
+ defaultMessage: 'action'
1537
+ },
1538
+ name: 'action'
1539
+ },
1540
+ ...!release.releasedAt ? [
1541
+ {
1542
+ label: {
1543
+ id: 'content-releases.page.ReleaseDetails.table.header.label.status',
1544
+ defaultMessage: 'status'
1545
+ },
1546
+ name: 'status'
1547
+ }
1548
+ ] : []
1549
+ ];
1550
+ const options = hasI18nEnabled ? GROUP_BY_OPTIONS : GROUP_BY_OPTIONS_NO_LOCALE;
1551
+ return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Layouts.Content, {
1552
+ children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
1553
+ gap: 8,
1554
+ direction: "column",
1555
+ alignItems: "stretch",
1556
+ children: [
1557
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
1558
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelect, {
1559
+ placeholder: groupByLabel,
1560
+ "aria-label": groupByLabel,
1561
+ customizeContent: (value)=>formatMessage({
1562
+ id: `content-releases.pages.ReleaseDetails.groupBy.label`,
1563
+ defaultMessage: `Group by {groupBy}`
1564
+ }, {
1565
+ groupBy: value
1566
+ }),
1567
+ value: formatMessage(getGroupByOptionLabel(selectedGroupBy)),
1568
+ onChange: (value)=>setQuery({
1569
+ groupBy: value
1570
+ }),
1571
+ children: options.map((option)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.SingleSelectOption, {
1572
+ value: option,
1573
+ children: formatMessage(getGroupByOptionLabel(option))
1574
+ }, option))
1575
+ })
1576
+ }),
1577
+ Object.keys(releaseActions).map((key)=>/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
1578
+ gap: 4,
1579
+ direction: "column",
1580
+ alignItems: "stretch",
1581
+ children: [
1582
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
1583
+ role: "separator",
1584
+ "aria-label": key,
1585
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Badge, {
1586
+ children: key
1587
+ })
1588
+ }),
1589
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Table.Root, {
1590
+ rows: releaseActions[key].map((item)=>({
1591
+ ...item,
1592
+ id: Number(item.entry.id)
1593
+ })),
1594
+ headers: headers,
1595
+ isLoading: isLoading || isFetching,
1596
+ children: /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin$1.Table.Content, {
1597
+ children: [
1598
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Table.Head, {
1599
+ children: headers.map(({ label, name })=>/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Table.HeaderCell, {
1600
+ label: formatMessage(label),
1601
+ name: name
1602
+ }, name))
1603
+ }),
1604
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Table.Loading, {}),
1605
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Table.Body, {
1606
+ children: releaseActions[key].map(({ id, contentType, locale, type, entry, status }, actionIndex)=>/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Tr, {
1607
+ children: [
1608
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Td, {
1609
+ width: "25%",
1610
+ maxWidth: "200px",
1611
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1612
+ ellipsis: true,
1613
+ children: `${contentType.mainFieldValue || entry.id}`
1614
+ })
1615
+ }),
1616
+ hasI18nEnabled && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Td, {
1617
+ width: "10%",
1618
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1619
+ children: `${locale?.name ? locale.name : '-'}`
1620
+ })
1621
+ }),
1622
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Td, {
1623
+ width: "10%",
1624
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1625
+ children: contentType.displayName || ''
1626
+ })
1627
+ }),
1628
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Td, {
1629
+ width: "20%",
1630
+ children: release.releasedAt ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1631
+ children: formatMessage({
1632
+ id: 'content-releases.page.ReleaseDetails.table.action-published',
1633
+ defaultMessage: 'This entry was <b>{isPublish, select, true {published} other {unpublished}}</b>.'
1634
+ }, {
1635
+ isPublish: type === 'publish',
1636
+ b: (children)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
1637
+ fontWeight: "bold",
1638
+ children: children
1639
+ })
1640
+ })
1641
+ }) : /*#__PURE__*/ jsxRuntime.jsx(index.ReleaseActionOptions, {
1642
+ selected: type,
1643
+ handleChange: (e)=>handleChangeType(e, id, [
1644
+ key,
1645
+ actionIndex
1646
+ ]),
1647
+ name: `release-action-${id}-type`,
1648
+ disabled: !canUpdate
1649
+ })
1650
+ }),
1651
+ !release.releasedAt && /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
1652
+ children: [
1653
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Td, {
1654
+ width: "20%",
1655
+ minWidth: "200px",
1656
+ children: /*#__PURE__*/ jsxRuntime.jsx(EntryValidationPopover, {
1657
+ action: type,
1658
+ schema: contentTypes?.[contentType.uid],
1659
+ entry: entry,
1660
+ status: status
1661
+ })
1662
+ }),
1663
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Td, {
1664
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
1665
+ justifyContent: "flex-end",
1666
+ children: /*#__PURE__*/ jsxRuntime.jsxs(index.ReleaseActionMenu.Root, {
1667
+ children: [
1668
+ /*#__PURE__*/ jsxRuntime.jsx(index.ReleaseActionMenu.ReleaseActionEntryLinkItem, {
1669
+ contentTypeUid: contentType.uid,
1670
+ documentId: entry.documentId,
1671
+ locale: locale?.code
1672
+ }),
1673
+ /*#__PURE__*/ jsxRuntime.jsx(index.ReleaseActionMenu.DeleteReleaseActionItem, {
1674
+ releaseId: release.id,
1675
+ actionId: id
1676
+ })
1677
+ ]
1678
+ })
1679
+ })
1680
+ })
1681
+ ]
1682
+ })
1683
+ ]
1684
+ }, id))
1685
+ })
1686
+ ]
1687
+ })
1688
+ })
1689
+ ]
1690
+ }, `releases-group-${key}`)),
1691
+ /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin$1.Pagination.Root, {
1692
+ ...releaseMeta?.pagination,
1693
+ defaultPageSize: releaseMeta?.pagination?.pageSize,
1694
+ children: [
1695
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Pagination.PageSize, {}),
1696
+ /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Pagination.Links, {})
1697
+ ]
1698
+ })
1699
+ ]
1700
+ })
1701
+ });
1702
+ };
1703
+ /* -------------------------------------------------------------------------------------------------
1704
+ * ReleaseDetailsPage
1705
+ * -----------------------------------------------------------------------------------------------*/ const ReleaseDetailsPage = ()=>{
1706
+ const { formatMessage } = reactIntl.useIntl();
1707
+ const { releaseId } = reactRouterDom.useParams();
1708
+ const { toggleNotification } = strapiAdmin$1.useNotification();
1709
+ const { formatAPIError } = strapiAdmin$1.useAPIErrorHandler();
1710
+ const navigate = reactRouterDom.useNavigate();
1711
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
1712
+ const [showWarningSubmit, setWarningSubmit] = React__namespace.useState(false);
1713
+ const { isLoading: isLoadingDetails, data, isSuccess: isSuccessDetails } = index.useGetReleaseQuery({
1714
+ id: releaseId
1715
+ }, {
1716
+ skip: !releaseId
1717
+ });
1718
+ const { data: dataTimezone, isLoading: isLoadingTimezone } = index.useGetReleaseSettingsQuery();
1719
+ const [updateRelease, { isLoading: isSubmittingForm }] = index.useUpdateReleaseMutation();
1720
+ const [deleteRelease] = index.useDeleteReleaseMutation();
1721
+ const toggleEditReleaseModal = ()=>{
1722
+ setReleaseModalShown((prev)=>!prev);
1723
+ };
1724
+ const getTimezoneValue = ()=>{
1725
+ if (releaseData?.timezone) {
1726
+ return releaseData.timezone;
1727
+ } else {
1728
+ if (dataTimezone?.data.defaultTimezone) {
1729
+ return dataTimezone.data.defaultTimezone;
1730
+ }
1731
+ return null;
1732
+ }
1733
+ };
1734
+ const toggleWarningSubmit = ()=>setWarningSubmit((prevState)=>!prevState);
1735
+ if (isLoadingDetails || isLoadingTimezone) {
1736
+ return /*#__PURE__*/ jsxRuntime.jsx(ReleaseDetailsLayout, {
1737
+ toggleEditReleaseModal: toggleEditReleaseModal,
1738
+ toggleWarningSubmit: toggleWarningSubmit,
1739
+ children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Page.Loading, {})
1740
+ });
1741
+ }
1742
+ if (!releaseId) {
1743
+ return /*#__PURE__*/ jsxRuntime.jsx(reactRouterDom.Navigate, {
1744
+ to: ".."
1745
+ });
1746
+ }
1747
+ const releaseData = isSuccessDetails && data?.data || null;
1748
+ const title = releaseData?.name || '';
1749
+ const timezone = getTimezoneValue();
1750
+ const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
1751
+ // Just get the date and time to display without considering updated timezone time
1752
+ const date = scheduledAt ? format(scheduledAt, 'yyyy-MM-dd') : undefined;
1753
+ const time = scheduledAt ? format(scheduledAt, 'HH:mm') : '';
1754
+ const handleEditRelease = async (values)=>{
1755
+ const response = await updateRelease({
1756
+ id: releaseId,
1757
+ name: values.name,
1758
+ scheduledAt: values.scheduledAt,
1759
+ timezone: values.timezone
1760
+ });
1761
+ if ('data' in response) {
1762
+ // When the response returns an object with 'data', handle success
1763
+ toggleNotification({
1764
+ type: 'success',
1765
+ message: formatMessage({
1766
+ id: 'content-releases.modal.release-updated-notification-success',
1767
+ defaultMessage: 'Release updated.'
1768
+ })
1769
+ });
1770
+ toggleEditReleaseModal();
1771
+ } else if (strapiAdmin$1.isFetchError(response.error)) {
1772
+ // When the response returns an object with 'error', handle fetch error
1773
+ toggleNotification({
1774
+ type: 'danger',
1775
+ message: formatAPIError(response.error)
1776
+ });
1777
+ } else {
1778
+ // Otherwise, the response returns an object with 'error', handle a generic error
1779
+ toggleNotification({
1780
+ type: 'danger',
1781
+ message: formatMessage({
1782
+ id: 'notification.error',
1783
+ defaultMessage: 'An error occurred'
1784
+ })
1785
+ });
1786
+ }
1787
+ };
1788
+ const handleDeleteRelease = async ()=>{
1789
+ const response = await deleteRelease({
1790
+ id: releaseId
1791
+ });
1792
+ if ('data' in response) {
1793
+ navigate('..');
1794
+ } else if (strapiAdmin$1.isFetchError(response.error)) {
1795
+ // When the response returns an object with 'error', handle fetch error
1796
+ toggleNotification({
1797
+ type: 'danger',
1798
+ message: formatAPIError(response.error)
1799
+ });
1800
+ } else {
1801
+ // Otherwise, the response returns an object with 'error', handle a generic error
1802
+ toggleNotification({
1803
+ type: 'danger',
1804
+ message: formatMessage({
1805
+ id: 'notification.error',
1806
+ defaultMessage: 'An error occurred'
1807
+ })
1808
+ });
1809
+ }
1810
+ };
1811
+ return /*#__PURE__*/ jsxRuntime.jsxs(ReleaseDetailsLayout, {
1812
+ toggleEditReleaseModal: toggleEditReleaseModal,
1813
+ toggleWarningSubmit: toggleWarningSubmit,
1814
+ children: [
1815
+ /*#__PURE__*/ jsxRuntime.jsx(ReleaseDetailsBody, {
1816
+ releaseId: releaseId
1817
+ }),
1818
+ /*#__PURE__*/ jsxRuntime.jsx(ReleaseModal, {
1819
+ open: releaseModalShown,
1820
+ handleClose: toggleEditReleaseModal,
1821
+ handleSubmit: handleEditRelease,
1822
+ isLoading: isLoadingDetails || isSubmittingForm,
1823
+ initialValues: {
1824
+ name: title || '',
1825
+ scheduledAt,
1826
+ date,
1827
+ time,
1828
+ isScheduled: Boolean(scheduledAt),
1829
+ timezone
1830
+ }
1831
+ }),
1832
+ /*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Root, {
1833
+ open: showWarningSubmit,
1834
+ onOpenChange: toggleWarningSubmit,
1835
+ children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.ConfirmDialog, {
1836
+ onConfirm: handleDeleteRelease,
1837
+ children: formatMessage({
1838
+ id: 'content-releases.dialog.confirmation-message',
1839
+ defaultMessage: 'Are you sure you want to delete this release?'
1840
+ })
1841
+ })
1842
+ })
1843
+ ]
1844
+ });
1845
+ };
1846
+
1847
+ const App = ()=>{
1848
+ return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin$1.Page.Protect, {
1849
+ permissions: index.PERMISSIONS.main,
1850
+ children: /*#__PURE__*/ jsxRuntime.jsxs(reactRouterDom.Routes, {
1851
+ children: [
1852
+ /*#__PURE__*/ jsxRuntime.jsx(reactRouterDom.Route, {
1853
+ index: true,
1854
+ element: /*#__PURE__*/ jsxRuntime.jsx(ReleasesPage, {})
1855
+ }),
1856
+ /*#__PURE__*/ jsxRuntime.jsx(reactRouterDom.Route, {
1857
+ path: ':releaseId',
1858
+ element: /*#__PURE__*/ jsxRuntime.jsx(ReleaseDetailsPage, {})
1859
+ })
1860
+ ]
1861
+ })
1862
+ });
1863
+ };
1864
+
1865
+ exports.App = App;
1866
+ //# sourceMappingURL=App-B9yCdSLJ.js.map