@strapi/content-releases 0.0.0-next.3844395bef7efa05c25c6d4337306935905bc653 → 0.0.0-next.4af8963f6880c5fb9fae32ecd580f5cd33eaddda

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 (29) hide show
  1. package/dist/_chunks/{App-L1jSxCiL.mjs → App-ise7GunC.mjs} +493 -235
  2. package/dist/_chunks/App-ise7GunC.mjs.map +1 -0
  3. package/dist/_chunks/{App-_20W9dYa.js → App-w2Zq-wj5.js} +489 -230
  4. package/dist/_chunks/App-w2Zq-wj5.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-gYDqKYFd.js → en-7P4i1cWH.js} +14 -3
  10. package/dist/_chunks/en-7P4i1cWH.js.map +1 -0
  11. package/dist/_chunks/{en-MyLPoISH.mjs → en-pb1wUzhy.mjs} +14 -3
  12. package/dist/_chunks/en-pb1wUzhy.mjs.map +1 -0
  13. package/dist/_chunks/{index-c4zRX_sg.mjs → index-D-Yjf60c.mjs} +89 -26
  14. package/dist/_chunks/index-D-Yjf60c.mjs.map +1 -0
  15. package/dist/_chunks/{index-KJa1Rb5F.js → index-Q8Pv7enO.js} +88 -25
  16. package/dist/_chunks/index-Q8Pv7enO.js.map +1 -0
  17. package/dist/admin/index.js +1 -1
  18. package/dist/admin/index.mjs +1 -1
  19. package/dist/server/index.js +597 -382
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +597 -382
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +12 -9
  24. package/dist/_chunks/App-L1jSxCiL.mjs.map +0 -1
  25. package/dist/_chunks/App-_20W9dYa.js.map +0 -1
  26. package/dist/_chunks/en-MyLPoISH.mjs.map +0 -1
  27. package/dist/_chunks/en-gYDqKYFd.js.map +0 -1
  28. package/dist/_chunks/index-KJa1Rb5F.js.map +0 -1
  29. package/dist/_chunks/index-c4zRX_sg.mjs.map +0 -1
@@ -3,14 +3,17 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const helperPlugin = require("@strapi/helper-plugin");
5
5
  const reactRouterDom = require("react-router-dom");
6
- const index = require("./index-KJa1Rb5F.js");
6
+ const index = require("./index-Q8Pv7enO.js");
7
7
  const React = require("react");
8
8
  const strapiAdmin = require("@strapi/admin/strapi-admin");
9
9
  const designSystem = require("@strapi/design-system");
10
10
  const v2 = require("@strapi/design-system/v2");
11
11
  const icons = require("@strapi/icons");
12
+ const format = require("date-fns/format");
13
+ const dateFnsTz = require("date-fns-tz");
12
14
  const reactIntl = require("react-intl");
13
15
  const styled = require("styled-components");
16
+ const dateFns = require("date-fns");
14
17
  const formik = require("formik");
15
18
  const yup = require("yup");
16
19
  require("@reduxjs/toolkit/query");
@@ -37,10 +40,28 @@ function _interopNamespace(e) {
37
40
  return Object.freeze(n);
38
41
  }
39
42
  const React__namespace = /* @__PURE__ */ _interopNamespace(React);
43
+ const format__default = /* @__PURE__ */ _interopDefault(format);
40
44
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
41
45
  const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
42
46
  const RELEASE_SCHEMA = yup__namespace.object().shape({
43
- name: yup__namespace.string().trim().required()
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
+ })
44
65
  }).required().noUnknown();
