@strapi/content-releases 0.0.0-next.6d59515520a3850456f256fb0e4c54b75054ddf4 → 0.0.0-next.898f8ae81b2cb3f89bd012e9db20a2d9b78a48d2

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-_20W9dYa.js → App-OK4Xac-O.js} +518 -240
  2. package/dist/_chunks/App-OK4Xac-O.js.map +1 -0
  3. package/dist/_chunks/{App-L1jSxCiL.mjs → App-xAkiD42p.mjs} +522 -245
  4. package/dist/_chunks/App-xAkiD42p.mjs.map +1 -0
  5. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs +51 -0
  6. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +1 -0
  7. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js +51 -0
  8. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +1 -0
  9. package/dist/_chunks/{en-gYDqKYFd.js → en-r0otWaln.js} +18 -4
  10. package/dist/_chunks/en-r0otWaln.js.map +1 -0
  11. package/dist/_chunks/{en-MyLPoISH.mjs → en-veqvqeEr.mjs} +18 -4
  12. package/dist/_chunks/en-veqvqeEr.mjs.map +1 -0
  13. package/dist/_chunks/{index-KJa1Rb5F.js → index-JvA2_26n.js} +134 -27
  14. package/dist/_chunks/index-JvA2_26n.js.map +1 -0
  15. package/dist/_chunks/{index-c4zRX_sg.mjs → index-exoiSU3V.mjs} +139 -32
  16. package/dist/_chunks/index-exoiSU3V.mjs.map +1 -0
  17. package/dist/admin/index.js +1 -1
  18. package/dist/admin/index.mjs +2 -2
  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-JvA2_26n.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(designSystem.Box, { width: "max-content", children: /* @__PURE__ */ jsxRuntime.jsx(
135
+ designSystem.Checkbox,
136
+ {
137
+ name: "isScheduled",
138
+ value: values.isScheduled,
139
+ onChange: (event) => {
140
+ setFieldValue("isScheduled", event.target.checked);
141
+ if (!event.target.checked) {
142
+ setFieldValue("date", null);
143
+ setFieldValue("time", "");
144
+ setFieldValue("timezone", null);
145
+ } else {
146
+ setFieldValue("date", initialValues.date);
147
+ setFieldValue("time", initialValues.time);
148
+ setFieldValue(
149
+ "timezone",
150
+ initialValues.timezone ?? systemTimezone?.value
151
+ );
152
+ }
153
+ },
154
+ children: /* @__PURE__ */ jsxRuntime.jsx(
155
+ designSystem.Typography,
156
+ {
157
+ textColor: values.isScheduled ? "primary600" : "neutral800",
158
+ fontWeight: values.isScheduled ? "semiBold" : "regular",
159
+ children: formatMessage({
160
+ id: "modal.form.input.label.schedule-release",
161
+ defaultMessage: "Schedule release"
162
+ })
163
+ }
164
+ )
165
+ }
166
+ ) }),
167
+ values.isScheduled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
168
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "start", children: [
169
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
170
+ designSystem.DatePicker,
171
+ {
172
+ label: formatMessage({
173
+ id: "content-releases.modal.form.input.label.date",
174
+ defaultMessage: "Date"
175
+ }),
176
+ name: "date",
177
+ error: errors.date,
178
+ onChange: (date) => {
179
+ const isoFormatDate = date ? dateFns.formatISO(date, { representation: "date" }) : null;
180
+ setFieldValue("date", isoFormatDate);
181
+ },
182
+ clearLabel: formatMessage({
183
+ id: "content-releases.modal.form.input.clearLabel",
184
+ defaultMessage: "Clear"
185
+ }),
186
+ onClear: () => {
187
+ setFieldValue("date", null);
188
+ },
189
+ selectedDate: values.date || void 0,
190
+ required: true
191
+ }
192
+ ) }),
193
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
194
+ designSystem.TimePicker,
195
+ {
196
+ label: formatMessage({
197
+ id: "content-releases.modal.form.input.label.time",
198
+ defaultMessage: "Time"
199
+ }),
200
+ name: "time",
201
+ error: errors.time,
202
+ onChange: (time) => {
203
+ setFieldValue("time", time);
204
+ },
205
+ clearLabel: formatMessage({
206
+ id: "content-releases.modal.form.input.clearLabel",
207
+ defaultMessage: "Clear"
208
+ }),
209
+ onClear: () => {
210
+ setFieldValue("time", "");
211
+ },
212
+ value: values.time || void 0,
213
+ required: true
214
+ }
215
+ ) })
216
+ ] }),
217
+ /* @__PURE__ */ jsxRuntime.jsx(TimezoneComponent, { timezoneOptions: timezoneList })
218
+ ] })
219
+ ] })
220
+ ] }) }),
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,59 @@ const ReleaseModal = ({
108
236
  )
109
237
  ] });
