@strapi/content-releases 0.0.0-next.c5f067b5650921187770124e9b6c8186e805e242 → 0.0.0-next.d10040847b91742ccb8083938399b63ffa289c7a

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-3ycH2d3s.mjs → App-g3vtS2Wa.mjs} +405 -180
  2. package/dist/_chunks/App-g3vtS2Wa.mjs.map +1 -0
  3. package/dist/_chunks/{App-5PsAyVt2.js → App-lnXbSPgp.js} +403 -177
  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-SOqjCdyh.mjs → en-WuuhP6Bn.mjs} +18 -6
  10. package/dist/_chunks/en-WuuhP6Bn.mjs.map +1 -0
  11. package/dist/_chunks/{en-2DuPv5k0.js → en-gcJJ5htG.js} +18 -6
  12. package/dist/_chunks/en-gcJJ5htG.js.map +1 -0
  13. package/dist/_chunks/{index-D57Rztnc.js → index-ItlgrLcr.js} +127 -23
  14. package/dist/_chunks/index-ItlgrLcr.js.map +1 -0
  15. package/dist/_chunks/{index-4gUWuCQV.mjs → index-uGex_IIQ.mjs} +131 -27
  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 +863 -419
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +862 -419
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +14 -11
  24. package/dist/_chunks/App-3ycH2d3s.mjs.map +0 -1
  25. package/dist/_chunks/App-5PsAyVt2.js.map +0 -1
  26. package/dist/_chunks/en-2DuPv5k0.js.map +0 -1
  27. package/dist/_chunks/en-SOqjCdyh.mjs.map +0 -1
  28. package/dist/_chunks/index-4gUWuCQV.mjs.map +0 -1
  29. package/dist/_chunks/index-D57Rztnc.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-D57Rztnc.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,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,33 +236,83 @@ 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
+ 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
+ };
111
288
  const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
112
289
  align-self: stretch;
113
290
  border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
114
291
  border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
115
292
  border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
116
293
  `;
117
- const StyledFlex = styled__default.default(designSystem.Flex)`
118
- align-self: stretch;
119
- cursor: ${({ disabled }) => disabled ? "not-allowed" : "pointer"};
120
-
294
+ const StyledMenuItem = styled__default.default(v2.Menu.Item)`
121
295
  svg path {
122
296
  fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
123
297
  }
124
298
  span {
125
299
  color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
126
300
  }
301
+
302
+ &:hover {
303
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
304
+ }
127
305
  `;
128
306
  const PencilIcon = styled__default.default(icons.Pencil)`
129
- width: ${({ theme }) => theme.spaces[4]};
130
- height: ${({ theme }) => theme.spaces[4]};
307
+ width: ${({ theme }) => theme.spaces[3]};
308
+ height: ${({ theme }) => theme.spaces[3]};
131
309
  path {
132
310
  fill: ${({ theme }) => theme.colors.neutral600};
133
311
  }
134
312
  `;
