@strapi/content-releases 0.0.0-next.6d384ed205b7f0792d9bea79195f01b30463cfa0 → 0.0.0-next.73143c28059b343ba62d98c29672ab114562fbbc

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-WkwjSaDY.mjs → App-g3vtS2Wa.mjs} +385 -174
  2. package/dist/_chunks/App-g3vtS2Wa.mjs.map +1 -0
  3. package/dist/_chunks/{App-IQIHCiSO.js → App-lnXbSPgp.js} +383 -171
  4. package/dist/_chunks/App-lnXbSPgp.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-m9eTk4UF.mjs → en-WuuhP6Bn.mjs} +15 -4
  10. package/dist/_chunks/en-WuuhP6Bn.mjs.map +1 -0
  11. package/dist/_chunks/{en-r9YocBH0.js → en-gcJJ5htG.js} +15 -4
  12. package/dist/_chunks/en-gcJJ5htG.js.map +1 -0
  13. package/dist/_chunks/{index-u_da7WHb.js → index-ItlgrLcr.js} +122 -19
  14. package/dist/_chunks/index-ItlgrLcr.js.map +1 -0
  15. package/dist/_chunks/{index-CyVlvX8h.mjs → index-uGex_IIQ.mjs} +126 -23
  16. package/dist/_chunks/index-uGex_IIQ.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 +814 -418
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +813 -418
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +13 -11
  24. package/dist/_chunks/App-IQIHCiSO.js.map +0 -1
  25. package/dist/_chunks/App-WkwjSaDY.mjs.map +0 -1
  26. package/dist/_chunks/en-m9eTk4UF.mjs.map +0 -1
  27. package/dist/_chunks/en-r9YocBH0.js.map +0 -1
  28. package/dist/_chunks/index-CyVlvX8h.mjs.map +0 -1
  29. package/dist/_chunks/index-u_da7WHb.js.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-u_da7WHb.js");
6
+ const index = require("./index-ItlgrLcr.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,12 +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
47
  name: yup__namespace.string().trim().required(),
44
- // scheduledAt is a date, but we always receive strings from the client
45
- scheduledAt: yup__namespace.string()
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
+ })
46
65
  }).required().noUnknown();
47
66
  const ReleaseModal = ({
48
67
  handleClose,
@@ -53,6 +72,24 @@ const ReleaseModal = ({
53
72
  const { formatMessage } = reactIntl.useIntl();
54
73
  const { pathname } = reactRouterDom.useLocation();
55
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
+ };
56
93
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalLayout, { onClose: handleClose, labelledBy: "title", children: [
57
94
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "title", fontWeight: "bold", textColor: "neutral800", children: formatMessage(
58
95
  {
@@ -64,45 +101,134 @@ const ReleaseModal = ({
64
101
  /* @__PURE__ */ jsxRuntime.jsx(
65
102
  formik.Formik,
66
103
  {
67
- validateOnChange: false,
68
- onSubmit: handleSubmit,
69
- 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
+ },
70
115
  validationSchema: RELEASE_SCHEMA,
71
- children: ({ values, errors, handleChange }) => /* @__PURE__ */ jsxRuntime.jsxs(formik.Form, { children: [
72
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalBody, { children: /* @__PURE__ */ jsxRuntime.jsx(
73
- designSystem.TextInput,
74
- {
75
- label: formatMessage({
76
- id: "content-releases.modal.form.input.label.release-name",
77
- defaultMessage: "Name"
78
- }),
79
- name: "name",
80
- value: values.name,
81
- error: errors.name,
82
- onChange: handleChange,
83
- required: true
84
- }
85
- ) }),
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
+ ] }) }),
86
221
  /* @__PURE__ */ jsxRuntime.jsx(
87
222
  designSystem.ModalFooter,
88
223
  {
89
224
  startActions: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleClose, variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }),
90
- endActions: /* @__PURE__ */ jsxRuntime.jsx(
91
- designSystem.Button,
225
+ endActions: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
92
226
  {
93
- name: "submit",
94
- loading: isLoading,
95
- disabled: !values.name || values.name === initialValues.name,
96
- type: "submit",
97
- children: formatMessage(
98
- {
99
- id: "content-releases.modal.form.button.submit",
100
- defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
101
- },
102
- { isCreatingRelease }
103
- )
104
- }
105
- )
227
+ id: "content-releases.modal.form.button.submit",
228
+ defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
229
+ },
230
+ { isCreatingRelease }
231
+ ) })
106
232
  }
107
233
  )
108
234
  ] })
@@ -110,22 +236,72 @@ const ReleaseModal = ({
110
236
  )
111
237
  ] });