45
66
  const ReleaseModal = ({
46
67
  handleClose,
@@ -51,6 +72,24 @@ const ReleaseModal = ({
51
72
  const { formatMessage } = reactIntl.useIntl();
52
73
  const { pathname } = reactRouterDom.useLocation();
53
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
+ };
54
93
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalLayout, { onClose: handleClose, labelledBy: "title", children: [
55
94
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "title", fontWeight: "bold", textColor: "neutral800", children: formatMessage(
56
95
  {
@@ -62,45 +101,134 @@ const ReleaseModal = ({
62
101
  /* @__PURE__ */ jsxRuntime.jsx(
63
102
  formik.Formik,
64
103
  {
65
- validateOnChange: false,
66
- onSubmit: handleSubmit,
67
- initialValues,
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
+ },
68
115
  validationSchema: RELEASE_SCHEMA,
69
- children: ({ values, errors, handleChange }) => /* @__PURE__ */ jsxRuntime.jsxs(formik.Form, { children: [
70
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalBody, { children: /* @__PURE__ */ jsxRuntime.jsx(
71
- designSystem.TextInput,
72
- {
73
- label: formatMessage({
74
- id: "content-releases.modal.form.input.label.release-name",
75
- defaultMessage: "Name"
76
- }),
77
- name: "name",
78
- value: values.name,
79
- error: errors.name,
80
- onChange: handleChange,
81
- required: true
82
- }
83
- ) }),
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(
135
+ designSystem.Checkbox,
136
+ {
137
+ name: "isScheduled",
138
+ value: values.isScheduled,
139
+ onChange: (event) => {
140
+ setFieldValue("isScheduled", event.target.checked);
141
+ if (!event.target.checked) {
142
+ setFieldValue("date", null);
143
+ setFieldValue("time", "");
144
+ setFieldValue("timezone", null);
145
+ } else {
146
+ setFieldValue("date", initialValues.date);
147
+ setFieldValue("time", initialValues.time);
148
+ setFieldValue(
149
+ "timezone",
150
+ initialValues.timezone ?? systemTimezone?.value
151
+ );
152
+ }
153
+ },
154
+ children: /* @__PURE__ */ jsxRuntime.jsx(
155
+ designSystem.Typography,
156
+ {
157
+ textColor: values.isScheduled ? "primary600" : "neutral800",
158
+ fontWeight: values.isScheduled ? "semiBold" : "regular",
159
+ children: formatMessage({
160
+ id: "modal.form.input.label.schedule-release",
161
+ defaultMessage: "Schedule release"
162
+ })
163
+ }
164
+ )
165
+ }
166
+ ),
167
+ values.isScheduled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
168
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "start", children: [
169
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
170
+ designSystem.DatePicker,
171
+ {
172
+ label: formatMessage({
173
+ id: "content-releases.modal.form.input.label.date",
174
+ defaultMessage: "Date"
175
+ }),
176
+ name: "date",
177
+ error: errors.date,
178
+ onChange: (date) => {
179
+ const isoFormatDate = date ? dateFns.formatISO(date, { representation: "date" }) : null;
180
+ setFieldValue("date", isoFormatDate);
181
+ },
182
+ clearLabel: formatMessage({
183
+ id: "content-releases.modal.form.input.clearLabel",
184
+ defaultMessage: "Clear"
185
+ }),
186
+ onClear: () => {
187
+ setFieldValue("date", null);
188
+ },
189
+ selectedDate: values.date || void 0,
190
+ required: true
191
+ }
192
+ ) }),
193
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
194
+ designSystem.TimePicker,
195
+ {
196
+ label: formatMessage({
197
+ id: "content-releases.modal.form.input.label.time",
198
+ defaultMessage: "Time"
199
+ }),
200
+ name: "time",
201
+ error: errors.time,
202
+ onChange: (time) => {
203
+ setFieldValue("time", time);
204
+ },
205
+ clearLabel: formatMessage({
206
+ id: "content-releases.modal.form.input.clearLabel",
207
+ defaultMessage: "Clear"
208
+ }),
209
+ onClear: () => {
210
+ setFieldValue("time", "");
211
+ },
212
+ value: values.time || void 0,
213
+ required: true
214
+ }
215
+ ) })
216
+ ] }),
217
+ /* @__PURE__ */ jsxRuntime.jsx(TimezoneComponent, { timezoneOptions: timezoneList })
218
+ ] })
219
+ ] })
220
+ ] }) }),
84
221
  /* @__PURE__ */ jsxRuntime.jsx(
85
222
  designSystem.ModalFooter,
86
223
  {
87
224
  startActions: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleClose, variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }),
88
- endActions: /* @__PURE__ */ jsxRuntime.jsx(
89
- designSystem.Button,
225
+ endActions: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
90
226
  {
91
- name: "submit",
92
- loading: isLoading,
93
- disabled: !values.name || values.name === initialValues.name,
94
- type: "submit",
95
- children: formatMessage(
96
- {
97
- id: "content-releases.modal.form.button.submit",
98
- defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
99
- },
100
- { isCreatingRelease }
101
- )
102
- }
103
- )
227
+ id: "content-releases.modal.form.button.submit",
228
+ defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
229
+ },
230
+ { isCreatingRelease }
231
+ ) })
104
232
  }
105
233
  )
106
234
  ] })
@@ -108,16 +236,67 @@ const ReleaseModal = ({
108
236
  )
109
237
  ] });
110
238
  };