135
313
  const TrashIcon = styled__default.default(icons.Trash)`
136
- width: ${({ theme }) => theme.spaces[4]};
137
- height: ${({ theme }) => theme.spaces[4]};
314
+ width: ${({ theme }) => theme.spaces[3]};
315
+ height: ${({ theme }) => theme.spaces[3]};
138
316
  path {
139
317
  fill: ${({ theme }) => theme.colors.danger600};
140
318
  }
@@ -142,24 +320,6 @@ const TrashIcon = styled__default.default(icons.Trash)`
142
320
  const TypographyMaxWidth = styled__default.default(designSystem.Typography)`
143
321
  max-width: 300px;
144
322
  `;
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
323
  const EntryValidationText = ({ action, schema, components, entry }) => {
164
324
  const { formatMessage } = reactIntl.useIntl();
165
325
  const { validate } = strapiAdmin.unstable_useDocument();
@@ -208,10 +368,8 @@ const ReleaseDetailsLayout = ({
208
368
  toggleWarningSubmit,
209
369
  children
210
370
  }) => {
211
- const { formatMessage } = reactIntl.useIntl();
371
+ const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
212
372
  const { releaseId } = reactRouterDom.useParams();
213
- const [isPopoverVisible, setIsPopoverVisible] = React__namespace.useState(false);
214
- const moreButtonRef = React__namespace.useRef(null);
215
373
  const {
216
374
  data,
217
375
  isLoading: isLoadingDetails,
@@ -227,13 +385,6 @@ const ReleaseDetailsLayout = ({
227
385
  const dispatch = index.useTypedDispatch();
228
386
  const { trackUsage } = helperPlugin.useTracking();
229
387
  const release = data?.data;
230
- const handleTogglePopover = () => {
231
- setIsPopoverVisible((prev) => !prev);
232
- };
233
- const openReleaseModal = () => {
234
- toggleEditReleaseModal();
235
- handleTogglePopover();
236
- };
237
388
  const handlePublishRelease = async () => {
238
389
  const response = await publishRelease({ id: releaseId });
239
390
  if ("data" in response) {
@@ -262,13 +413,21 @@ const ReleaseDetailsLayout = ({
262
413
  });
263
414
  }
264
415
  };
265
- const openWarningConfirmDialog = () => {
266
- toggleWarningSubmit();
267
- handleTogglePopover();
268
- };
269
416
  const handleRefresh = () => {
270
417
  dispatch(index.releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
271
418
  };
419
+ const getCreatedByUser = () => {
420
+ if (!release?.createdBy) {
421
+ return null;
422
+ }
423
+ if (release.createdBy.username) {
424
+ return release.createdBy.username;
425
+ }
426
+ if (release.createdBy.firstname) {
427
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
428
+ }
429
+ return release.createdBy.email;
430
+ };
272
431
  if (isLoadingDetails) {
273
432
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
274
433
  }
@@ -290,90 +449,125 @@ const ReleaseDetailsLayout = ({
290
449
  );
291
450
  }
292
451
  const totalEntries = release.actions.meta.count || 0;
293
- const createdBy = release.createdBy.lastname ? `${release.createdBy.firstname} ${release.createdBy.lastname}` : `${release.createdBy.firstname}`;
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
+ ) : "";
294
482
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoadingDetails, children: [
295
483
  /* @__PURE__ */ jsxRuntime.jsx(
296
484
  designSystem.HeaderLayout,
297
485
  {
298
486
  title: release.name,
299
- subtitle: formatMessage(
300
- {
301
- id: "content-releases.pages.Details.header-subtitle",
302
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
303
- },
304
- { number: totalEntries }
305
- ),
487
+ subtitle: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? ` - ${scheduledText}` : ""),
306
488
  navigationAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
307
489
  id: "global.back",
308
490
  defaultMessage: "Back"
309
491
  }) }),
310
492
  primaryAction: !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
311
- /* @__PURE__ */ jsxRuntime.jsx(
312
- designSystem.IconButton,
313
- {
314
- label: formatMessage({
315
- id: "content-releases.header.actions.open-release-actions",
316
- defaultMessage: "Release actions"
317
- }),
318
- ref: moreButtonRef,
319
- onClick: handleTogglePopover,
320
- children: /* @__PURE__ */ jsxRuntime.jsx(icons.More, {})
321
- }
322
- ),
323
- isPopoverVisible && /* @__PURE__ */ jsxRuntime.jsxs(
324
- designSystem.Popover,
325
- {
326
- source: moreButtonRef,
327
- placement: "bottom-end",
328
- onDismiss: handleTogglePopover,
329
- spacing: 4,
330
- minWidth: "242px",
331
- children: [
332
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", justifyContent: "center", direction: "column", padding: 1, children: [
333
- /* @__PURE__ */ jsxRuntime.jsxs(PopoverButton, { disabled: !canUpdate, onClick: openReleaseModal, children: [
334
- /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
335
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
336
- id: "content-releases.header.actions.edit",
337
- defaultMessage: "Edit"
338
- }) })
339
- ] }),
340
- /* @__PURE__ */ jsxRuntime.jsxs(PopoverButton, { disabled: !canDelete, onClick: openWarningConfirmDialog, children: [
341
- /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
342
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
343
- id: "content-releases.header.actions.delete",
344
- defaultMessage: "Delete"
345
- }) })
346
- ] })
347
- ] }),
348
- /* @__PURE__ */ jsxRuntime.jsxs(
349
- ReleaseInfoWrapper,
350
- {
351
- direction: "column",
352
- justifyContent: "center",
353
- alignItems: "flex-start",
354
- gap: 1,
355
- padding: 5,
356
- children: [
357
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
358
- id: "content-releases.header.actions.created",
359
- defaultMessage: "Created"
360
- }) }),
361
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", color: "neutral300", children: [
362
- /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(release.createdAt) }),
363
- formatMessage(
364
- {
365
- id: "content-releases.header.actions.created.description",
366
- defaultMessage: " by {createdBy}"
367
- },
368
- { createdBy }
369
- )
370
- ] })
371
- ]
372
- }
373
- )
374
- ]
375
- }
376
- ),
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
+ ] }),
377
571
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "S", variant: "tertiary", onClick: handleRefresh, children: formatMessage({
378
572
  id: "content-releases.header.actions.refresh",
379
573
  defaultMessage: "Refresh"
@@ -429,6 +623,9 @@ const ReleaseDetailsBody = () => {
429
623
  isError: isReleaseError,
430
624
  error: releaseError
431
625
  } = index.useGetReleaseQuery({ id: releaseId });
626
+ const {
627
+ allowedActions: { canUpdate }
628
+ } = helperPlugin.useRBAC(index.PERMISSIONS);
432
629
  const release = releaseData?.data;
433
630
  const selectedGroupBy = query?.groupBy || "contentType";
434
631
  const {
@@ -535,7 +732,7 @@ const ReleaseDetailsBody = () => {
535
732
  designSystem.SingleSelect,
536
733
  {
537
734
  "aria-label": formatMessage({
538
- id: "content-releases.pages.ReleaseDetails.groupBy.label",
735
+ id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
539
736
  defaultMessage: "Group by"
540
737
  }),
541
738
  customizeContent: (value) => formatMessage(
@@ -553,7 +750,7 @@ const ReleaseDetailsBody = () => {
553
750
  }
554
751
  ) }),
555
752
  Object.keys(releaseActions).map((key) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, direction: "column", alignItems: "stretch", children: [
556
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: key }) }),
753
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { role: "separator", "aria-label": key, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: key }) }),
557
754
  /* @__PURE__ */ jsxRuntime.jsx(
558
755
  helperPlugin.Table.Root,
559
756
  {
@@ -642,7 +839,8 @@ const ReleaseDetailsBody = () => {
642
839
  {
643
840
  selected: type,
644
841
  handleChange: (e) => handleChangeType(e, id, [key, actionIndex]),
645
- name: `release-action-${id}-type`
842
+ name: `release-action-${id}-type`,
843
+ disabled: !canUpdate
646
844
  }
647
845
  ) }),