112
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
+ onTextValueChange: (timezone) => {
277
+ setFieldValue("timezone", timezone);
278
+ },
279
+ onClear: () => {
280
+ setFieldValue("timezone", "");
281
+ },
282
+ error: errors.timezone,
283
+ required: true,
284
+ children: timezoneList.map((timezone) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: timezone.value, children: timezone.value.replace("_", " ") }, timezone.value))
285
+ }
286
+ );
287
+ };
113
288
  const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
114
289
  align-self: stretch;
115
290
  border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
116
291
  border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
117
292
  border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
118
293
  `;
119
- const StyledFlex = styled__default.default(designSystem.Flex)`
120
- align-self: stretch;
121
- cursor: ${({ disabled }) => disabled ? "not-allowed" : "pointer"};
122
-
294
+ const StyledMenuItem = styled__default.default(v2.Menu.Item)`
123
295
  svg path {
124
296
  fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
125
297
  }
126
298
  span {
127
299
  color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
128
300
  }
301
+
302
+ &:hover {
303
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
304
+ }
129
305
  `;
130
306
  const PencilIcon = styled__default.default(icons.Pencil)`
131
307
  width: ${({ theme }) => theme.spaces[3]};
@@ -144,24 +320,6 @@ const TrashIcon = styled__default.default(icons.Trash)`
144
320
  const TypographyMaxWidth = styled__default.default(designSystem.Typography)`
145
321
  max-width: 300px;
146
322
  `;