239
+ const getTimezones = (selectedDate) => {
240
+ const timezoneList = Intl.supportedValuesOf("timeZone").map((timezone) => {
241
+ const offsetPart = new Intl.DateTimeFormat("en", {
242
+ timeZone: timezone,
243
+ timeZoneName: "longOffset"
244
+ }).formatToParts(selectedDate).find((part) => part.type === "timeZoneName");
245
+ const offset = offsetPart ? offsetPart.value : "";
246
+ let utcOffset = offset.replace("GMT", "UTC");
247
+ if (!utcOffset.includes("+") && !utcOffset.includes("-")) {
248
+ utcOffset = `${utcOffset}+00:00`;
249
+ }
250
+ return { offset: utcOffset, value: `${utcOffset}-${timezone}` };
251
+ });
252
+ const systemTimezone = timezoneList.find(
253
+ (timezone) => timezone.value.split("-")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
254
+ );
255
+ return { timezoneList, systemTimezone };
256
+ };
257
+ const TimezoneComponent = ({ timezoneOptions }) => {
258
+ const { values, errors, setFieldValue } = formik.useFormikContext();
259
+ const { formatMessage } = reactIntl.useIntl();
260
+ const [timezoneList, setTimezoneList] = React__namespace.useState(timezoneOptions);
261
+ React__namespace.useEffect(() => {
262
+ if (values.date) {
263
+ const { timezoneList: timezoneList2 } = getTimezones(new Date(values.date));
264
+ setTimezoneList(timezoneList2);
265
+ const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("-")[1] === values.timezone.split("-")[1]);
266
+ if (updatedTimezone) {
267
+ setFieldValue("timezone", updatedTimezone.value);
268
+ }
269
+ }
270
+ }, [setFieldValue, values.date, values.timezone]);
271
+ return /* @__PURE__ */ jsxRuntime.jsx(
272
+ designSystem.Combobox,
273
+ {
274
+ label: formatMessage({
275
+ id: "content-releases.modal.form.input.label.timezone",
276
+ defaultMessage: "Timezone"
277
+ }),
278
+ name: "timezone",
279
+ value: values.timezone || void 0,
280
+ textValue: values.timezone ? values.timezone.replace("-", " ") : void 0,
281
+ onChange: (timezone) => {
282
+ setFieldValue("timezone", timezone);
283
+ },
284
+ onClear: () => {
285
+ setFieldValue("timezone", "");
286
+ },
287
+ error: errors.timezone,
288
+ required: true,
289
+ children: timezoneList.map((timezone) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: timezone.value, children: timezone.value.replace("-", " ") }, timezone.value))
290
+ }
291
+ );
292
+ };
111
293
  const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
112
294
  align-self: stretch;
113
295
  border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
114
296
  border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
115
297
  border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
116
298
  `;
117
- const StyledFlex = styled__default.default(designSystem.Flex)`
118
- align-self: stretch;
119
- cursor: ${({ disabled }) => disabled ? "not-allowed" : "pointer"};
120
-
299
+ const StyledMenuItem = styled__default.default(v2.Menu.Item)`
121
300
  svg path {
122
301
  fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
123
302
  }
@@ -126,15 +305,15 @@ const StyledFlex = styled__default.default(designSystem.Flex)`
126
305
  }
127
306
  `;
128
307
  const PencilIcon = styled__default.default(icons.Pencil)`
129
- width: ${({ theme }) => theme.spaces[4]};
130
- height: ${({ theme }) => theme.spaces[4]};
308
+ width: ${({ theme }) => theme.spaces[3]};
309
+ height: ${({ theme }) => theme.spaces[3]};
131
310
  path {
132
311
  fill: ${({ theme }) => theme.colors.neutral600};
133
312
  }
134
313
  `;
135
314
  const TrashIcon = styled__default.default(icons.Trash)`
136
- width: ${({ theme }) => theme.spaces[4]};
137
- height: ${({ theme }) => theme.spaces[4]};
315
+ width: ${({ theme }) => theme.spaces[3]};
316
+ height: ${({ theme }) => theme.spaces[3]};
138
317
  path {
139
318
  fill: ${({ theme }) => theme.colors.danger600};
140
319
  }
@@ -142,24 +321,6 @@ const TrashIcon = styled__default.default(icons.Trash)`
142
321
  const TypographyMaxWidth = styled__default.default(designSystem.Typography)`
143
322
  max-width: 300px;
