@strapi/content-releases 0.0.0-experimental.ee4d311a5e6a131fad03cf07e4696f49fdd9c2e6 → 0.0.0-experimental.f75e3c6d67cc47c64ab37479efdbb7b43be50b78

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