147
- const PopoverButton = ({ onClick, disabled, children }) => {
148
- return /* @__PURE__ */ jsxRuntime.jsx(
149
- StyledFlex,
150
- {
151
- paddingTop: 2,
152
- paddingBottom: 2,
153
- paddingLeft: 4,
154
- paddingRight: 4,
155
- alignItems: "center",
156
- gap: 2,
157
- as: "button",
158
- hasRadius: true,
159
- onClick,
160
- disabled,
161
- children
162
- }
163
- );
164
- };
165
323
  const EntryValidationText = ({ action, schema, components, entry }) => {
166
324
  const { formatMessage } = reactIntl.useIntl();
167
325
  const { validate } = strapiAdmin.unstable_useDocument();
@@ -210,10 +368,8 @@ const ReleaseDetailsLayout = ({
210
368
  toggleWarningSubmit,
211
369
  children
212
370
  }) => {
213
- const { formatMessage } = reactIntl.useIntl();
371
+ const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
214
372
  const { releaseId } = reactRouterDom.useParams();
215
- const [isPopoverVisible, setIsPopoverVisible] = React__namespace.useState(false);
216
- const moreButtonRef = React__namespace.useRef(null);
217
373
  const {
218
374
  data,
219
375
  isLoading: isLoadingDetails,
@@ -229,13 +385,6 @@ const ReleaseDetailsLayout = ({
229
385
  const dispatch = index.useTypedDispatch();
230
386
  const { trackUsage } = helperPlugin.useTracking();
231
387
  const release = data?.data;
232
- const handleTogglePopover = () => {
233
- setIsPopoverVisible((prev) => !prev);
234
- };
235
- const openReleaseModal = () => {
236
- toggleEditReleaseModal();
237
- handleTogglePopover();
238
- };
239
388
  const handlePublishRelease = async () => {
240
389
  const response = await publishRelease({ id: releaseId });
241
390
  if ("data" in response) {
@@ -264,10 +413,6 @@ const ReleaseDetailsLayout = ({
264
413
  });
265
414
  }
266
415
  };
267
- const openWarningConfirmDialog = () => {
268
- toggleWarningSubmit();
269
- handleTogglePopover();
270
- };
271
416
  const handleRefresh = () => {
272
417
  dispatch(index.releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
273
418
  };
@@ -305,89 +450,124 @@ const ReleaseDetailsLayout = ({
305
450
  }
306
451
  const totalEntries = release.actions.meta.count || 0;
307
452
  const hasCreatedByUser = Boolean(getCreatedByUser());
453
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
454
+ const isScheduled = release.scheduledAt && release.timezone;
455
+ const numberOfEntriesText = formatMessage(
456
+ {
457
+ id: "content-releases.pages.Details.header-subtitle",
458
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
459
+ },
460
+ { number: totalEntries }
461
+ );
462
+ const scheduledText = isScheduled ? formatMessage(
463
+ {
464
+ id: "content-releases.pages.ReleaseDetails.header-subtitle.scheduled",
465
+ defaultMessage: "Scheduled for {date} at {time} ({offset})"
466
+ },
467
+ {
468
+ date: formatDate(new Date(release.scheduledAt), {
469
+ weekday: "long",
470
+ day: "numeric",
471
+ month: "long",
472
+ year: "numeric",
473
+ timeZone: release.timezone
474
+ }),
475
+ time: formatTime(new Date(release.scheduledAt), {
476
+ timeZone: release.timezone,
477
+ hourCycle: "h23"
478
+ }),
479
+ offset: index.getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
480
+ }
481
+ ) : "";
308
482
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoadingDetails, children: [
309
483
  /* @__PURE__ */ jsxRuntime.jsx(
310
484
  designSystem.HeaderLayout,
311
485
  {
312
486
  title: release.name,
313
- subtitle: formatMessage(
314
- {
315
- id: "content-releases.pages.Details.header-subtitle",
316
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
317
- },
318
- { number: totalEntries }
319
- ),
487
+ subtitle: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? ` - ${scheduledText}` : ""),
320
488
  navigationAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
321
489
  id: "global.back",
322
490
  defaultMessage: "Back"
323
491
  }) }),
324
492
  primaryAction: !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
325
- /* @__PURE__ */ jsxRuntime.jsx(
326
- designSystem.IconButton,
327
- {
328
- label: formatMessage({
329
- id: "content-releases.header.actions.open-release-actions",
330
- defaultMessage: "Release edit and delete menu"
331
- }),
332
- ref: moreButtonRef,
333
- onClick: handleTogglePopover,
334
- children: /* @__PURE__ */ jsxRuntime.jsx(icons.More, {})
335
- }
336
- ),
337
- isPopoverVisible && /* @__PURE__ */ jsxRuntime.jsxs(
338
- designSystem.Popover,
339
- {
340
- source: moreButtonRef,
341
- placement: "bottom-end",
342
- onDismiss: handleTogglePopover,
343
- spacing: 4,
344
- minWidth: "242px",
345
- children: [
346
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", justifyContent: "center", direction: "column", padding: 1, children: [
347
- /* @__PURE__ */ jsxRuntime.jsxs(PopoverButton, { disabled: !canUpdate, onClick: openReleaseModal, children: [
348
- /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
349
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
350
- id: "content-releases.header.actions.edit",
351
- defaultMessage: "Edit"
352
- }) })
353
- ] }),
354
- /* @__PURE__ */ jsxRuntime.jsxs(PopoverButton, { disabled: !canDelete, onClick: openWarningConfirmDialog, children: [
355
- /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
356
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
357
- id: "content-releases.header.actions.delete",
358
- defaultMessage: "Delete"
359
- }) })
360
- ] })
361
- ] }),
362
- /* @__PURE__ */ jsxRuntime.jsxs(
363
- ReleaseInfoWrapper,
364
- {
365
- direction: "column",
366
- justifyContent: "center",
367
- alignItems: "flex-start",
368
- gap: 1,
369
- padding: 5,
370
- children: [
371
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
372
- id: "content-releases.header.actions.created",
373
- defaultMessage: "Created"
374
- }) }),
375
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", color: "neutral300", children: [
376
- /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(release.createdAt) }),
377
- formatMessage(
378
- {
379
- id: "content-releases.header.actions.created.description",
380
- defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
381
- },
382
- { createdBy: getCreatedByUser(), hasCreatedByUser }
383
- )
384
- ] })
385
- ]
386
- }
387
- )
388
- ]
389
- }
390
- ),
493
+ /* @__PURE__ */ jsxRuntime.jsxs(v2.Menu.Root, { children: [
494
+ /* @__PURE__ */ jsxRuntime.jsx(
495
+ v2.Menu.Trigger,
496
+ {
497
+ as: designSystem.IconButton,
498
+ paddingLeft: 2,
499
+ paddingRight: 2,
500
+ "aria-label": formatMessage({
501
+ id: "content-releases.header.actions.open-release-actions",
502
+ defaultMessage: "Release edit and delete menu"
503
+ }),
504
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.More, {}),
505
+ variant: "tertiary"
506
+ }
507
+ ),
508
+ /* @__PURE__ */ jsxRuntime.jsxs(v2.Menu.Content, { top: 1, popoverPlacement: "bottom-end", children: [
509
+ /* @__PURE__ */ jsxRuntime.jsxs(
510
+ designSystem.Flex,
511
+ {
512
+ alignItems: "center",
513
+ justifyContent: "center",
514
+ direction: "column",
515
+ padding: 1,
516
+ width: "100%",
517
+ children: [
518
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
519
+ /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
520
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
521
+ id: "content-releases.header.actions.edit",
522
+ defaultMessage: "Edit"
523
+ }) })
524
+ ] }) }),
525
+ /* @__PURE__ */ jsxRuntime.jsx(
526
+ StyledMenuItem,
527
+ {
528
+ disabled: !canDelete,
529
+ onSelect: toggleWarningSubmit,
530
+ variant: "danger",
531
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
532
+ /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
533
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
534
+ id: "content-releases.header.actions.delete",
535
+ defaultMessage: "Delete"
536
+ }) })
537
+ ] })
538
+ }
539
+ )
540
+ ]
541
+ }
542
+ ),
543
+ /* @__PURE__ */ jsxRuntime.jsxs(
544
+ ReleaseInfoWrapper,
545
+ {
546
+ direction: "column",
547
+ justifyContent: "center",
548
+ alignItems: "flex-start",
549
+ gap: 1,
550
+ padding: 5,
551
+ children: [
552
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
553
+ id: "content-releases.header.actions.created",
554
+ defaultMessage: "Created"
555
+ }) }),
556
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", color: "neutral300", children: [
557
+ /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(release.createdAt) }),
558
+ formatMessage(
559
+ {
560
+ id: "content-releases.header.actions.created.description",
561
+ defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
562
+ },
563
+ { createdBy: getCreatedByUser(), hasCreatedByUser }
564
+ )
565
+ ] })
566
+ ]
567
+ }
568
+ )
569
+ ] })
570
+ ] }),
391
571
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "S", variant: "tertiary", onClick: handleRefresh, children: formatMessage({
392
572
  id: "content-releases.header.actions.refresh",
393
573
  defaultMessage: "Refresh"
@@ -443,6 +623,9 @@ const ReleaseDetailsBody = () => {
443
623
  isError: isReleaseError,
444
624
  error: releaseError
445
625
  } = index.useGetReleaseQuery({ id: releaseId });
626
+ const {
627
+ allowedActions: { canUpdate }
628
+ } = helperPlugin.useRBAC(index.PERMISSIONS);
446
629
  const release = releaseData?.data;
447
630
  const selectedGroupBy = query?.groupBy || "contentType";
448
631
  const {
@@ -656,7 +839,8 @@ const ReleaseDetailsBody = () => {
656
839
  {
657
840
  selected: type,
658
841
  handleChange: (e) => handleChangeType(e, id, [key, actionIndex]),
659
- name: `release-action-${id}-type`
842
+ name: `release-action-${id}-type`,
843
+ disabled: !canUpdate
660
844
  }
661
845
  ) }),
662
846
  !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -735,11 +919,18 @@ const ReleaseDetailsPage = () => {
735
919
  }
736
920
  );