144
323
  `;
145
- const PopoverButton = ({ onClick, disabled, children }) => {
146
- return /* @__PURE__ */ jsxRuntime.jsx(
147
- StyledFlex,
148
- {
149
- paddingTop: 2,
150
- paddingBottom: 2,
151
- paddingLeft: 4,
152
- paddingRight: 4,
153
- alignItems: "center",
154
- gap: 2,
155
- as: "button",
156
- hasRadius: true,
157
- onClick,
158
- disabled,
159
- children
160
- }
161
- );
162
- };
163
324
  const EntryValidationText = ({ action, schema, components, entry }) => {
164
325
  const { formatMessage } = reactIntl.useIntl();
165
326
  const { validate } = strapiAdmin.unstable_useDocument();
@@ -210,8 +371,6 @@ const ReleaseDetailsLayout = ({
210
371
  }) => {
211
372
  const { formatMessage } = reactIntl.useIntl();
212
373
  const { releaseId } = reactRouterDom.useParams();
213
- const [isPopoverVisible, setIsPopoverVisible] = React__namespace.useState(false);
214
- const moreButtonRef = React__namespace.useRef(null);
215
374
  const {
216
375
  data,
217
376
  isLoading: isLoadingDetails,
@@ -225,14 +384,8 @@ const ReleaseDetailsLayout = ({
225
384
  allowedActions: { canUpdate, canDelete }
226
385
  } = helperPlugin.useRBAC(index.PERMISSIONS);
227
386
  const dispatch = index.useTypedDispatch();
387
+ const { trackUsage } = helperPlugin.useTracking();
228
388
  const release = data?.data;
229
- const handleTogglePopover = () => {
230
- setIsPopoverVisible((prev) => !prev);
231
- };
232
- const openReleaseModal = () => {
233
- toggleEditReleaseModal();
234
- handleTogglePopover();
235
- };
236
389
  const handlePublishRelease = async () => {
237
390
  const response = await publishRelease({ id: releaseId });
238
391
  if ("data" in response) {
@@ -243,6 +396,12 @@ const ReleaseDetailsLayout = ({
243
396
  defaultMessage: "Release was published successfully."
244
397
  })
245
398
  });
399
+ const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
400
+ trackUsage("didPublishRelease", {
401
+ totalEntries: totalEntries2,
402
+ totalPublishedEntries,
403
+ totalUnpublishedEntries
404
+ });
246
405
  } else if (index.isAxiosError(response.error)) {
247
406
  toggleNotification({
248
407
  type: "warning",
@@ -255,13 +414,21 @@ const ReleaseDetailsLayout = ({
255
414
  });
256
415
  }
257
416
  };
258
- const openWarningConfirmDialog = () => {
259
- toggleWarningSubmit();
260
- handleTogglePopover();
261
- };
262
417
  const handleRefresh = () => {
263
418
  dispatch(index.releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
264
419
  };
420
+ const getCreatedByUser = () => {
421
+ if (!release?.createdBy) {
422
+ return null;
423
+ }
424
+ if (release.createdBy.username) {
425
+ return release.createdBy.username;
426
+ }
427
+ if (release.createdBy.firstname) {
428
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
429
+ }
430
+ return release.createdBy.email;
431
+ };
265
432
  if (isLoadingDetails) {
266
433
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
267
434
  }
@@ -283,7 +450,7 @@ const ReleaseDetailsLayout = ({
283
450
  );
284
451
  }
285
452
  const totalEntries = release.actions.meta.count || 0;
286
- const createdBy = release.createdBy.lastname ? `${release.createdBy.firstname} ${release.createdBy.lastname}` : `${release.createdBy.firstname}`;
453
+ const hasCreatedByUser = Boolean(getCreatedByUser());
287
454
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoadingDetails, children: [
288
455
  /* @__PURE__ */ jsxRuntime.jsx(
289
456
  designSystem.HeaderLayout,
@@ -301,72 +468,98 @@ const ReleaseDetailsLayout = ({
301
468
  defaultMessage: "Back"
302
469
  }) }),
303
470
  primaryAction: !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
304
- /* @__PURE__ */ jsxRuntime.jsx(
305
- designSystem.IconButton,
306
- {
307
- label: formatMessage({
308
- id: "content-releases.header.actions.open-release-actions",
309
- defaultMessage: "Release actions"
310
- }),
311
- ref: moreButtonRef,
312
- onClick: handleTogglePopover,
313
- children: /* @__PURE__ */ jsxRuntime.jsx(icons.More, {})
314
- }
315
- ),
316
- isPopoverVisible && /* @__PURE__ */ jsxRuntime.jsxs(
317
- designSystem.Popover,
318
- {
319
- source: moreButtonRef,
320
- placement: "bottom-end",
321
- onDismiss: handleTogglePopover,
322
- spacing: 4,
323
- minWidth: "242px",
324
- children: [
325
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", justifyContent: "center", direction: "column", padding: 1, children: [
326
- /* @__PURE__ */ jsxRuntime.jsxs(PopoverButton, { disabled: !canUpdate, onClick: openReleaseModal, children: [
327
- /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
328
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
329
- id: "content-releases.header.actions.edit",
330
- defaultMessage: "Edit"
331
- }) })
332
- ] }),
333
- /* @__PURE__ */ jsxRuntime.jsxs(PopoverButton, { disabled: !canDelete, onClick: openWarningConfirmDialog, children: [
334
- /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
335
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
336
- id: "content-releases.header.actions.delete",
337
- defaultMessage: "Delete"
338
- }) })
339
- ] })
340
- ] }),
341
- /* @__PURE__ */ jsxRuntime.jsxs(
342
- ReleaseInfoWrapper,
343
- {
344
- direction: "column",
345
- justifyContent: "center",
346
- alignItems: "flex-start",
347
- gap: 1,
348
- padding: 5,
349
- children: [
350
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
351
- id: "content-releases.header.actions.created",
352
- defaultMessage: "Created"
353
- }) }),
354
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", color: "neutral300", children: [
355
- /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(release.createdAt) }),
356
- formatMessage(
357
- {
358
- id: "content-releases.header.actions.created.description",
359
- defaultMessage: " by {createdBy}"
360
- },
361
- { createdBy }
362
- )
363
- ] })
364
- ]
365
- }
366
- )
367
- ]
368
- }
369
- ),
471
+ /* @__PURE__ */ jsxRuntime.jsxs(v2.Menu.Root, { children: [
472
+ /* @__PURE__ */ jsxRuntime.jsx(
473
+ v2.Menu.Trigger,
474
+ {
475
+ as: designSystem.IconButton,
476
+ paddingLeft: 2,
477
+ paddingRight: 2,
478
+ "aria-label": formatMessage({
479
+ id: "content-releases.header.actions.open-release-actions",
480
+ defaultMessage: "Release edit and delete menu"
481
+ }),
482
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.More, {}),
483
+ variant: "tertiary"
484
+ }
485
+ ),
486
+ /* @__PURE__ */ jsxRuntime.jsxs(v2.Menu.Content, { top: 1, popoverPlacement: "bottom-end", children: [
487
+ /* @__PURE__ */ jsxRuntime.jsxs(
488
+ designSystem.Flex,
489
+ {
490
+ alignItems: "center",
491
+ justifyContent: "center",
492
+ direction: "column",
493
+ padding: 1,
494
+ width: "100%",
495
+ children: [
496
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(
497
+ designSystem.Flex,
498
+ {
499
+ paddingTop: 2,
500
+ paddingBottom: 2,
501
+ alignItems: "center",
502
+ gap: 2,
503
+ hasRadius: true,
504
+ width: "100%",
505
+ children: [
506
+ /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
507
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
508
+ id: "content-releases.header.actions.edit",
509
+ defaultMessage: "Edit"
510
+ }) })
511
+ ]
512
+ }
513
+ ) }),
514
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxRuntime.jsxs(
515
+ designSystem.Flex,
516
+ {
517
+ paddingTop: 2,
518
+ paddingBottom: 2,
519
+ alignItems: "center",
520
+ gap: 2,
521
+ hasRadius: true,
522
+ width: "100%",
523
+ children: [
524
+ /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
525
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
526
+ id: "content-releases.header.actions.delete",
527
+ defaultMessage: "Delete"
528
+ }) })
529
+ ]
530
+ }
531
+ ) })
532
+ ]
533
+ }
534
+ ),
535
+ /* @__PURE__ */ jsxRuntime.jsxs(
536
+ ReleaseInfoWrapper,
537
+ {
538
+ direction: "column",
539
+ justifyContent: "center",
540
+ alignItems: "flex-start",
541
+ gap: 1,
542
+ padding: 5,
543
+ children: [
544
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
545
+ id: "content-releases.header.actions.created",
546
+ defaultMessage: "Created"
547
+ }) }),
548
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", color: "neutral300", children: [
549
+ /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(release.createdAt) }),
550
+ formatMessage(
551
+ {
552
+ id: "content-releases.header.actions.created.description",
553
+ defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
554
+ },
555
+ { createdBy: getCreatedByUser(), hasCreatedByUser }
556
+ )
557
+ ] })
558
+ ]
559
+ }
560
+ )
561
+ ] })
562
+ ] }),
370
563
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "S", variant: "tertiary", onClick: handleRefresh, children: formatMessage({
371
564
  id: "content-releases.header.actions.refresh",
372
565
  defaultMessage: "Refresh"
@@ -422,6 +615,9 @@ const ReleaseDetailsBody = () => {
422
615
  isError: isReleaseError,
423
616
  error: releaseError
424
617
  } = index.useGetReleaseQuery({ id: releaseId });
618
+ const {
619
+ allowedActions: { canUpdate }
620
+ } = helperPlugin.useRBAC(index.PERMISSIONS);
425
621
  const release = releaseData?.data;
426
622
  const selectedGroupBy = query?.groupBy || "contentType";
427
623
  const {
@@ -435,7 +631,7 @@ const ReleaseDetailsBody = () => {
435
631
  releaseId
436
632
  });
437
633
  const [updateReleaseAction] = index.useUpdateReleaseActionMutation();
438
- const handleChangeType = async (e, actionId) => {
634
+ const handleChangeType = async (e, actionId, actionPath) => {
439
635
  const response = await updateReleaseAction({
440
636
  params: {
441
637
  releaseId,
@@ -443,7 +639,11 @@ const ReleaseDetailsBody = () => {
443
639
  },
444
640
  body: {
445
641
  type: e.target.value
446
- }
642
+ },
643
+ query,
644
+ // We are passing the query params to make optimistic updates
645
+ actionPath
646
+ // We are passing the action path to found the position in the cache of the action for optimistic updates
447
647
  });
448
648
  if ("error" in response) {
449
649
  if (index.isAxiosError(response.error)) {
@@ -524,7 +724,7 @@ const ReleaseDetailsBody = () => {
524
724
  designSystem.SingleSelect,
525
725
  {
526
726
  "aria-label": formatMessage({
527
- id: "content-releases.pages.ReleaseDetails.groupBy.label",
727
+ id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
528
728
  defaultMessage: "Group by"
529
729
  }),
530
730
  customizeContent: (value) => formatMessage(
@@ -542,7 +742,7 @@ const ReleaseDetailsBody = () => {
542
742
  }
543
743
  ) }),
544
744
  Object.keys(releaseActions).map((key) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, direction: "column", alignItems: "stretch", children: [
545
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: key }) }),
745
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { role: "separator", "aria-label": key, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: key }) }),
546
746
  /* @__PURE__ */ jsxRuntime.jsx(
547
747
  helperPlugin.Table.Root,
548
748
  {
@@ -612,56 +812,59 @@ const ReleaseDetailsBody = () => {
612
812
  )
613
813
  ] }),
614
814
  /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.Table.LoadingBody, {}),
615
- /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.Table.Body, { children: releaseActions[key].map(({ id, contentType, locale, type, entry }) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
616
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "25%", maxWidth: "200px", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: `${contentType.mainFieldValue || entry.id}` }) }),
617
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "10%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
618
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "10%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: contentType.displayName || "" }) }),
619
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "20%", children: release.releasedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage(
620
- {
621
- id: "content-releases.page.ReleaseDetails.table.action-published",
622
- defaultMessage: "This entry was <b>{isPublish, select, true {published} other {unpublished}}</b>."
623
- },
624
- {
625
- isPublish: type === "publish",
626
- b: (children) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children })
627
- }
628
- ) }) : /* @__PURE__ */ jsxRuntime.jsx(
629
- index.ReleaseActionOptions,
630
- {
631
- selected: type,
632
- handleChange: (e) => handleChangeType(e, id),
633
- name: `release-action-${id}-type`
634
- }
635
- ) }),
636
- !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
637
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "20%", minWidth: "200px", children: /* @__PURE__ */ jsxRuntime.jsx(
638
- EntryValidationText,
815
+ /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.Table.Body, { children: releaseActions[key].map(
816
+ ({ id, contentType, locale, type, entry }, actionIndex) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
817
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "25%", maxWidth: "200px", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: `${contentType.mainFieldValue || entry.id}` }) }),
818
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "10%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
819
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "10%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: contentType.displayName || "" }) }),
820
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "20%", children: release.releasedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage(
821
+ {
822
+ id: "content-releases.page.ReleaseDetails.table.action-published",
823
+ defaultMessage: "This entry was <b>{isPublish, select, true {published} other {unpublished}}</b>."
824
+ },
639
825
  {
640
- action: type,
641
- schema: contentTypes?.[contentType.uid],
642
- components,
643
- entry
826
+ isPublish: type === "publish",
827
+ b: (children) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children })
828
+ }
829
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(
830
+ index.ReleaseActionOptions,
831
+ {
832
+ selected: type,
833
+ handleChange: (e) => handleChangeType(e, id, [key, actionIndex]),
834
+ name: `release-action-${id}-type`,
835
+ disabled: !canUpdate
644
836
  }
645
837
  ) }),
646
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxRuntime.jsxs(index.ReleaseActionMenu.Root, { children: [
647
- /* @__PURE__ */ jsxRuntime.jsx(
648
- index.ReleaseActionMenu.ReleaseActionEntryLinkItem,
649
- {
650
- contentTypeUid: contentType.uid,
651
- entryId: entry.id,
652
- locale: locale?.code
653
- }
654
- ),
655
- /* @__PURE__ */ jsxRuntime.jsx(
656
- index.ReleaseActionMenu.DeleteReleaseActionItem,
838
+ !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
839
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "20%", minWidth: "200px", children: /* @__PURE__ */ jsxRuntime.jsx(
840
+ EntryValidationText,
657
841
  {
658
- releaseId: release.id,
659
- actionId: id
842
+ action: type,
843
+ schema: contentTypes?.[contentType.uid],
844
+ components,
845
+ entry
660
846
  }
661
- )
662
- ] }) }) })
663
- ] })
664
- ] }, id)) })
847
+ ) }),
848
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxRuntime.jsxs(index.ReleaseActionMenu.Root, { children: [
849
+ /* @__PURE__ */ jsxRuntime.jsx(
850
+ index.ReleaseActionMenu.ReleaseActionEntryLinkItem,
851
+ {
852
+ contentTypeUid: contentType.uid,
853
+ entryId: entry.id,
854
+ locale: locale?.code
855
+ }
856
+ ),
857
+ /* @__PURE__ */ jsxRuntime.jsx(
858
+ index.ReleaseActionMenu.DeleteReleaseActionItem,
859
+ {
860
+ releaseId: release.id,
861
+ actionId: id
862
+ }
863
+ )
864
+ ] }) }) })
865
+ ] })
866
+ ] }, id)
867
+ ) })
665
868
  ] })
666
869
  }
667
870
  )
@@ -708,11 +911,18 @@ const ReleaseDetailsPage = () => {
708
911
  }
709
912
  );
710
913
  }
711
- const title = isSuccessDetails && data?.data?.name || "";
914
+ const releaseData = isSuccessDetails && data?.data || null;
915
+ const title = releaseData?.name || "";
916
+ const timezone = releaseData?.timezone ?? null;
917
+ const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
918
+ const date = scheduledAt ? new Date(format__default.default(scheduledAt, "yyyy-MM-dd")) : null;
919
+ const time = scheduledAt ? format__default.default(scheduledAt, "HH:mm") : "";
712
920
  const handleEditRelease = async (values) => {
713
921
  const response = await updateRelease({
714
922
  id: releaseId,
715
- name: values.name
923
+ name: values.name,
924
+ scheduledAt: values.scheduledAt,
925
+ timezone: values.timezone
716
926
  });
717
927
  if ("data" in response) {
718
928
  toggleNotification({
@@ -766,7 +976,14 @@ const ReleaseDetailsPage = () => {
766
976
  handleClose: toggleEditReleaseModal,
767
977
  handleSubmit: handleEditRelease,
768
978
  isLoading: isLoadingDetails || isSubmittingForm,
769
- initialValues: { name: title || "" }
979
+ initialValues: {
980
+ name: title || "",
981
+ scheduledAt,
982
+ date,
983
+ time,
984
+ isScheduled: Boolean(scheduledAt),
985
+ timezone
986
+ }
770
987
  }
771
988
  ),
772
989
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -786,37 +1003,6 @@ const ReleaseDetailsPage = () => {
786
1003
  }
787
1004
  );
788
1005
  };
789
- const ReleasesLayout = ({
790
- isLoading,
791
- totalReleases,
792
- onClickAddRelease,
793
- children
794
- }) => {
795
- const { formatMessage } = reactIntl.useIntl();
796
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
797
- /* @__PURE__ */ jsxRuntime.jsx(
798
- designSystem.HeaderLayout,
799
- {
800
- title: formatMessage({
801
- id: "content-releases.pages.Releases.title",
802
- defaultMessage: "Releases"
803
- }),
804
- subtitle: !isLoading && formatMessage(
805
- {
806
- id: "content-releases.pages.Releases.header-subtitle",
807
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
808
- },
809
- { number: totalReleases }
810
- ),
811
- primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}), onClick: onClickAddRelease, children: formatMessage({
812
- id: "content-releases.header.actions.add-release",
813
- defaultMessage: "New release"
814
- }) }) })
815
- }
816
- ),
817
- children
818
- ] });
819
- };
820
1006
  const LinkCard = styled__default.default(v2.Link)`
