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

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