737
921
  }
738
- const title = isSuccessDetails && data?.data?.name || "";
922
+ const releaseData = isSuccessDetails && data?.data || null;
923
+ const title = releaseData?.name || "";
924
+ const timezone = releaseData?.timezone ?? null;
925
+ const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
926
+ const date = scheduledAt ? new Date(format__default.default(scheduledAt, "yyyy-MM-dd")) : null;
927
+ const time = scheduledAt ? format__default.default(scheduledAt, "HH:mm") : "";
739
928
  const handleEditRelease = async (values) => {
740
929
  const response = await updateRelease({
741
930
  id: releaseId,
742
- name: values.name
931
+ name: values.name,
932
+ scheduledAt: values.scheduledAt,
933
+ timezone: values.timezone
743
934
  });
744
935
  if ("data" in response) {
745
936
  toggleNotification({
@@ -793,7 +984,14 @@ const ReleaseDetailsPage = () => {
793
984
  handleClose: toggleEditReleaseModal,
794
985
  handleSubmit: handleEditRelease,
795
986
  isLoading: isLoadingDetails || isSubmittingForm,
796
- initialValues: { name: title || "" }
987
+ initialValues: {
988
+ name: title || "",
989
+ scheduledAt,
990
+ date,
991
+ time,
992
+ isScheduled: Boolean(scheduledAt),
993
+ timezone
994
+ }
797
995
  }
798
996
  ),
799
997
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -818,6 +1016,7 @@ const LinkCard = styled__default.default(v2.Link)`
818
1016
  `;
819
1017
  const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
820
1018
  const { formatMessage } = reactIntl.useIntl();
1019
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
821
1020
  if (isError) {
822
1021
  return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
823
1022
  }
@@ -838,7 +1037,7 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
838
1037
  }
839
1038
  );
840
1039
  }
