@strapi/content-releases 0.0.0-experimental.check-license → 0.0.0-experimental.d4cb32ce579e12a4436d68036f2327132fba1309

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 (34) hide show
  1. package/dist/_chunks/App-OK4Xac-O.js +1315 -0
  2. package/dist/_chunks/App-OK4Xac-O.js.map +1 -0
  3. package/dist/_chunks/App-xAkiD42p.mjs +1292 -0
  4. package/dist/_chunks/App-xAkiD42p.mjs.map +1 -0
  5. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs +51 -0
  6. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +1 -0
  7. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js +51 -0
  8. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +1 -0
  9. package/dist/_chunks/en-r0otWaln.js +77 -0
  10. package/dist/_chunks/en-r0otWaln.js.map +1 -0
  11. package/dist/_chunks/en-veqvqeEr.mjs +77 -0
  12. package/dist/_chunks/en-veqvqeEr.mjs.map +1 -0
  13. package/dist/_chunks/index-JvA2_26n.js +1015 -0
  14. package/dist/_chunks/index-JvA2_26n.js.map +1 -0
  15. package/dist/_chunks/index-exoiSU3V.mjs +994 -0
  16. package/dist/_chunks/index-exoiSU3V.mjs.map +1 -0
  17. package/dist/admin/index.js +17 -35
  18. package/dist/admin/index.js.map +1 -1
  19. package/dist/admin/index.mjs +17 -35
  20. package/dist/admin/index.mjs.map +1 -1
  21. package/dist/server/index.js +1332 -5
  22. package/dist/server/index.js.map +1 -1
  23. package/dist/server/index.mjs +1310 -5
  24. package/dist/server/index.mjs.map +1 -1
  25. package/package.json +35 -19
  26. package/dist/_chunks/en-21947221.js +0 -5
  27. package/dist/_chunks/en-21947221.js.map +0 -1
  28. package/dist/_chunks/en-92157b33.mjs +0 -5
  29. package/dist/_chunks/en-92157b33.mjs.map +0 -1
  30. package/dist/admin/src/index.d.ts +0 -15
  31. package/dist/admin/src/tests/index.test.d.ts +0 -0
  32. package/dist/server/src/__tests__/index.test.d.ts +0 -2
  33. package/dist/server/src/index.d.ts +0 -4
  34. package/dist/server/src/register.d.ts +0 -1