110
238
  };
239
+ const getTimezones = (selectedDate) => {
240
+ const timezoneList = Intl.supportedValuesOf("timeZone").map((timezone) => {
241
+ const utcOffset = index.getTimezoneOffset(timezone, selectedDate);
242
+ return { offset: utcOffset, value: `${utcOffset}_${timezone}` };
243
+ });
244
+ const systemTimezone = timezoneList.find(
245
+ (timezone) => timezone.value.split("_")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
246
+ );
247
+ return { timezoneList, systemTimezone };
248
+ };
249
+ const TimezoneComponent = ({ timezoneOptions }) => {
250
+ const { values, errors, setFieldValue } = formik.useFormikContext();
251
+ const { formatMessage } = reactIntl.useIntl();
252
+ const [timezoneList, setTimezoneList] = React__namespace.useState(timezoneOptions);
253
+ React__namespace.useEffect(() => {
254
+ if (values.date) {
255
+ const { timezoneList: timezoneList2 } = getTimezones(new Date(values.date));
256
+ setTimezoneList(timezoneList2);
257
+ const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("_")[1] === values.timezone.split("_")[1]);
258
+ if (updatedTimezone) {
259
+ setFieldValue("timezone", updatedTimezone.value);
260
+ }
261
+ }
262
+ }, [setFieldValue, values.date, values.timezone]);
263
+ return /* @__PURE__ */ jsxRuntime.jsx(
264
+ designSystem.Combobox,
265
+ {
266
+ label: formatMessage({
267
+ id: "content-releases.modal.form.input.label.timezone",
268
+ defaultMessage: "Timezone"
269
+ }),
270
+ name: "timezone",
271
+ value: values.timezone || void 0,
272
+ textValue: values.timezone ? values.timezone.replace("_", " ") : void 0,
273
+ onChange: (timezone) => {
274
+ setFieldValue("timezone", timezone);
275
+ },
276
+ onClear: () => {
277
+ setFieldValue("timezone", "");
278
+ },
279
+ error: errors.timezone,
280
+ required: true,
281
+ children: timezoneList.map((timezone) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: timezone.value, children: timezone.value.replace("_", " ") }, timezone.value))
282
+ }
283
+ );
284
+ };
111
285
  const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
112
286
  align-self: stretch;
113
287
  border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
114
288
  border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
115
289
  border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
116
290
  `;
117
- const StyledFlex = styled__default.default(designSystem.Flex)`
118
- align-self: stretch;
119
- cursor: ${({ disabled }) => disabled ? "not-allowed" : "pointer"};
120
-
291
+ const StyledMenuItem = styled__default.default(v2.Menu.Item)`
121
292
  svg path {
122
293
  fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
123
294
  }
@@ -126,15 +297,15 @@ const StyledFlex = styled__default.default(designSystem.Flex)`
126
297
  }
127
298
  `;
128
299
  const PencilIcon = styled__default.default(icons.Pencil)`
129
- width: ${({ theme }) => theme.spaces[4]};
130
- height: ${({ theme }) => theme.spaces[4]};
300
+ width: ${({ theme }) => theme.spaces[3]};
301
+ height: ${({ theme }) => theme.spaces[3]};
131
302
  path {
132
303
  fill: ${({ theme }) => theme.colors.neutral600};
133
304
  }
134
305
  `;
135
306
  const TrashIcon = styled__default.default(icons.Trash)`
136
- width: ${({ theme }) => theme.spaces[4]};
137
- height: ${({ theme }) => theme.spaces[4]};
307
+ width: ${({ theme }) => theme.spaces[3]};
308
+ height: ${({ theme }) => theme.spaces[3]};
138
309
  path {
139
310
  fill: ${({ theme }) => theme.colors.danger600};
140
311
  }