821
1007
  display: block;
822
1008
  `;
@@ -868,8 +1054,22 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
868
1054
  }
869
1055
  ) }) }, id)) });
870
1056
  };
1057
+ const StyledAlert = styled__default.default(designSystem.Alert)`
1058
+ button {
1059
+ display: none;
1060
+ }
1061
+ p + div {
1062
+ margin-left: auto;
1063
+ }
1064
+ `;
871
1065
  const INITIAL_FORM_VALUES = {
872
- name: ""
1066
+ name: "",
1067
+ date: null,
1068
+ time: "",
1069
+ // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
1070
+ isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
1071
+ scheduledAt: null,
1072
+ timezone: null
873
1073
  };
874
1074
  const ReleasesPage = () => {
875
1075
  const tabRef = React__namespace.useRef(null);
@@ -882,6 +1082,9 @@ const ReleasesPage = () => {
882
1082
  const [{ query }, setQuery] = helperPlugin.useQueryParams();
883
1083
  const response = index.useGetReleasesQuery(query);
884
1084
  const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
1085
+ const { getFeature } = strapiAdmin.useLicenseLimits();
1086
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
1087
+ const { trackUsage } = helperPlugin.useTracking();
885
1088
  const { isLoading, isSuccess, isError } = response;
886
1089
  const activeTab = response?.currentData?.meta?.activeTab || "pending";
887
1090
  const activeTabIndex = ["pending", "done"].indexOf(activeTab);
@@ -910,9 +1113,10 @@ const ReleasesPage = () => {
910
1113
  setReleaseModalShown((prev) => !prev);
911
1114
  };
912
1115
  if (isLoading) {
913
- return /* @__PURE__ */ jsxRuntime.jsx(ReleasesLayout, { onClickAddRelease: toggleAddReleaseModal, isLoading: true, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) }) });
1116
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
914
1117
  }
915
1118
  const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
1119
+ const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
916
1120
  const handleTabChange = (index2) => {
917
1121
  setQuery({
918
1122
  ...query,
@@ -925,9 +1129,11 @@ const ReleasesPage = () => {
925
1129
  }
926
1130
  });
927
1131
  };
928
- const handleAddRelease = async (values) => {
1132
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
929
1133
  const response2 = await createRelease({
930
- name: values.name
1134
+ name,
1135
+ scheduledAt,
1136
+ timezone
931
1137
  });
932
1138
  if ("data" in response2) {
933
1139
  toggleNotification({
@@ -937,6 +1143,7 @@ const ReleasesPage = () => {
937
1143
  defaultMessage: "Release created."
938
1144
  })
939
1145
  });
1146
+ trackUsage("didCreateRelease");
940
1147
  push(`/plugins/content-releases/${response2.data.data.id}`);
941
1148
  } else if (index.isAxiosError(response2.error)) {
942
1149
  toggleNotification({
@@ -950,8 +1157,60 @@ const ReleasesPage = () => {
950
1157
  });
951
1158
  }
952
1159
  };
953
- return /* @__PURE__ */ jsxRuntime.jsxs(ReleasesLayout, { onClickAddRelease: toggleAddReleaseModal, totalReleases, children: [
1160
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
1161
+ /* @__PURE__ */ jsxRuntime.jsx(
1162
+ designSystem.HeaderLayout,
1163
+ {
1164
+ title: formatMessage({
1165
+ id: "content-releases.pages.Releases.title",
1166
+ defaultMessage: "Releases"
1167
+ }),
1168
+ subtitle: formatMessage(
1169
+ {
1170
+ id: "content-releases.pages.Releases.header-subtitle",
1171
+ defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
1172
+ },
1173
+ { number: totalReleases }
1174
+ ),
1175
+ primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
1176
+ designSystem.Button,
1177
+ {
1178
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1179
+ onClick: toggleAddReleaseModal,
1180
+ disabled: hasReachedMaximumPendingReleases,
1181
+ children: formatMessage({
1182
+ id: "content-releases.header.actions.add-release",
1183
+ defaultMessage: "New release"
1184
+ })
1185
+ }
1186
+ ) })
1187
+ }
1188
+ ),
954
1189
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1190
+ activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
1191
+ StyledAlert,
1192
+ {
1193
+ marginBottom: 6,
1194
+ action: /* @__PURE__ */ jsxRuntime.jsx(v2.Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
1195
+ id: "content-releases.pages.Releases.max-limit-reached.action",
1196
+ defaultMessage: "Explore plans"
1197
+ }) }),
1198
+ title: formatMessage(
1199
+ {
1200
+ id: "content-releases.pages.Releases.max-limit-reached.title",
1201
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
1202
+ },
1203
+ { number: maximumReleases }
1204
+ ),
1205
+ onClose: () => {
1206
+ },
1207
+ closeLabel: "",
1208
+ children: formatMessage({
1209
+ id: "content-releases.pages.Releases.max-limit-reached.message",
1210
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
1211
+ })
1212
+ }
1213
+ ),
955
1214
  /* @__PURE__ */ jsxRuntime.jsxs(
956
1215
  designSystem.TabGroup,
957
1216
  {
@@ -1034,4 +1293,4 @@ const App = () => {
1034
1293
  ] }) });
1035
1294
  };
1036
1295
  exports.App = App;
1037
- //# sourceMappingURL=App-_20W9dYa.js.map
1296
+ //# sourceMappingURL=App-w2Zq-wj5.js.map