648
846
  !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -721,11 +919,18 @@ const ReleaseDetailsPage = () => {
721
919
  }
722
920
  );
723
921
  }
724
- 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") : "";
725
928
  const handleEditRelease = async (values) => {
726
929
  const response = await updateRelease({
727
930
  id: releaseId,
728
- name: values.name
931
+ name: values.name,
932
+ scheduledAt: values.scheduledAt,
933
+ timezone: values.timezone
729
934
  });
730
935
  if ("data" in response) {
731
936
  toggleNotification({
@@ -779,7 +984,14 @@ const ReleaseDetailsPage = () => {
779
984
  handleClose: toggleEditReleaseModal,
780
985
  handleSubmit: handleEditRelease,
781
986
  isLoading: isLoadingDetails || isSubmittingForm,
782
- initialValues: { name: title || "" }
987
+ initialValues: {
988
+ name: title || "",
989
+ scheduledAt,
990
+ date,
991
+ time,
992
+ isScheduled: Boolean(scheduledAt),
993
+ timezone
994
+ }
783
995
  }
784
996
  ),
785
997
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -804,6 +1016,7 @@ const LinkCard = styled__default.default(v2.Link)`
804
1016
  `;
805
1017
  const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
806
1018
  const { formatMessage } = reactIntl.useIntl();
1019
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
807
1020
  if (isError) {
808
1021
  return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
809
1022
  }
@@ -824,7 +1037,7 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
824
1037
  }
825
1038
  );
826
1039
  }