841
- 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(
1040
+ 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(
842
1041
  designSystem.Flex,
843
1042
  {
844
1043
  direction: "column",
@@ -853,7 +1052,10 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
853
1052
  gap: 2,
854
1053
  children: [
855
1054
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
856
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: formatMessage(
1055
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: IsSchedulingEnabled ? scheduledAt ? /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
1056
+ id: "content-releases.pages.Releases.not-scheduled",
1057
+ defaultMessage: "Not scheduled"
1058
+ }) : formatMessage(
857
1059
  {
858
1060
  id: "content-releases.page.Releases.release-item.entries",
859
1061
  defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
@@ -873,7 +1075,13 @@ const StyledAlert = styled__default.default(designSystem.Alert)`
873
1075
  }
874
1076
  `;
875
1077
  const INITIAL_FORM_VALUES = {
876
- name: ""
1078
+ name: "",
1079
+ date: null,
1080
+ time: "",
1081
+ // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
1082
+ isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
1083
+ scheduledAt: null,
1084
+ timezone: null
877
1085
  };
878
1086
  const ReleasesPage = () => {
879
1087
  const tabRef = React__namespace.useRef(null);
@@ -919,8 +1127,8 @@ const ReleasesPage = () => {
919
1127
  if (isLoading) {
920
1128
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
921
1129
  }
922
- const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
923
- const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
1130
+ const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
1131
+ const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
924
1132
  const handleTabChange = (index2) => {
925
1133
  setQuery({
926
1134
  ...query,
@@ -933,9 +1141,11 @@ const ReleasesPage = () => {
933
1141
  }
934
1142
  });
935
1143
  };
936
- const handleAddRelease = async (values) => {
1144
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
937
1145
  const response2 = await createRelease({
938
- name: values.name
1146
+ name,
1147
+ scheduledAt,
1148
+ timezone
939
1149
  });
940
1150
  if ("data" in response2) {
941
1151
  toggleNotification({
@@ -967,13 +1177,10 @@ const ReleasesPage = () => {
967
1177
  id: "content-releases.pages.Releases.title",
968
1178
  defaultMessage: "Releases"
969
1179
  }),
970
- subtitle: formatMessage(
971
- {
972
- id: "content-releases.pages.Releases.header-subtitle",
973
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
974
- },
975
- { number: totalReleases }
976
- ),
1180
+ subtitle: formatMessage({
1181
+ id: "content-releases.pages.Releases.header-subtitle",
1182
+ defaultMessage: "Create and manage content updates"
1183
+ }),
977
1184
  primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
978
1185
  designSystem.Button,
979
1186
  {
@@ -989,7 +1196,7 @@ const ReleasesPage = () => {
989
1196
  }
990
1197
  ),
991
1198
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
992
- activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
1199
+ hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
993
1200
  StyledAlert,
994
1201
  {
995
1202
  marginBottom: 6,
@@ -1027,10 +1234,15 @@ const ReleasesPage = () => {
1027
1234
  children: [
1028
1235
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingBottom: 8, children: [
1029
1236
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs, { children: [
1030
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1031
- id: "content-releases.pages.Releases.tab.pending",
1032
- defaultMessage: "Pending"
1033
- }) }),
1237
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage(
1238
+ {
1239
+ id: "content-releases.pages.Releases.tab.pending",
1240
+ defaultMessage: "Pending ({count})"
1241
+ },
1242
+ {
1243
+ count: totalPendingReleases
1244
+ }
1245
+ ) }),
1034
1246
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1035
1247
  id: "content-releases.pages.Releases.tab.done",
1036
1248
  defaultMessage: "Done"
@@ -1059,7 +1271,7 @@ const ReleasesPage = () => {
1059
1271
  ]
1060
1272
  }
1061
1273
  ),
1062
- totalReleases > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1274
+ response.currentData?.meta?.pagination?.total ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1063
1275
  /* @__PURE__ */ jsxRuntime.jsx(
1064
1276
  helperPlugin.PageSizeURLQuery,
1065
1277
  {
@@ -1075,7 +1287,7 @@ const ReleasesPage = () => {
1075
1287
  }
1076
1288
  }
1077
1289
  )
1078
- ] })
1290
+ ] }) : null
1079
1291
  ] }) }),
1080
1292
  releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
1081
1293
  ReleaseModal,
@@ -1095,4 +1307,4 @@ const App = () => {
1095
1307
  ] }) });
1096
1308
  };
1097
1309
  exports.App = App;
1098
- //# sourceMappingURL=App-IQIHCiSO.js.map
1310
+ //# sourceMappingURL=App-lnXbSPgp.js.map