@@ -0,0 +1,1315 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const helperPlugin = require("@strapi/helper-plugin");
5
+ const reactRouterDom = require("react-router-dom");
6
+ const index = require("./index-JvA2_26n.js");
7
+ const React = require("react");
8
+ const strapiAdmin = require("@strapi/admin/strapi-admin");
9
+ const designSystem = require("@strapi/design-system");
10
+ const v2 = require("@strapi/design-system/v2");
11
+ const icons = require("@strapi/icons");
12
+ const format = require("date-fns/format");
13
+ const dateFnsTz = require("date-fns-tz");
14
+ const reactIntl = require("react-intl");
15
+ const styled = require("styled-components");
16
+ const dateFns = require("date-fns");
17
+ const formik = require("formik");
18
+ const yup = require("yup");
19
+ require("@reduxjs/toolkit/query");
20
+ require("axios");
21
+ require("@reduxjs/toolkit/query/react");
22
+ require("react-redux");
23
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
24
+ function _interopNamespace(e) {
25
+ if (e && e.__esModule)
26
+ return e;
27
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
28
+ if (e) {
29
+ for (const k in e) {
30
+ if (k !== "default") {
31
+ const d = Object.getOwnPropertyDescriptor(e, k);
32
+ Object.defineProperty(n, k, d.get ? d : {
33
+ enumerable: true,
34
+ get: () => e[k]
35
+ });
36
+ }
37
+ }
38
+ }
39
+ n.default = e;
40
+ return Object.freeze(n);
41
+ }
42
+ const React__namespace = /* @__PURE__ */ _interopNamespace(React);
43
+ const format__default = /* @__PURE__ */ _interopDefault(format);
44
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
45
+ const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
46
+ const RELEASE_SCHEMA = yup__namespace.object().shape({
47
+ name: yup__namespace.string().trim().required(),
48
+ scheduledAt: yup__namespace.string().nullable(),
49
+ isScheduled: yup__namespace.boolean().optional(),
50
+ time: yup__namespace.string().when("isScheduled", {
51
+ is: true,
52
+ then: yup__namespace.string().trim().required(),
53
+ otherwise: yup__namespace.string().nullable()
54
+ }),
55
+ timezone: yup__namespace.string().when("isScheduled", {
56
+ is: true,
57
+ then: yup__namespace.string().required().nullable(),
58
+ otherwise: yup__namespace.string().nullable()
59
+ }),
60
+ date: yup__namespace.string().when("isScheduled", {
61
+ is: true,
62
+ then: yup__namespace.string().required().nullable(),
63
+ otherwise: yup__namespace.string().nullable()
64
+ })
65
+ }).required().noUnknown();
66
+ const ReleaseModal = ({
67
+ handleClose,
68
+ handleSubmit,
69
+ initialValues,
70
+ isLoading = false
71
+ }) => {
72
+ const { formatMessage } = reactIntl.useIntl();
73
+ const { pathname } = reactRouterDom.useLocation();
74
+ const isCreatingRelease = pathname === `/plugins/${index.pluginId}`;
75
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
76
+ const { timezoneList, systemTimezone = { value: "UTC+00:00-Africa/Abidjan " } } = getTimezones(
77
+ initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : /* @__PURE__ */ new Date()
78
+ );
79
+ const getScheduledTimestamp = (values) => {
80
+ const { date, time, timezone } = values;
81
+ if (!date || !time || !timezone)
82
+ return null;
83
+ const formattedDate = dateFns.parse(time, "HH:mm", new Date(date));
84
+ const timezoneWithoutOffset = timezone.split("_")[1];
85
+ return dateFnsTz.zonedTimeToUtc(formattedDate, timezoneWithoutOffset);
86
+ };
87
+ const getTimezoneWithOffset = () => {
88
+ const currentTimezone = timezoneList.find(
89
+ (timezone) => timezone.value.split("_")[1] === initialValues.timezone
90
+ );
91
+ return currentTimezone?.value || systemTimezone.value;
92
+ };
93
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalLayout, { onClose: handleClose, labelledBy: "title", children: [
94
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "title", fontWeight: "bold", textColor: "neutral800", children: formatMessage(
95
+ {
96
+ id: "content-releases.modal.title",
97
+ defaultMessage: "{isCreatingRelease, select, true {New release} other {Edit release}}"
98
+ },
99
+ { isCreatingRelease }
100
+ ) }) }),
101
+ /* @__PURE__ */ jsxRuntime.jsx(
102
+ formik.Formik,
103
+ {
104
+ onSubmit: (values) => {
105
+ handleSubmit({
106
+ ...values,
107
+ timezone: values.timezone ? values.timezone.split("_")[1] : null,
108
+ scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
109
+ });
110
+ },
111
+ initialValues: {
112
+ ...initialValues,
113
+ timezone: initialValues.timezone ? getTimezoneWithOffset() : systemTimezone.value
114
+ },
115
+ validationSchema: RELEASE_SCHEMA,
116
+ validateOnChange: false,
117
+ children: ({ values, errors, handleChange, setFieldValue }) => /* @__PURE__ */ jsxRuntime.jsxs(formik.Form, { children: [
118
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalBody, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
119
+ /* @__PURE__ */ jsxRuntime.jsx(
120
+ designSystem.TextInput,
121
+ {
122
+ label: formatMessage({
123
+ id: "content-releases.modal.form.input.label.release-name",
124
+ defaultMessage: "Name"
125
+ }),
126
+ name: "name",
127
+ value: values.name,
128
+ error: errors.name,
129
+ onChange: handleChange,
130
+ required: true
131
+ }
132
+ ),
133
+ IsSchedulingEnabled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
134
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "max-content", children: /* @__PURE__ */ jsxRuntime.jsx(
135
+ designSystem.Checkbox,
136
+ {
137
+ name: "isScheduled",
138
+ value: values.isScheduled,
139
+ onChange: (event) => {
140
+ setFieldValue("isScheduled", event.target.checked);
141
+ if (!event.target.checked) {
142
+ setFieldValue("date", null);
143
+ setFieldValue("time", "");
144
+ setFieldValue("timezone", null);
145
+ } else {
146
+ setFieldValue("date", initialValues.date);
147
+ setFieldValue("time", initialValues.time);
148
+ setFieldValue(
149
+ "timezone",
150
+ initialValues.timezone ?? systemTimezone?.value
151
+ );
152
+ }
153
+ },
154
+ children: /* @__PURE__ */ jsxRuntime.jsx(
155
+ designSystem.Typography,
156
+ {
157
+ textColor: values.isScheduled ? "primary600" : "neutral800",
158
+ fontWeight: values.isScheduled ? "semiBold" : "regular",
159
+ children: formatMessage({
160
+ id: "modal.form.input.label.schedule-release",
161
+ defaultMessage: "Schedule release"
162
+ })
163
+ }
164
+ )
165
+ }
166
+ ) }),
167
+ values.isScheduled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
168
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "start", children: [
169
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
170
+ designSystem.DatePicker,
171
+ {
172
+ label: formatMessage({
173
+ id: "content-releases.modal.form.input.label.date",
174
+ defaultMessage: "Date"
175
+ }),
176
+ name: "date",
177
+ error: errors.date,
178
+ onChange: (date) => {
179
+ const isoFormatDate = date ? dateFns.formatISO(date, { representation: "date" }) : null;
180
+ setFieldValue("date", isoFormatDate);
181
+ },
182
+ clearLabel: formatMessage({
183
+ id: "content-releases.modal.form.input.clearLabel",
184
+ defaultMessage: "Clear"
185
+ }),
186
+ onClear: () => {
187
+ setFieldValue("date", null);
188
+ },
189
+ selectedDate: values.date || void 0,
190
+ required: true
191
+ }
192
+ ) }),
193
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
194
+ designSystem.TimePicker,
195
+ {
196
+ label: formatMessage({
197
+ id: "content-releases.modal.form.input.label.time",
198
+ defaultMessage: "Time"
199
+ }),
200
+ name: "time",
201
+ error: errors.time,
202
+ onChange: (time) => {
203
+ setFieldValue("time", time);
204
+ },
205
+ clearLabel: formatMessage({
206
+ id: "content-releases.modal.form.input.clearLabel",
207
+ defaultMessage: "Clear"
208
+ }),
209
+ onClear: () => {
210
+ setFieldValue("time", "");
211
+ },
212
+ value: values.time || void 0,
213
+ required: true
214
+ }
215
+ ) })
216
+ ] }),
217
+ /* @__PURE__ */ jsxRuntime.jsx(TimezoneComponent, { timezoneOptions: timezoneList })
218
+ ] })
219
+ ] })
220
+ ] }) }),
221
+ /* @__PURE__ */ jsxRuntime.jsx(
222
+ designSystem.ModalFooter,
223
+ {
224
+ startActions: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleClose, variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }),
225
+ endActions: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
226
+ {
227
+ id: "content-releases.modal.form.button.submit",
228
+ defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
229
+ },
230
+ { isCreatingRelease }
231
+ ) })
232
+ }
233
+ )
234
+ ] })
235
+ }
236
+ )
237
+ ] });
238
+ };
239
+ const getTimezones = (selectedDate) => {
240
+ const timezoneList = Intl.supportedValuesOf("timeZone").map((timezone) => {
241
+ const utcOffset = index.getTimezoneOffset(timezone, selectedDate);
242
+ return { offset: utcOffset, value: `${utcOffset}_${timezone}` };
243
+ });
244
+ const systemTimezone = timezoneList.find(
245
+ (timezone) => timezone.value.split("_")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
246
+ );
247
+ return { timezoneList, systemTimezone };
248
+ };
249
+ const TimezoneComponent = ({ timezoneOptions }) => {
250
+ const { values, errors, setFieldValue } = formik.useFormikContext();
251
+ const { formatMessage } = reactIntl.useIntl();
252
+ const [timezoneList, setTimezoneList] = React__namespace.useState(timezoneOptions);
253
+ React__namespace.useEffect(() => {
254
+ if (values.date) {
255
+ const { timezoneList: timezoneList2 } = getTimezones(new Date(values.date));
256
+ setTimezoneList(timezoneList2);
257
+ const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("_")[1] === values.timezone.split("_")[1]);
258
+ if (updatedTimezone) {
259
+ setFieldValue("timezone", updatedTimezone.value);
260
+ }
261
+ }
262
+ }, [setFieldValue, values.date, values.timezone]);
263
+ return /* @__PURE__ */ jsxRuntime.jsx(
264
+ designSystem.Combobox,
265
+ {
266
+ label: formatMessage({
267
+ id: "content-releases.modal.form.input.label.timezone",
268
+ defaultMessage: "Timezone"
269
+ }),
270
+ name: "timezone",
271
+ value: values.timezone || void 0,
272
+ textValue: values.timezone ? values.timezone.replace("_", " ") : void 0,
273
+ onChange: (timezone) => {
274
+ setFieldValue("timezone", timezone);
275
+ },
276
+ onClear: () => {
277
+ setFieldValue("timezone", "");
278
+ },
279
+ error: errors.timezone,
280
+ required: true,
281
+ children: timezoneList.map((timezone) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: timezone.value, children: timezone.value.replace("_", " ") }, timezone.value))
282
+ }
283
+ );
284
+ };
285
+ const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
286
+ align-self: stretch;
287
+ border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
288
+ border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
289
+ border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
290
+ `;
291
+ const StyledMenuItem = styled__default.default(v2.Menu.Item)`
292
+ svg path {
293
+ fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
294
+ }
295
+ span {
296
+ color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
297
+ }
298
+ `;
299
+ const PencilIcon = styled__default.default(icons.Pencil)`
300
+ width: ${({ theme }) => theme.spaces[3]};
301
+ height: ${({ theme }) => theme.spaces[3]};
302
+ path {
303
+ fill: ${({ theme }) => theme.colors.neutral600};
304
+ }
305
+ `;
306
+ const TrashIcon = styled__default.default(icons.Trash)`
307
+ width: ${({ theme }) => theme.spaces[3]};
308
+ height: ${({ theme }) => theme.spaces[3]};
309
+ path {
310
+ fill: ${({ theme }) => theme.colors.danger600};
311
+ }
312
+ `;
313
+ const TypographyMaxWidth = styled__default.default(designSystem.Typography)`
314
+ max-width: 300px;
315
+ `;
316
+ const EntryValidationText = ({ action, schema, components, entry }) => {
317
+ const { formatMessage } = reactIntl.useIntl();
318
+ const { validate } = strapiAdmin.unstable_useDocument();
319
+ const { errors } = validate(entry, {
320
+ contentType: schema,
321
+ components,
322
+ isCreatingEntry: false
323
+ });
324
+ if (Object.keys(errors).length > 0) {
325
+ const validationErrorsMessages = Object.entries(errors).map(
326
+ ([key, value]) => formatMessage(
327
+ { id: `${value.id}.withField`, defaultMessage: value.defaultMessage },
328
+ { field: key }
329
+ )
330
+ ).join(" ");
331
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
332
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "danger600", as: icons.CrossCircle }),
333
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsxRuntime.jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
334
+ ] });
335
+ }
336
+ if (action == "publish") {
337
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
338
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
339
+ entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
340
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
341
+ defaultMessage: "Already published"
342
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
343
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
344
+ defaultMessage: "Ready to publish"
345
+ }) })
346
+ ] });
347
+ }
348
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
349
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
350
+ !entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
351
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
352
+ defaultMessage: "Already unpublished"
353
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
354
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
355
+ defaultMessage: "Ready to unpublish"
356
+ }) })
357
+ ] });
358
+ };
359
+ const ReleaseDetailsLayout = ({
360
+ toggleEditReleaseModal,
361
+ toggleWarningSubmit,
362
+ children
363
+ }) => {
364
+ const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
365
+ const { releaseId } = reactRouterDom.useParams();
366
+ const {
367
+ data,
368
+ isLoading: isLoadingDetails,
369
+ isError,
370
+ error
371
+ } = index.useGetReleaseQuery({ id: releaseId });
372
+ const [publishRelease, { isLoading: isPublishing }] = index.usePublishReleaseMutation();
373
+ const toggleNotification = helperPlugin.useNotification();
374
+ const { formatAPIError } = helperPlugin.useAPIErrorHandler();
375
+ const {
376
+ allowedActions: { canUpdate, canDelete }
377
+ } = helperPlugin.useRBAC(index.PERMISSIONS);
378
+ const dispatch = index.useTypedDispatch();
379
+ const { trackUsage } = helperPlugin.useTracking();
380
+ const release = data?.data;
381
+ const handlePublishRelease = async () => {
382
+ const response = await publishRelease({ id: releaseId });
383
+ if ("data" in response) {
384
+ toggleNotification({
385
+ type: "success",
386
+ message: formatMessage({
387
+ id: "content-releases.pages.ReleaseDetails.publish-notification-success",
388
+ defaultMessage: "Release was published successfully."
389
+ })
390
+ });
391
+ const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
392
+ trackUsage("didPublishRelease", {
393
+ totalEntries: totalEntries2,
394
+ totalPublishedEntries,
395
+ totalUnpublishedEntries
396
+ });
397
+ } else if (index.isAxiosError(response.error)) {
398
+ toggleNotification({
399
+ type: "warning",
400
+ message: formatAPIError(response.error)
401
+ });
402
+ } else {
403
+ toggleNotification({
404
+ type: "warning",
405
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
406
+ });
407
+ }
408
+ };
409
+ const handleRefresh = () => {
410
+ dispatch(index.releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
411
+ };
412
+ const getCreatedByUser = () => {
413
+ if (!release?.createdBy) {
414
+ return null;
415
+ }
416
+ if (release.createdBy.username) {
417
+ return release.createdBy.username;
418
+ }
419
+ if (release.createdBy.firstname) {
420
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
421
+ }
422
+ return release.createdBy.email;
423
+ };
424
+ if (isLoadingDetails) {
425
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
426
+ }
427
+ if (isError || !release) {
428
+ return /* @__PURE__ */ jsxRuntime.jsx(
429
+ reactRouterDom.Redirect,
430
+ {
431
+ to: {
432
+ pathname: "/plugins/content-releases",
433
+ state: {
434
+ errors: [
435
+ {
436
+ code: error?.code
437
+ }
438
+ ]
439
+ }
440
+ }
441
+ }
442
+ );
443
+ }
444
+ const totalEntries = release.actions.meta.count || 0;
445
+ const hasCreatedByUser = Boolean(getCreatedByUser());
446
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
447
+ const isScheduled = release.scheduledAt && release.timezone;
448
+ const numberOfEntriesText = formatMessage(
449
+ {
450
+ id: "content-releases.pages.Details.header-subtitle",
451
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
452
+ },
453
+ { number: totalEntries }
454
+ );
455
+ const scheduledText = isScheduled ? formatMessage(
456
+ {
457
+ id: "content-releases.pages.ReleaseDetails.header-subtitle.scheduled",
458
+ defaultMessage: "Scheduled for {date} at {time} ({offset})"
459
+ },
460
+ {
461
+ date: formatDate(new Date(release.scheduledAt), {
462
+ weekday: "long",
463
+ day: "numeric",
464
+ month: "long",
465
+ year: "numeric",
466
+ timeZone: release.timezone
467
+ }),
468
+ time: formatTime(new Date(release.scheduledAt), {
469
+ timeZone: release.timezone,
470
+ hourCycle: "h23"
471
+ }),
472
+ offset: index.getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
473
+ }
474
+ ) : "";
475
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoadingDetails, children: [
476
+ /* @__PURE__ */ jsxRuntime.jsx(
477
+ designSystem.HeaderLayout,
478
+ {
479
+ title: release.name,
480
+ subtitle: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? ` - ${scheduledText}` : ""),
481
+ navigationAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
482
+ id: "global.back",
483
+ defaultMessage: "Back"
484
+ }) }),
485
+ primaryAction: !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
486
+ /* @__PURE__ */ jsxRuntime.jsxs(v2.Menu.Root, { children: [
487
+ /* @__PURE__ */ jsxRuntime.jsx(
488
+ v2.Menu.Trigger,
489
+ {
490
+ as: designSystem.IconButton,
491
+ paddingLeft: 2,
492
+ paddingRight: 2,
493
+ "aria-label": formatMessage({
494
+ id: "content-releases.header.actions.open-release-actions",
495
+ defaultMessage: "Release edit and delete menu"
496
+ }),
497
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.More, {}),
498
+ variant: "tertiary"
499
+ }
500
+ ),
501
+ /* @__PURE__ */ jsxRuntime.jsxs(v2.Menu.Content, { top: 1, popoverPlacement: "bottom-end", children: [
502
+ /* @__PURE__ */ jsxRuntime.jsxs(
503
+ designSystem.Flex,
504
+ {
505
+ alignItems: "center",
506
+ justifyContent: "center",
507
+ direction: "column",
508
+ padding: 1,
509
+ width: "100%",
510
+ children: [
511
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(
512
+ designSystem.Flex,
513
+ {
514
+ paddingTop: 2,
515
+ paddingBottom: 2,
516
+ alignItems: "center",
517
+ gap: 2,
518
+ hasRadius: true,
519
+ width: "100%",
520
+ children: [
521
+ /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
522
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
523
+ id: "content-releases.header.actions.edit",
524
+ defaultMessage: "Edit"
525
+ }) })
526
+ ]
527
+ }
528
+ ) }),
529
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxRuntime.jsxs(
530
+ designSystem.Flex,
531
+ {
532
+ paddingTop: 2,
533
+ paddingBottom: 2,
534
+ alignItems: "center",
535
+ gap: 2,
536
+ hasRadius: true,
537
+ width: "100%",
538
+ children: [
539
+ /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
540
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
541
+ id: "content-releases.header.actions.delete",
542
+ defaultMessage: "Delete"
543
+ }) })
544
+ ]
545
+ }
546
+ ) })
547
+ ]
548
+ }
549
+ ),
550
+ /* @__PURE__ */ jsxRuntime.jsxs(
551
+ ReleaseInfoWrapper,
552
+ {
553
+ direction: "column",
554
+ justifyContent: "center",
555
+ alignItems: "flex-start",
556
+ gap: 1,
557
+ padding: 5,
558
+ children: [
559
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
560
+ id: "content-releases.header.actions.created",
561
+ defaultMessage: "Created"
562
+ }) }),
563
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", color: "neutral300", children: [
564
+ /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(release.createdAt) }),
565
+ formatMessage(
566
+ {
567
+ id: "content-releases.header.actions.created.description",
568
+ defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
569
+ },
570
+ { createdBy: getCreatedByUser(), hasCreatedByUser }
571
+ )
572
+ ] })
573
+ ]
574
+ }
575
+ )
576
+ ] })
577
+ ] }),
578
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "S", variant: "tertiary", onClick: handleRefresh, children: formatMessage({
579
+ id: "content-releases.header.actions.refresh",
580
+ defaultMessage: "Refresh"
581
+ }) }),
582
+ /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.publish, children: /* @__PURE__ */ jsxRuntime.jsx(
583
+ designSystem.Button,
584
+ {
585
+ size: "S",
586
+ variant: "default",
587
+ onClick: handlePublishRelease,
588
+ loading: isPublishing,
589
+ disabled: release.actions.meta.count === 0,
590
+ children: formatMessage({
591
+ id: "content-releases.header.actions.publish",
592
+ defaultMessage: "Publish"
593
+ })
594
+ }
595
+ ) })
596
+ ] })
597
+ }
598
+ ),
599
+ children
600
+ ] });
601
+ };
602
+ const GROUP_BY_OPTIONS = ["contentType", "locale", "action"];
603
+ const getGroupByOptionLabel = (value) => {
604
+ if (value === "locale") {
605
+ return {
606
+ id: "content-releases.pages.ReleaseDetails.groupBy.option.locales",
607
+ defaultMessage: "Locales"
608
+ };
609
+ }
610
+ if (value === "action") {
611
+ return {
612
+ id: "content-releases.pages.ReleaseDetails.groupBy.option.actions",
613
+ defaultMessage: "Actions"
614
+ };
615
+ }
616
+ return {
617
+ id: "content-releases.pages.ReleaseDetails.groupBy.option.content-type",
618
+ defaultMessage: "Content-Types"
619
+ };
620
+ };
621
+ const ReleaseDetailsBody = () => {
622
+ const { formatMessage } = reactIntl.useIntl();
623
+ const { releaseId } = reactRouterDom.useParams();
624
+ const [{ query }, setQuery] = helperPlugin.useQueryParams();
625
+ const toggleNotification = helperPlugin.useNotification();
626
+ const { formatAPIError } = helperPlugin.useAPIErrorHandler();
627
+ const {
628
+ data: releaseData,
629
+ isLoading: isReleaseLoading,
630
+ isError: isReleaseError,
631
+ error: releaseError
632
+ } = index.useGetReleaseQuery({ id: releaseId });
633
+ const {
634
+ allowedActions: { canUpdate }
635
+ } = helperPlugin.useRBAC(index.PERMISSIONS);
636
+ const release = releaseData?.data;
637
+ const selectedGroupBy = query?.groupBy || "contentType";
638
+ const {
639
+ isLoading,
640
+ isFetching,
641
+ isError,
642
+ data,
643
+ error: releaseActionsError
644
+ } = index.useGetReleaseActionsQuery({
645
+ ...query,
646
+ releaseId
647
+ });
648
+ const [updateReleaseAction] = index.useUpdateReleaseActionMutation();
649
+ const handleChangeType = async (e, actionId, actionPath) => {
650
+ const response = await updateReleaseAction({
651
+ params: {
652
+ releaseId,
653
+ actionId
654
+ },
655
+ body: {
656
+ type: e.target.value
657
+ },
658
+ query,
659
+ // We are passing the query params to make optimistic updates
660
+ actionPath
661
+ // We are passing the action path to found the position in the cache of the action for optimistic updates
662
+ });
663
+ if ("error" in response) {
664
+ if (index.isAxiosError(response.error)) {
665
+ toggleNotification({
666
+ type: "warning",
667
+ message: formatAPIError(response.error)
668
+ });
669
+ } else {
670
+ toggleNotification({
671
+ type: "warning",
672
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
673
+ });
674
+ }
675
+ }
676
+ };
677
+ if (isLoading || isReleaseLoading) {
678
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
679
+ }
680
+ const releaseActions = data?.data;
681
+ const releaseMeta = data?.meta;
682
+ const contentTypes = releaseMeta?.contentTypes || {};
683
+ const components = releaseMeta?.components || {};
684
+ if (isReleaseError || !release) {
685
+ const errorsArray = [];
686
+ if (releaseError) {
687
+ errorsArray.push({
688
+ code: releaseError.code
689
+ });
690
+ }
691
+ if (releaseActionsError) {
692
+ errorsArray.push({
693
+ code: releaseActionsError.code
694
+ });
695
+ }
696
+ return /* @__PURE__ */ jsxRuntime.jsx(
697
+ reactRouterDom.Redirect,
698
+ {
699
+ to: {
700
+ pathname: "/plugins/content-releases",
701
+ state: {
702
+ errors: errorsArray
703
+ }
704
+ }
705
+ }
706
+ );
707
+ }
708
+ if (isError || !releaseActions) {
709
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {}) });
710
+ }
711
+ if (Object.keys(releaseActions).length === 0) {
712
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(
713
+ helperPlugin.NoContent,
714
+ {
715
+ content: {
716
+ id: "content-releases.pages.Details.tab.emptyEntries",
717
+ defaultMessage: "This release is empty. Open the Content Manager, select an entry and add it to the release."
718
+ },
719
+ action: /* @__PURE__ */ jsxRuntime.jsx(
720
+ v2.LinkButton,
721
+ {
722
+ as: reactRouterDom.Link,
723
+ to: {
724
+ pathname: "/content-manager"
725
+ },
726
+ style: { textDecoration: "none" },
727
+ variant: "secondary",
728
+ children: formatMessage({
729
+ id: "content-releases.page.Details.button.openContentManager",
730
+ defaultMessage: "Open the Content Manager"
731
+ })
732
+ }
733
+ )
734
+ }
735
+ ) });
736
+ }
737
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 8, direction: "column", alignItems: "stretch", children: [
738
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
739
+ designSystem.SingleSelect,
740
+ {
741
+ "aria-label": formatMessage({
742
+ id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
743
+ defaultMessage: "Group by"
744
+ }),
745
+ customizeContent: (value) => formatMessage(
746
+ {
747
+ id: `content-releases.pages.ReleaseDetails.groupBy.label`,
748
+ defaultMessage: `Group by {groupBy}`
749
+ },
750
+ {
751
+ groupBy: value
752
+ }
753
+ ),
754
+ value: formatMessage(getGroupByOptionLabel(selectedGroupBy)),
755
+ onChange: (value) => setQuery({ groupBy: value }),
756
+ children: GROUP_BY_OPTIONS.map((option) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: option, children: formatMessage(getGroupByOptionLabel(option)) }, option))
757
+ }
758
+ ) }),
759
+ Object.keys(releaseActions).map((key) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, direction: "column", alignItems: "stretch", children: [
760
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { role: "separator", "aria-label": key, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: key }) }),
761
+ /* @__PURE__ */ jsxRuntime.jsx(
762
+ helperPlugin.Table.Root,
763
+ {
764
+ rows: releaseActions[key].map((item) => ({
765
+ ...item,
766
+ id: Number(item.entry.id)
767
+ })),
768
+ colCount: releaseActions[key].length,
769
+ isLoading,
770
+ isFetching,
771
+ children: /* @__PURE__ */ jsxRuntime.jsxs(helperPlugin.Table.Content, { children: [
772
+ /* @__PURE__ */ jsxRuntime.jsxs(helperPlugin.Table.Head, { children: [
773
+ /* @__PURE__ */ jsxRuntime.jsx(
774
+ helperPlugin.Table.HeaderCell,
775
+ {
776
+ fieldSchemaType: "string",
777
+ label: formatMessage({
778
+ id: "content-releases.page.ReleaseDetails.table.header.label.name",
779
+ defaultMessage: "name"
780
+ }),
781
+ name: "name"
782
+ }
783
+ ),
784
+ /* @__PURE__ */ jsxRuntime.jsx(
785
+ helperPlugin.Table.HeaderCell,
786
+ {
787
+ fieldSchemaType: "string",
788
+ label: formatMessage({
789
+ id: "content-releases.page.ReleaseDetails.table.header.label.locale",
790
+ defaultMessage: "locale"
791
+ }),
792
+ name: "locale"
793
+ }
794
+ ),
795
+ /* @__PURE__ */ jsxRuntime.jsx(
796
+ helperPlugin.Table.HeaderCell,
797
+ {
798
+ fieldSchemaType: "string",
799
+ label: formatMessage({
800
+ id: "content-releases.page.ReleaseDetails.table.header.label.content-type",
801
+ defaultMessage: "content-type"
802
+ }),
803
+ name: "content-type"
804
+ }
805
+ ),
806
+ /* @__PURE__ */ jsxRuntime.jsx(
807
+ helperPlugin.Table.HeaderCell,
808
+ {
809
+ fieldSchemaType: "string",
810
+ label: formatMessage({
811
+ id: "content-releases.page.ReleaseDetails.table.header.label.action",
812
+ defaultMessage: "action"
813
+ }),
814
+ name: "action"
815
+ }
816
+ ),
817
+ !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsx(
818
+ helperPlugin.Table.HeaderCell,
819
+ {
820
+ fieldSchemaType: "string",
821
+ label: formatMessage({
822
+ id: "content-releases.page.ReleaseDetails.table.header.label.status",
823
+ defaultMessage: "status"
824
+ }),
825
+ name: "status"
826
+ }
827
+ )
828
+ ] }),
829
+ /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.Table.LoadingBody, {}),
830
+ /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.Table.Body, { children: releaseActions[key].map(
831
+ ({ id, contentType, locale, type, entry }, actionIndex) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
832
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "25%", maxWidth: "200px", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: `${contentType.mainFieldValue || entry.id}` }) }),
833
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "10%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
834
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "10%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: contentType.displayName || "" }) }),
835
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "20%", children: release.releasedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage(
836
+ {
837
+ id: "content-releases.page.ReleaseDetails.table.action-published",
838
+ defaultMessage: "This entry was <b>{isPublish, select, true {published} other {unpublished}}</b>."
839
+ },
840
+ {
841
+ isPublish: type === "publish",
842
+ b: (children) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children })
843
+ }
844
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(
845
+ index.ReleaseActionOptions,
846
+ {
847
+ selected: type,
848
+ handleChange: (e) => handleChangeType(e, id, [key, actionIndex]),
849
+ name: `release-action-${id}-type`,
850
+ disabled: !canUpdate
851
+ }
852
+ ) }),
853
+ !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
854
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "20%", minWidth: "200px", children: /* @__PURE__ */ jsxRuntime.jsx(
855
+ EntryValidationText,
856
+ {
857
+ action: type,
858
+ schema: contentTypes?.[contentType.uid],
859
+ components,
860
+ entry
861
+ }
862
+ ) }),
863
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxRuntime.jsxs(index.ReleaseActionMenu.Root, { children: [
864
+ /* @__PURE__ */ jsxRuntime.jsx(
865
+ index.ReleaseActionMenu.ReleaseActionEntryLinkItem,
866
+ {
867
+ contentTypeUid: contentType.uid,
868
+ entryId: entry.id,
869
+ locale: locale?.code
870
+ }
871
+ ),
872
+ /* @__PURE__ */ jsxRuntime.jsx(
873
+ index.ReleaseActionMenu.DeleteReleaseActionItem,
874
+ {
875
+ releaseId: release.id,
876
+ actionId: id
877
+ }
878
+ )
879
+ ] }) }) })
880
+ ] })
881
+ ] }, id)
882
+ ) })
883
+ ] })
884
+ }
885
+ )
886
+ ] }, `releases-group-${key}`)),
887
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
888
+ /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.PageSizeURLQuery, { defaultValue: releaseMeta?.pagination?.pageSize.toString() }),
889
+ /* @__PURE__ */ jsxRuntime.jsx(
890
+ helperPlugin.PaginationURLQuery,
891
+ {
892
+ pagination: {
893
+ pageCount: releaseMeta?.pagination?.pageCount || 0
894
+ }
895
+ }
896
+ )
897
+ ] })
898
+ ] }) });
899
+ };
900
+ const ReleaseDetailsPage = () => {
901
+ const { formatMessage } = reactIntl.useIntl();
902
+ const { releaseId } = reactRouterDom.useParams();
903
+ const toggleNotification = helperPlugin.useNotification();
904
+ const { formatAPIError } = helperPlugin.useAPIErrorHandler();
905
+ const { push } = reactRouterDom.useHistory();
906
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
907
+ const [showWarningSubmit, setWarningSubmit] = React__namespace.useState(false);
908
+ const {
909
+ isLoading: isLoadingDetails,
910
+ data,
911
+ isSuccess: isSuccessDetails
912
+ } = index.useGetReleaseQuery({ id: releaseId });
913
+ const [updateRelease, { isLoading: isSubmittingForm }] = index.useUpdateReleaseMutation();
914
+ const [deleteRelease, { isLoading: isDeletingRelease }] = index.useDeleteReleaseMutation();
915
+ const toggleEditReleaseModal = () => {
916
+ setReleaseModalShown((prev) => !prev);
917
+ };
918
+ const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
919
+ if (isLoadingDetails) {
920
+ return /* @__PURE__ */ jsxRuntime.jsx(
921
+ ReleaseDetailsLayout,
922
+ {
923
+ toggleEditReleaseModal,
924
+ toggleWarningSubmit,
925
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) })
926
+ }
927
+ );
928
+ }
929
+ const releaseData = isSuccessDetails && data?.data || null;
930
+ const title = releaseData?.name || "";
931
+ const timezone = releaseData?.timezone ?? null;
932
+ const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
933
+ const date = scheduledAt ? new Date(format__default.default(scheduledAt, "yyyy-MM-dd")) : null;
934
+ const time = scheduledAt ? format__default.default(scheduledAt, "HH:mm") : "";
935
+ const handleEditRelease = async (values) => {
936
+ const response = await updateRelease({
937
+ id: releaseId,
938
+ name: values.name,
939
+ scheduledAt: values.scheduledAt,
940
+ timezone: values.timezone
941
+ });
942
+ if ("data" in response) {
943
+ toggleNotification({
944
+ type: "success",
945
+ message: formatMessage({
946
+ id: "content-releases.modal.release-updated-notification-success",
947
+ defaultMessage: "Release updated."
948
+ })
949
+ });
950
+ } else if (index.isAxiosError(response.error)) {
951
+ toggleNotification({
952
+ type: "warning",
953
+ message: formatAPIError(response.error)
954
+ });
955
+ } else {
956
+ toggleNotification({
957
+ type: "warning",
958
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
959
+ });
960
+ }
961
+ toggleEditReleaseModal();
962
+ };
963
+ const handleDeleteRelease = async () => {
964
+ const response = await deleteRelease({
965
+ id: releaseId
966
+ });
967
+ if ("data" in response) {
968
+ push("/plugins/content-releases");
969
+ } else if (index.isAxiosError(response.error)) {
970
+ toggleNotification({
971
+ type: "warning",
972
+ message: formatAPIError(response.error)
973
+ });
974
+ } else {
975
+ toggleNotification({
976
+ type: "warning",
977
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
978
+ });
979
+ }
980
+ };
981
+ return /* @__PURE__ */ jsxRuntime.jsxs(
982
+ ReleaseDetailsLayout,
983
+ {
984
+ toggleEditReleaseModal,
985
+ toggleWarningSubmit,
986
+ children: [
987
+ /* @__PURE__ */ jsxRuntime.jsx(ReleaseDetailsBody, {}),
988
+ releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
989
+ ReleaseModal,
990
+ {
991
+ handleClose: toggleEditReleaseModal,
992
+ handleSubmit: handleEditRelease,
993
+ isLoading: isLoadingDetails || isSubmittingForm,
994
+ initialValues: {
995
+ name: title || "",
996
+ scheduledAt,
997
+ date,
998
+ time,
999
+ isScheduled: Boolean(scheduledAt),
1000
+ timezone
1001
+ }
1002
+ }
1003
+ ),
1004
+ /* @__PURE__ */ jsxRuntime.jsx(
1005
+ helperPlugin.ConfirmDialog,
1006
+ {
1007
+ bodyText: {
1008
+ id: "content-releases.dialog.confirmation-message",
1009
+ defaultMessage: "Are you sure you want to delete this release?"
1010
+ },
1011
+ isOpen: showWarningSubmit,
1012
+ isConfirmButtonLoading: isDeletingRelease,
1013
+ onToggleDialog: toggleWarningSubmit,
1014
+ onConfirm: handleDeleteRelease
1015
+ }
1016
+ )
1017
+ ]
1018
+ }
1019
+ );
1020
+ };
1021
+ const LinkCard = styled__default.default(v2.Link)`
1022
+ display: block;
1023
+ `;
1024
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
1025
+ const { formatMessage } = reactIntl.useIntl();
1026
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
1027
+ if (isError) {
1028
+ return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
1029
+ }
1030
+ if (releases?.length === 0) {
1031
+ return /* @__PURE__ */ jsxRuntime.jsx(
1032
+ designSystem.EmptyStateLayout,
1033
+ {
1034
+ content: formatMessage(
1035
+ {
1036
+ id: "content-releases.page.Releases.tab.emptyEntries",
1037
+ defaultMessage: "No releases"
1038
+ },
1039
+ {
1040
+ target: sectionTitle
1041
+ }
1042
+ ),
1043
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.EmptyDocuments, { width: "10rem" })
1044
+ }
1045
+ );
1046
+ }
1047
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid, { gap: 4, children: releases.map(({ id, name, actions, scheduledAt }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxRuntime.jsxs(
1048
+ designSystem.Flex,
1049
+ {
1050
+ direction: "column",
1051
+ justifyContent: "space-between",
1052
+ padding: 4,
1053
+ hasRadius: true,
1054
+ background: "neutral0",
1055
+ shadow: "tableShadow",
1056
+ height: "100%",
1057
+ width: "100%",
1058
+ alignItems: "start",
1059
+ gap: 2,
1060
+ children: [
1061
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
1062
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: IsSchedulingEnabled ? scheduledAt ? /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
1063
+ id: "content-releases.pages.Releases.not-scheduled",
1064
+ defaultMessage: "Not scheduled"
1065
+ }) : formatMessage(
1066
+ {
1067
+ id: "content-releases.page.Releases.release-item.entries",
1068
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
1069
+ },
1070
+ { number: actions.meta.count }
1071
+ ) })
1072
+ ]
1073
+ }
1074
+ ) }) }, id)) });
1075
+ };
1076
+ const StyledAlert = styled__default.default(designSystem.Alert)`
1077
+ button {
1078
+ display: none;
1079
+ }
1080
+ p + div {
1081
+ margin-left: auto;
1082
+ }
1083
+ `;
1084
+ const INITIAL_FORM_VALUES = {
1085
+ name: "",
1086
+ date: null,
1087
+ time: "",
1088
+ // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
1089
+ isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
1090
+ scheduledAt: null,
1091
+ timezone: null
1092
+ };
1093
+ const ReleasesPage = () => {
1094
+ const tabRef = React__namespace.useRef(null);
1095
+ const location = reactRouterDom.useLocation();
1096
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
1097
+ const toggleNotification = helperPlugin.useNotification();
1098
+ const { formatMessage } = reactIntl.useIntl();
1099
+ const { push, replace } = reactRouterDom.useHistory();
1100
+ const { formatAPIError } = helperPlugin.useAPIErrorHandler();
1101
+ const [{ query }, setQuery] = helperPlugin.useQueryParams();
1102
+ const response = index.useGetReleasesQuery(query);
1103
+ const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
1104
+ const { getFeature } = strapiAdmin.useLicenseLimits();
1105
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
1106
+ const { trackUsage } = helperPlugin.useTracking();
1107
+ const { isLoading, isSuccess, isError } = response;
1108
+ const activeTab = response?.currentData?.meta?.activeTab || "pending";
1109
+ const activeTabIndex = ["pending", "done"].indexOf(activeTab);
1110
+ React__namespace.useEffect(() => {
1111
+ if (location?.state?.errors) {
1112
+ toggleNotification({
1113
+ type: "warning",
1114
+ title: formatMessage({
1115
+ id: "content-releases.pages.Releases.notification.error.title",
1116
+ defaultMessage: "Your request could not be processed."
1117
+ }),
1118
+ message: formatMessage({
1119
+ id: "content-releases.pages.Releases.notification.error.message",
1120
+ defaultMessage: "Please try again or open another release."
1121
+ })
1122
+ });
1123
+ replace({ state: null });
1124
+ }
1125
+ }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
1126
+ React__namespace.useEffect(() => {
1127
+ if (tabRef.current) {
1128
+ tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
1129
+ }
1130
+ }, [activeTabIndex]);
1131
+ const toggleAddReleaseModal = () => {
1132
+ setReleaseModalShown((prev) => !prev);
1133
+ };
1134
+ if (isLoading) {
1135
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
1136
+ }
1137
+ const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
1138
+ const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
1139
+ const handleTabChange = (index2) => {
1140
+ setQuery({
1141
+ ...query,
1142
+ page: 1,
1143
+ pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
1144
+ filters: {
1145
+ releasedAt: {
1146
+ $notNull: index2 === 0 ? false : true
1147
+ }
1148
+ }
1149
+ });
1150
+ };
1151
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
1152
+ const response2 = await createRelease({
1153
+ name,
1154
+ scheduledAt,
1155
+ timezone
1156
+ });
1157
+ if ("data" in response2) {
1158
+ toggleNotification({
1159
+ type: "success",
1160
+ message: formatMessage({
1161
+ id: "content-releases.modal.release-created-notification-success",
1162
+ defaultMessage: "Release created."
1163
+ })
1164
+ });
1165
+ trackUsage("didCreateRelease");
1166
+ push(`/plugins/content-releases/${response2.data.data.id}`);
1167
+ } else if (index.isAxiosError(response2.error)) {
1168
+ toggleNotification({
1169
+ type: "warning",
1170
+ message: formatAPIError(response2.error)
1171
+ });
1172
+ } else {
1173
+ toggleNotification({
1174
+ type: "warning",
1175
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1176
+ });
1177
+ }
1178
+ };
1179
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
1180
+ /* @__PURE__ */ jsxRuntime.jsx(
1181
+ designSystem.HeaderLayout,
1182
+ {
1183
+ title: formatMessage({
1184
+ id: "content-releases.pages.Releases.title",
1185
+ defaultMessage: "Releases"
1186
+ }),
1187
+ subtitle: formatMessage(
1188
+ {
1189
+ id: "content-releases.pages.Releases.header-subtitle",
1190
+ defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
1191
+ },
1192
+ { number: totalReleases }
1193
+ ),
1194
+ primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
1195
+ designSystem.Button,
1196
+ {
1197
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1198
+ onClick: toggleAddReleaseModal,
1199
+ disabled: hasReachedMaximumPendingReleases,
1200
+ children: formatMessage({
1201
+ id: "content-releases.header.actions.add-release",
1202
+ defaultMessage: "New release"
1203
+ })
1204
+ }
1205
+ ) })
1206
+ }
1207
+ ),
1208
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1209
+ activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
1210
+ StyledAlert,
1211
+ {
1212
+ marginBottom: 6,
1213
+ action: /* @__PURE__ */ jsxRuntime.jsx(v2.Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
1214
+ id: "content-releases.pages.Releases.max-limit-reached.action",
1215
+ defaultMessage: "Explore plans"
1216
+ }) }),
1217
+ title: formatMessage(
1218
+ {
1219
+ id: "content-releases.pages.Releases.max-limit-reached.title",
1220
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
1221
+ },
1222
+ { number: maximumReleases }
1223
+ ),
1224
+ onClose: () => {
1225
+ },
1226
+ closeLabel: "",
1227
+ children: formatMessage({
1228
+ id: "content-releases.pages.Releases.max-limit-reached.message",
1229
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
1230
+ })
1231
+ }
1232
+ ),
1233
+ /* @__PURE__ */ jsxRuntime.jsxs(
1234
+ designSystem.TabGroup,
1235
+ {
1236
+ label: formatMessage({
1237
+ id: "content-releases.pages.Releases.tab-group.label",
1238
+ defaultMessage: "Releases list"
1239
+ }),
1240
+ variant: "simple",
1241
+ initialSelectedTabIndex: activeTabIndex,
1242
+ onTabChange: handleTabChange,
1243
+ ref: tabRef,
1244
+ children: [
1245
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingBottom: 8, children: [
1246
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs, { children: [
1247
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1248
+ id: "content-releases.pages.Releases.tab.pending",
1249
+ defaultMessage: "Pending"
1250
+ }) }),
1251
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1252
+ id: "content-releases.pages.Releases.tab.done",
1253
+ defaultMessage: "Done"
1254
+ }) })
1255
+ ] }),
1256
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {})
1257
+ ] }),
1258
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.TabPanels, { children: [
1259
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
1260
+ ReleasesGrid,
1261
+ {
1262
+ sectionTitle: "pending",
1263
+ releases: response?.currentData?.data,
1264
+ isError
1265
+ }
1266
+ ) }),
1267
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
1268
+ ReleasesGrid,
1269
+ {
1270
+ sectionTitle: "done",
1271
+ releases: response?.currentData?.data,
1272
+ isError
1273
+ }
1274
+ ) })
1275
+ ] })
1276
+ ]
1277
+ }
1278
+ ),
1279
+ totalReleases > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1280
+ /* @__PURE__ */ jsxRuntime.jsx(
1281
+ helperPlugin.PageSizeURLQuery,
1282
+ {
1283
+ options: ["8", "16", "32", "64"],
1284
+ defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
1285
+ }
1286
+ ),
1287
+ /* @__PURE__ */ jsxRuntime.jsx(
1288
+ helperPlugin.PaginationURLQuery,
1289
+ {
1290
+ pagination: {
1291
+ pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
1292
+ }
1293
+ }
1294
+ )
1295
+ ] })
1296
+ ] }) }),
1297
+ releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
1298
+ ReleaseModal,
1299
+ {
1300
+ handleClose: toggleAddReleaseModal,
1301
+ handleSubmit: handleAddRelease,
1302
+ isLoading: isSubmittingForm,
1303
+ initialValues: INITIAL_FORM_VALUES
1304
+ }
1305
+ )
1306
+ ] });
1307
+ };
1308
+ const App = () => {
1309
+ return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPagePermissions, { permissions: index.PERMISSIONS.main, children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Switch, { children: [
1310
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { exact: true, path: `/plugins/${index.pluginId}`, component: ReleasesPage }),
1311
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { exact: true, path: `/plugins/${index.pluginId}/:releaseId`, component: ReleaseDetailsPage })
1312
+ ] }) });
1313
+ };
1314
+ exports.App = App;
1315
+ //# sourceMappingURL=App-OK4Xac-O.js.map