827
- 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(
828
1041
  designSystem.Flex,
829
1042
  {
830
1043
  direction: "column",
@@ -839,7 +1052,10 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
839
1052
  gap: 2,
840
1053
  children: [
841
1054
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
842
- /* @__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(
843
1059
  {
844
1060
  id: "content-releases.page.Releases.release-item.entries",
845
1061
  defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
@@ -859,7 +1075,13 @@ const StyledAlert = styled__default.default(designSystem.Alert)`
859
1075
  }
860
1076
  `;
861
1077
  const INITIAL_FORM_VALUES = {
862
- 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
863
1085
  };
864
1086
  const ReleasesPage = () => {
865
1087
  const tabRef = React__namespace.useRef(null);
@@ -905,8 +1127,8 @@ const ReleasesPage = () => {
905
1127
  if (isLoading) {
906
1128
  return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
907
1129
  }
908
- const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
909
- const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
1130
+ const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
1131
+ const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
910
1132
  const handleTabChange = (index2) => {
911
1133
  setQuery({
912
1134
  ...query,
@@ -919,9 +1141,11 @@ const ReleasesPage = () => {
919
1141
  }
920
1142
  });
921
1143
  };
922
- const handleAddRelease = async (values) => {
1144
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
923
1145
  const response2 = await createRelease({
924
- name: values.name
1146
+ name,
1147
+ scheduledAt,
1148
+ timezone
925
1149
  });
926
1150
  if ("data" in response2) {
927
1151
  toggleNotification({
@@ -953,13 +1177,10 @@ const ReleasesPage = () => {
953
1177
  id: "content-releases.pages.Releases.title",
954
1178
  defaultMessage: "Releases"
955
1179
  }),
956
- subtitle: formatMessage(
957
- {
958
- id: "content-releases.pages.Releases.header-subtitle",
959
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
960
- },
961
- { number: totalReleases }
962
- ),
1180
+ subtitle: formatMessage({
1181
+ id: "content-releases.pages.Releases.header-subtitle",
1182
+ defaultMessage: "Create and manage content updates"
1183
+ }),
963
1184
  primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
964
1185
  designSystem.Button,
965
1186
  {
@@ -975,7 +1196,7 @@ const ReleasesPage = () => {
975
1196
  }
976
1197
  ),
977
1198
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
978
- activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
1199
+ hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
979
1200
  StyledAlert,
980
1201
  {
981
1202
  marginBottom: 6,
@@ -1013,10 +1234,15 @@ const ReleasesPage = () => {
1013
1234
  children: [
1014
1235
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingBottom: 8, children: [
1015
1236
  /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs, { children: [
1016
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1017
- id: "content-releases.pages.Releases.tab.pending",
1018
- defaultMessage: "Pending"
1019
- }) }),
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
+ ) }),
1020
1246
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1021
1247
  id: "content-releases.pages.Releases.tab.done",
1022
1248
  defaultMessage: "Done"
@@ -1045,7 +1271,7 @@ const ReleasesPage = () => {
1045
1271
  ]
1046
1272
  }
1047
1273
  ),
1048
- 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: [
1049
1275
  /* @__PURE__ */ jsxRuntime.jsx(
1050
1276
  helperPlugin.PageSizeURLQuery,
1051
1277
  {
@@ -1061,7 +1287,7 @@ const ReleasesPage = () => {
1061
1287
  }
1062
1288
  }
1063
1289
  )
1064
- ] })
1290
+ ] }) : null
1065
1291
  ] }) }),
1066
1292
  releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
1067
1293
  ReleaseModal,
@@ -1081,4 +1307,4 @@ const App = () => {
1081
1307
  ] }) });
1082
1308
  };
1083
1309
  exports.App = App;
1084
- //# sourceMappingURL=App-5PsAyVt2.js.map
1310
+ //# sourceMappingURL=App-lnXbSPgp.js.map