@@ -142,24 +313,6 @@ const TrashIcon = styled__default.default(icons.Trash)`
142
313
  const TypographyMaxWidth = styled__default.default(designSystem.Typography)`
143
314
  max-width: 300px;
144
315
  `;
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
316
  const EntryValidationText = ({ action, schema, components, entry }) => {
164
317
  const { formatMessage } = reactIntl.useIntl();
165
318
  const { validate } = strapiAdmin.unstable_useDocument();
@@ -208,10 +361,8 @@ const ReleaseDetailsLayout = ({
208
361
  toggleWarningSubmit,
209
362
  children
210
363
  }) => {
211
- const { formatMessage } = reactIntl.useIntl();
364
+ const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
212
365
  const { releaseId } = reactRouterDom.useParams();
213
- const [isPopoverVisible, setIsPopoverVisible] = React__namespace.useState(false);
214
- const moreButtonRef = React__namespace.useRef(null);
215
366
  const {
216
367
  data,
217
368
  isLoading: isLoadingDetails,
@@ -225,14 +376,8 @@ const ReleaseDetailsLayout = ({
225
376
  allowedActions: { canUpdate, canDelete }
226
377
  } = helperPlugin.useRBAC(index.PERMISSIONS);
227
378
  const dispatch = index.useTypedDispatch();
379
+ const { trackUsage } = helperPlugin.useTracking();
228
380
  const release = data?.data;
229
- const handleTogglePopover = () => {
230
- setIsPopoverVisible((prev) => !prev);
231
- };
232
- const openReleaseModal = () => {
233
- toggleEditReleaseModal();
234
- handleTogglePopover();
235
- };
236
381
  const handlePublishRelease = async () => {
237
382
  const response = await publishRelease({ id: releaseId });
238
383
  if ("data" in response) {
@@ -243,6 +388,12 @@ const ReleaseDetailsLayout = ({
243
388
  defaultMessage: "Release was published successfully."
244
389
  })
245
390
  });
391
+ const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
392
+ trackUsage("didPublishRelease", {
393
+ totalEntries: totalEntries2,
394
+ totalPublishedEntries,
395
+ totalUnpublishedEntries
396
+ });
246
397
  } else if (index.isAxiosError(response.error)) {
247
398
  toggleNotification({
248
399
  type: "warning",
@@ -255,13 +406,21 @@ const ReleaseDetailsLayout = ({
255
406
  });
256
407
  }
257
408
  };
258
- const openWarningConfirmDialog = () => {
259
- toggleWarningSubmit();
260
- handleTogglePopover();
261
- };
262
409
  const handleRefresh = () => {
263
410
  dispatch(index.releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
264
411
  };
412
+ const getCreatedByUser = () => {
413
+ if (!release?.createdBy) {
414
+ return null;
415
+ }
416
+ if (release.createdBy.username) {
417
+ return release.createdBy.username;
418
+ }
419
+ if (release.createdBy.firstname) {
420
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
421
+ }
422
+ return release.createdBy.email;
423
+ };
265
424
  if (isLoadingDetails) {
266
425
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
267
426
  }
@@ -283,90 +442,139 @@ const ReleaseDetailsLayout = ({
283
442
  );
284
443
  }
285
444
  const totalEntries = release.actions.meta.count || 0;
286
- const createdBy = release.createdBy.lastname ? `${release.createdBy.firstname} ${release.createdBy.lastname}` : `${release.createdBy.firstname}`;
445
+ const hasCreatedByUser = Boolean(getCreatedByUser());
446
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
447
+ const isScheduled = release.scheduledAt && release.timezone;
448
+ const numberOfEntriesText = formatMessage(
449
+ {
450
+ id: "content-releases.pages.Details.header-subtitle",
451
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
452
+ },
453
+ { number: totalEntries }
454
+ );
455
+ const scheduledText = isScheduled ? formatMessage(
456
+ {
457
+ id: "content-releases.pages.ReleaseDetails.header-subtitle.scheduled",
458
+ defaultMessage: "Scheduled for {date} at {time} ({offset})"
459
+ },
460
+ {
461
+ date: formatDate(new Date(release.scheduledAt), {
462
+ weekday: "long",
463
+ day: "numeric",
464
+ month: "long",
465
+ year: "numeric",
466
+ timeZone: release.timezone
467
+ }),
468
+ time: formatTime(new Date(release.scheduledAt), {
469
+ timeZone: release.timezone,
470
+ hourCycle: "h23"
471
+ }),
472
+ offset: index.getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
473
+ }
474
+ ) : "";
287
475
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoadingDetails, children: [
288
476
  /* @__PURE__ */ jsxRuntime.jsx(
289
477
  designSystem.HeaderLayout,
290
478
  {
291
479
  title: release.name,
292
- subtitle: formatMessage(
293
- {
294
- id: "content-releases.pages.Details.header-subtitle",
295
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
296
- },
297
- { number: totalEntries }
298
- ),
480
+ subtitle: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? ` - ${scheduledText}` : ""),
299
481
  navigationAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
300
482
  id: "global.back",
301
483
  defaultMessage: "Back"
302
484
  }) }),
303
485
  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
- ),
486
+ /* @__PURE__ */ jsxRuntime.jsxs(v2.Menu.Root, { children: [
487
+ /* @__PURE__ */ jsxRuntime.jsx(
488
+ v2.Menu.Trigger,
489
+ {
490
+ as: designSystem.IconButton,
491
+ paddingLeft: 2,
492
+ paddingRight: 2,
493
+ "aria-label": formatMessage({
494
+ id: "content-releases.header.actions.open-release-actions",
495
+ defaultMessage: "Release edit and delete menu"
496
+ }),
497
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.More, {}),
498
+ variant: "tertiary"
499
+ }
500
+ ),
501
+ /* @__PURE__ */ jsxRuntime.jsxs(v2.Menu.Content, { top: 1, popoverPlacement: "bottom-end", children: [
502
+ /* @__PURE__ */ jsxRuntime.jsxs(
503
+ designSystem.Flex,
504
+ {
505
+ alignItems: "center",
506
+ justifyContent: "center",
507
+ direction: "column",
508
+ padding: 1,
509
+ width: "100%",
510
+ children: [
511
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(
512
+ designSystem.Flex,
513
+ {
514
+ paddingTop: 2,
515
+ paddingBottom: 2,
516
+ alignItems: "center",
517
+ gap: 2,
518
+ hasRadius: true,
519
+ width: "100%",
520
+ children: [
521
+ /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
522
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
523
+ id: "content-releases.header.actions.edit",
524
+ defaultMessage: "Edit"
525
+ }) })
526
+ ]
527
+ }
528
+ ) }),
529
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxRuntime.jsxs(
530
+ designSystem.Flex,
531
+ {
532
+ paddingTop: 2,
533
+ paddingBottom: 2,
534
+ alignItems: "center",
535
+ gap: 2,
536
+ hasRadius: true,
537
+ width: "100%",
538
+ children: [
539
+ /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
540
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
541
+ id: "content-releases.header.actions.delete",
542
+ defaultMessage: "Delete"
543
+ }) })
544
+ ]
545
+ }
546
+ ) })
547
+ ]
548
+ }
549
+ ),
550
+ /* @__PURE__ */ jsxRuntime.jsxs(
551
+ ReleaseInfoWrapper,
552
+ {
553
+ direction: "column",
554
+ justifyContent: "center",
555
+ alignItems: "flex-start",
556
+ gap: 1,
557
+ padding: 5,
558
+ children: [
559
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
560
+ id: "content-releases.header.actions.created",
561
+ defaultMessage: "Created"
562
+ }) }),
563
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", color: "neutral300", children: [
564
+ /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(release.createdAt) }),
565
+ formatMessage(
566
+ {
567
+ id: "content-releases.header.actions.created.description",
568
+ defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
569
+ },
570
+ { createdBy: getCreatedByUser(), hasCreatedByUser }
571
+ )
572
+ ] })
573
+ ]
574
+ }
575
+ )
576
+ ] })
577
+ ] }),
370
578
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "S", variant: "tertiary", onClick: handleRefresh, children: formatMessage({
371
579
  id: "content-releases.header.actions.refresh",
372
580
  defaultMessage: "Refresh"
@@ -422,6 +630,9 @@ const ReleaseDetailsBody = () => {
422
630
  isError: isReleaseError,
423
631
  error: releaseError
424
632
  } = index.useGetReleaseQuery({ id: releaseId });
633
+ const {
634
+ allowedActions: { canUpdate }
635
+ } = helperPlugin.useRBAC(index.PERMISSIONS);
425
636
  const release = releaseData?.data;
426
637
  const selectedGroupBy = query?.groupBy || "contentType";
427
638
  const {
@@ -435,7 +646,7 @@ const ReleaseDetailsBody = () => {
435
646
  releaseId
436
647
  });
437
648
  const [updateReleaseAction] = index.useUpdateReleaseActionMutation();
438
- const handleChangeType = async (e, actionId) => {
649
+ const handleChangeType = async (e, actionId, actionPath) => {
439
650
  const response = await updateReleaseAction({
440
651
  params: {
441
652
  releaseId,
@@ -443,7 +654,11 @@ const ReleaseDetailsBody = () => {
443
654
  },
444
655
  body: {
445
656
  type: e.target.value
446
- }
657
+ },
658
+ query,
659
+ // We are passing the query params to make optimistic updates
660
+ actionPath
661
+ // We are passing the action path to found the position in the cache of the action for optimistic updates
447
662
  });
448
663
  if ("error" in response) {
449
664
  if (index.isAxiosError(response.error)) {
@@ -524,7 +739,7 @@ const ReleaseDetailsBody = () => {
524
739
  designSystem.SingleSelect,
525
740
  {
526
741
  "aria-label": formatMessage({
527
- id: "content-releases.pages.ReleaseDetails.groupBy.label",
742
+ id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
528
743
  defaultMessage: "Group by"
529
744
  }),
530
745
  customizeContent: (value) => formatMessage(
@@ -542,7 +757,7 @@ const ReleaseDetailsBody = () => {
542
757
  }
543
758
  ) }),
544
759
  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 }) }),
760
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { role: "separator", "aria-label": key, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: key }) }),
546
761
  /* @__PURE__ */ jsxRuntime.jsx(
547
762
  helperPlugin.Table.Root,
548
763
  {
@@ -612,56 +827,59 @@ const ReleaseDetailsBody = () => {
612
827
  )
613
828
  ] }),
614
829
  /* @__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,
830
+ /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.Table.Body, { children: releaseActions[key].map(
831
+ ({ id, contentType, locale, type, entry }, actionIndex) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
832
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "25%", maxWidth: "200px", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: `${contentType.mainFieldValue || entry.id}` }) }),
833
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "10%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
834
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "10%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: contentType.displayName || "" }) }),
835
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "20%", children: release.releasedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage(
836
+ {
837
+ id: "content-releases.page.ReleaseDetails.table.action-published",
838
+ defaultMessage: "This entry was <b>{isPublish, select, true {published} other {unpublished}}</b>."
839
+ },
639
840
  {
640
- action: type,
641
- schema: contentTypes?.[contentType.uid],
642
- components,
643
- entry
841
+ isPublish: type === "publish",
842
+ b: (children) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children })
843
+ }
844
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(
845
+ index.ReleaseActionOptions,
846
+ {
847
+ selected: type,
848
+ handleChange: (e) => handleChangeType(e, id, [key, actionIndex]),
849
+ name: `release-action-${id}-type`,
850
+ disabled: !canUpdate
644
851
  }
645
852
  ) }),
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,
853
+ !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
854
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "20%", minWidth: "200px", children: /* @__PURE__ */ jsxRuntime.jsx(
855
+ EntryValidationText,
657
856
  {
658
- releaseId: release.id,
659
- actionId: id
857
+ action: type,
858
+ schema: contentTypes?.[contentType.uid],
859
+ components,
860
+ entry
660
861
  }
661
- )
662
- ] }) }) })
663
- ] })
664
- ] }, id)) })
862
+ ) }),
863
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxRuntime.jsxs(index.ReleaseActionMenu.Root, { children: [
864
+ /* @__PURE__ */ jsxRuntime.jsx(
865
+ index.ReleaseActionMenu.ReleaseActionEntryLinkItem,
866
+ {
867
+ contentTypeUid: contentType.uid,
868
+ entryId: entry.id,
869
+ locale: locale?.code
870
+ }
871
+ ),
872
+ /* @__PURE__ */ jsxRuntime.jsx(
873
+ index.ReleaseActionMenu.DeleteReleaseActionItem,
874
+ {
875
+ releaseId: release.id,
876
+ actionId: id
877
+ }
878
+ )
879
+ ] }) }) })
880
+ ] })
881
+ ] }, id)
882
+ ) })
665
883
  ] })
666
884
  }
667
885
  )
@@ -708,11 +926,18 @@ const ReleaseDetailsPage = () => {
708
926
  }
709
927
  );
710
928
  }
711
- const title = isSuccessDetails && data?.data?.name || "";
929
+ const releaseData = isSuccessDetails && data?.data || null;
930
+ const title = releaseData?.name || "";
931
+ const timezone = releaseData?.timezone ?? null;
932
+ const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
933
+ const date = scheduledAt ? new Date(format__default.default(scheduledAt, "yyyy-MM-dd")) : null;
934
+ const time = scheduledAt ? format__default.default(scheduledAt, "HH:mm") : "";
712
935
  const handleEditRelease = async (values) => {
713
936
  const response = await updateRelease({
714
937
  id: releaseId,
715
- name: values.name
938
+ name: values.name,
939
+ scheduledAt: values.scheduledAt,
940
+ timezone: values.timezone
716
941
  });
717
942
  if ("data" in response) {
718
943
  toggleNotification({
@@ -766,7 +991,14 @@ const ReleaseDetailsPage = () => {
766
991
  handleClose: toggleEditReleaseModal,
767
992
  handleSubmit: handleEditRelease,
768
993
  isLoading: isLoadingDetails || isSubmittingForm,
769
- initialValues: { name: title || "" }
994
+ initialValues: {
995
+ name: title || "",
996
+ scheduledAt,
997
+ date,
998
+ time,
999
+ isScheduled: Boolean(scheduledAt),
1000
+ timezone
1001
+ }
770
1002
  }
771
1003
  ),
772
1004
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -786,42 +1018,12 @@ const ReleaseDetailsPage = () => {
786
1018
  }
787
1019
  );
788
1020
  };
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
1021
  const LinkCard = styled__default.default(v2.Link)`
821
1022
  display: block;
822
1023
  `;
823
1024
  const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
824
1025
  const { formatMessage } = reactIntl.useIntl();
1026
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
825
1027
  if (isError) {
826
1028
  return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
827
1029
  }
@@ -842,7 +1044,7 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
842
1044
  }
843
1045
  );
844
1046
  }
845
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid, { gap: 4, children: releases.map(({ id, name, actions }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxRuntime.jsxs(
1047
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid, { gap: 4, children: releases.map(({ id, name, actions, scheduledAt }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxRuntime.jsxs(
846
1048
  designSystem.Flex,
847
1049
  {
848
1050
  direction: "column",
@@ -857,7 +1059,10 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
857
1059
  gap: 2,
858
1060
  children: [
859
1061
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
860
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: formatMessage(
1062
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: IsSchedulingEnabled ? scheduledAt ? /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
1063
+ id: "content-releases.pages.Releases.not-scheduled",
1064
+ defaultMessage: "Not scheduled"
1065
+ }) : formatMessage(
861
1066
  {
862
1067
  id: "content-releases.page.Releases.release-item.entries",
863
1068
  defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
@@ -868,8 +1073,22 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
868
1073
  }
869
1074
  ) }) }, id)) });
870
1075
  };
1076
+ const StyledAlert = styled__default.default(designSystem.Alert)`
1077
+ button {
1078
+ display: none;
1079
+ }
1080
+ p + div {
1081
+ margin-left: auto;
1082
+ }
1083
+ `;
871
1084
  const INITIAL_FORM_VALUES = {
872
- name: ""
1085
+ name: "",
1086
+ date: null,
1087
+ time: "",
1088
+ // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
1089
+ isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
1090
+ scheduledAt: null,
1091
+ timezone: null
873
1092
  };
874
1093
  const ReleasesPage = () => {
875
1094
  const tabRef = React__namespace.useRef(null);
@@ -882,6 +1101,9 @@ const ReleasesPage = () => {
882
1101
  const [{ query }, setQuery] = helperPlugin.useQueryParams();
883
1102
  const response = index.useGetReleasesQuery(query);
884
1103
  const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
1104
+ const { getFeature } = strapiAdmin.useLicenseLimits();
1105
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
1106
+ const { trackUsage } = helperPlugin.useTracking();
885
1107
  const { isLoading, isSuccess, isError } = response;
886
1108
  const activeTab = response?.currentData?.meta?.activeTab || "pending";
887
1109
  const activeTabIndex = ["pending", "done"].indexOf(activeTab);
@@ -910,9 +1132,10 @@ const ReleasesPage = () => {
910
1132
  setReleaseModalShown((prev) => !prev);
911
1133
  };
912
1134
  if (isLoading) {
913
- return /* @__PURE__ */ jsxRuntime.jsx(ReleasesLayout, { onClickAddRelease: toggleAddReleaseModal, isLoading: true, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) }) });
1135
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
914
1136
  }
915
1137
  const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
1138
+ const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
916
1139
  const handleTabChange = (index2) => {
917
1140
  setQuery({
918
1141
  ...query,
@@ -925,9 +1148,11 @@ const ReleasesPage = () => {
925
1148
  }
926
1149
  });
927
1150
  };
928
- const handleAddRelease = async (values) => {
1151
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
929
1152
  const response2 = await createRelease({
930
- name: values.name
1153
+ name,
1154
+ scheduledAt,
1155
+ timezone
931
1156
  });
932
1157
  if ("data" in response2) {
933
1158
  toggleNotification({
@@ -937,6 +1162,7 @@ const ReleasesPage = () => {
937
1162
  defaultMessage: "Release created."
938
1163
  })
939
1164
  });
1165
+ trackUsage("didCreateRelease");
940
1166
  push(`/plugins/content-releases/${response2.data.data.id}`);
941
1167
  } else if (index.isAxiosError(response2.error)) {
942
1168
  toggleNotification({
@@ -950,8 +1176,60 @@ const ReleasesPage = () => {
950
1176
  });
951
1177
  }
952
1178
  };
953
- return /* @__PURE__ */ jsxRuntime.jsxs(ReleasesLayout, { onClickAddRelease: toggleAddReleaseModal, totalReleases, children: [
1179
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
1180
+ /* @__PURE__ */ jsxRuntime.jsx(
1181
+ designSystem.HeaderLayout,
1182
+ {
1183
+ title: formatMessage({
1184
+ id: "content-releases.pages.Releases.title",
1185
+ defaultMessage: "Releases"
1186
+ }),
1187
+ subtitle: formatMessage(
1188
+ {
1189
+ id: "content-releases.pages.Releases.header-subtitle",
1190
+ defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
1191
+ },
1192
+ { number: totalReleases }
1193
+ ),
1194
+ primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
1195
+ designSystem.Button,
1196
+ {
1197
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1198
+ onClick: toggleAddReleaseModal,
1199
+ disabled: hasReachedMaximumPendingReleases,
1200
+ children: formatMessage({
1201
+ id: "content-releases.header.actions.add-release",
1202
+ defaultMessage: "New release"
1203
+ })
1204
+ }
1205
+ ) })
1206
+ }
1207
+ ),
954
1208
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1209
+ activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
1210
+ StyledAlert,
1211
+ {
1212
+ marginBottom: 6,
1213
+ action: /* @__PURE__ */ jsxRuntime.jsx(v2.Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
1214
+ id: "content-releases.pages.Releases.max-limit-reached.action",
1215
+ defaultMessage: "Explore plans"
1216
+ }) }),
1217
+ title: formatMessage(
1218
+ {
1219
+ id: "content-releases.pages.Releases.max-limit-reached.title",
1220
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
1221
+ },
1222
+ { number: maximumReleases }
1223
+ ),
1224
+ onClose: () => {
1225
+ },
1226
+ closeLabel: "",
1227
+ children: formatMessage({
1228
+ id: "content-releases.pages.Releases.max-limit-reached.message",
1229
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
1230
+ })
1231
+ }
1232
+ ),
955
1233
  /* @__PURE__ */ jsxRuntime.jsxs(
956
1234
  designSystem.TabGroup,
957
1235
  {
@@ -1034,4 +1312,4 @@ const App = () => {
1034
1312
  ] }) });
1035
1313
  };
1036
1314
  exports.App = App;
1037
- //# sourceMappingURL=App-_20W9dYa.js.map
1315
+ //# sourceMappingURL=App-OK4Xac-O.js.map