@strapi/content-releases 0.0.0-next.287aae0bb4a682fba312332372895dbf8e032bf1 → 0.0.0-next.2a816cdadd2ece37767550b2a249c03ef2b53aeb

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-M11U5GPh.mjs → App-eVYKuxUc.mjs} +377 -159
  2. package/dist/_chunks/App-eVYKuxUc.mjs.map +1 -0
  3. package/dist/_chunks/{App-x6I6piPB.js → App-oGnTffWo.js} +375 -156
  4. package/dist/_chunks/App-oGnTffWo.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--WvbU-_H.js → en-r0otWaln.js} +13 -2
  10. package/dist/_chunks/en-r0otWaln.js.map +1 -0
  11. package/dist/_chunks/{en-oREbrNEq.mjs → en-veqvqeEr.mjs} +13 -2
  12. package/dist/_chunks/en-veqvqeEr.mjs.map +1 -0
  13. package/dist/_chunks/{index-Ls9Qc5Tg.mjs → index-8XT9UxC5.mjs} +101 -20
  14. package/dist/_chunks/index-8XT9UxC5.mjs.map +1 -0
  15. package/dist/_chunks/{index-M4gICzzI.js → index-ABxBBqzg.js} +97 -16
  16. package/dist/_chunks/index-ABxBBqzg.js.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 +566 -395
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +566 -395
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +12 -9
  24. package/dist/_chunks/App-M11U5GPh.mjs.map +0 -1
  25. package/dist/_chunks/App-x6I6piPB.js.map +0 -1
  26. package/dist/_chunks/en--WvbU-_H.js.map +0 -1
  27. package/dist/_chunks/en-oREbrNEq.mjs.map +0 -1
  28. package/dist/_chunks/index-Ls9Qc5Tg.mjs.map +0 -1
  29. package/dist/_chunks/index-M4gICzzI.js.map +0 -1
@@ -1,22 +1,42 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { useNotification, useAPIErrorHandler, LoadingIndicatorPage, ConfirmDialog, useRBAC, useTracking, RelativeTime, CheckPermissions, useQueryParams, AnErrorOccurred, NoContent, Table, PageSizeURLQuery, PaginationURLQuery, CheckPagePermissions } from "@strapi/helper-plugin";
3
3
  import { useLocation, useParams, useHistory, Redirect, Link as Link$1, Switch, Route } from "react-router-dom";
4
- import { p as pluginId, u as useGetReleaseQuery, a as useUpdateReleaseMutation, b as useDeleteReleaseMutation, c as usePublishReleaseMutation, P as PERMISSIONS, d as useTypedDispatch, e as useGetReleaseActionsQuery, f as useUpdateReleaseActionMutation, R as ReleaseActionOptions, g as ReleaseActionMenu, i as isAxiosError, r as releaseApi, h as useGetReleasesQuery, j as useCreateReleaseMutation } from "./index-Ls9Qc5Tg.mjs";
4
+ import { g as getTimezoneOffset, p as pluginId, u as useGetReleaseQuery, a as useUpdateReleaseMutation, b as useDeleteReleaseMutation, c as usePublishReleaseMutation, P as PERMISSIONS, d as useTypedDispatch, e as useGetReleaseActionsQuery, f as useUpdateReleaseActionMutation, R as ReleaseActionOptions, h as ReleaseActionMenu, i as isAxiosError, r as releaseApi, j as useGetReleasesQuery, k as useCreateReleaseMutation } from "./index-8XT9UxC5.mjs";
5
5
  import * as React from "react";
6
6
  import { unstable_useDocument, useLicenseLimits } from "@strapi/admin/strapi-admin";
7
- import { ModalLayout, ModalHeader, Typography, ModalBody, TextInput, ModalFooter, Button, Flex, ContentLayout, Main, HeaderLayout, Link, IconButton, Popover, SingleSelect, SingleSelectOption, Badge, Tr, Td, Icon, Tooltip, Alert, TabGroup, Box, Tabs, Tab, Divider, TabPanels, TabPanel, EmptyStateLayout, Grid, GridItem } from "@strapi/design-system";
8
- import { LinkButton, Link as Link$2 } from "@strapi/design-system/v2";
7
+ import { ModalLayout, ModalHeader, Typography, ModalBody, Flex, TextInput, Checkbox, Box, DatePicker, TimePicker, ModalFooter, Button, Combobox, ComboboxOption, ContentLayout, Main, HeaderLayout, Link, IconButton, SingleSelect, SingleSelectOption, Badge, Tr, Td, Icon, Tooltip, Alert, TabGroup, Tabs, Tab, Divider, TabPanels, TabPanel, EmptyStateLayout, Grid, GridItem } from "@strapi/design-system";
8
+ import { Menu, LinkButton, Link as Link$2 } from "@strapi/design-system/v2";
9
9
  import { Pencil, Trash, ArrowLeft, More, CrossCircle, CheckCircle, Plus, EmptyDocuments } from "@strapi/icons";
10
+ import format from "date-fns/format";
11
+ import { zonedTimeToUtc, utcToZonedTime } from "date-fns-tz";
10
12
  import { useIntl } from "react-intl";
11
13
  import styled from "styled-components";
12
- import { Formik, Form } from "formik";
14
+ import { formatISO, parse } from "date-fns";
15
+ import { Formik, Form, useFormikContext } from "formik";
13
16
  import * as yup from "yup";
14
17
  import "@reduxjs/toolkit/query";
15
18
  import "axios";
16
19
  import "@reduxjs/toolkit/query/react";
17
20
  import "react-redux";
18
21
  const RELEASE_SCHEMA = yup.object().shape({
19
- name: yup.string().trim().required()
22
+ name: yup.string().trim().required(),
23
+ scheduledAt: yup.string().nullable(),
24
+ isScheduled: yup.boolean().optional(),
25
+ time: yup.string().when("isScheduled", {
26
+ is: true,
27
+ then: yup.string().trim().required(),
28
+ otherwise: yup.string().nullable()
29
+ }),
30
+ timezone: yup.string().when("isScheduled", {
31
+ is: true,
32
+ then: yup.string().required().nullable(),
33
+ otherwise: yup.string().nullable()
34
+ }),
35
+ date: yup.string().when("isScheduled", {
36
+ is: true,
37
+ then: yup.string().required().nullable(),
38
+ otherwise: yup.string().nullable()
39
+ })
20
40
  }).required().noUnknown();
21
41
  const ReleaseModal = ({
22
42
  handleClose,
@@ -27,6 +47,24 @@ const ReleaseModal = ({
27
47
  const { formatMessage } = useIntl();
28
48
  const { pathname } = useLocation();
29
49
  const isCreatingRelease = pathname === `/plugins/${pluginId}`;
50
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
51
+ const { timezoneList, systemTimezone = { value: "UTC+00:00-Africa/Abidjan " } } = getTimezones(
52
+ initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : /* @__PURE__ */ new Date()
53
+ );
54
+ const getScheduledTimestamp = (values) => {
55
+ const { date, time, timezone } = values;
56
+ if (!date || !time || !timezone)
57
+ return null;
58
+ const formattedDate = parse(time, "HH:mm", new Date(date));
59
+ const timezoneWithoutOffset = timezone.split("-")[1];
60
+ return zonedTimeToUtc(formattedDate, timezoneWithoutOffset);
61
+ };
62
+ const getTimezoneWithOffset = () => {
63
+ const currentTimezone = timezoneList.find(
64
+ (timezone) => timezone.value.split("-")[1] === initialValues.timezone
65
+ );
66
+ return currentTimezone?.value || systemTimezone.value;
67
+ };
30
68
  return /* @__PURE__ */ jsxs(ModalLayout, { onClose: handleClose, labelledBy: "title", children: [
31
69
  /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { id: "title", fontWeight: "bold", textColor: "neutral800", children: formatMessage(
32
70
  {
@@ -38,45 +76,134 @@ const ReleaseModal = ({
38
76
  /* @__PURE__ */ jsx(
39
77
  Formik,
40
78
  {
41
- validateOnChange: false,
42
- onSubmit: handleSubmit,
43
- initialValues,
79
+ onSubmit: (values) => {
80
+ handleSubmit({
81
+ ...values,
82
+ timezone: values.timezone ? values.timezone.split("-")[1] : null,
83
+ scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
84
+ });
85
+ },
86
+ initialValues: {
87
+ ...initialValues,
88
+ timezone: initialValues.timezone ? getTimezoneWithOffset() : systemTimezone.value
89
+ },
44
90
  validationSchema: RELEASE_SCHEMA,
45
- children: ({ values, errors, handleChange }) => /* @__PURE__ */ jsxs(Form, { children: [
46
- /* @__PURE__ */ jsx(ModalBody, { children: /* @__PURE__ */ jsx(
47
- TextInput,
48
- {
49
- label: formatMessage({
50
- id: "content-releases.modal.form.input.label.release-name",
51
- defaultMessage: "Name"
52
- }),
53
- name: "name",
54
- value: values.name,
55
- error: errors.name,
56
- onChange: handleChange,
57
- required: true
58
- }
59
- ) }),
91
+ validateOnChange: false,
92
+ children: ({ values, errors, handleChange, setFieldValue }) => /* @__PURE__ */ jsxs(Form, { children: [
93
+ /* @__PURE__ */ jsx(ModalBody, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
94
+ /* @__PURE__ */ jsx(
95
+ TextInput,
96
+ {
97
+ label: formatMessage({
98
+ id: "content-releases.modal.form.input.label.release-name",
99
+ defaultMessage: "Name"
100
+ }),
101
+ name: "name",
102
+ value: values.name,
103
+ error: errors.name,
104
+ onChange: handleChange,
105
+ required: true
106
+ }
107
+ ),
108
+ IsSchedulingEnabled && /* @__PURE__ */ jsxs(Fragment, { children: [
109
+ /* @__PURE__ */ jsx(
110
+ Checkbox,
111
+ {
112
+ name: "isScheduled",
113
+ value: values.isScheduled,
114
+ onChange: (event) => {
115
+ setFieldValue("isScheduled", event.target.checked);
116
+ if (!event.target.checked) {
117
+ setFieldValue("date", null);
118
+ setFieldValue("time", "");
119
+ setFieldValue("timezone", null);
120
+ } else {
121
+ setFieldValue("date", initialValues.date);
122
+ setFieldValue("time", initialValues.time);
123
+ setFieldValue(
124
+ "timezone",
125
+ initialValues.timezone ?? systemTimezone?.value
126
+ );
127
+ }
128
+ },
129
+ children: /* @__PURE__ */ jsx(
130
+ Typography,
131
+ {
132
+ textColor: values.isScheduled ? "primary600" : "neutral800",
133
+ fontWeight: values.isScheduled ? "semiBold" : "regular",
134
+ children: formatMessage({
135
+ id: "modal.form.input.label.schedule-release",
136
+ defaultMessage: "Schedule release"
137
+ })
138
+ }
139
+ )
140
+ }
141
+ ),
142
+ values.isScheduled && /* @__PURE__ */ jsxs(Fragment, { children: [
143
+ /* @__PURE__ */ jsxs(Flex, { gap: 4, alignItems: "start", children: [
144
+ /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(
145
+ DatePicker,
146
+ {
147
+ label: formatMessage({
148
+ id: "content-releases.modal.form.input.label.date",
149
+ defaultMessage: "Date"
150
+ }),
151
+ name: "date",
152
+ error: errors.date,
153
+ onChange: (date) => {
154
+ const isoFormatDate = date ? formatISO(date, { representation: "date" }) : null;
155
+ setFieldValue("date", isoFormatDate);
156
+ },
157
+ clearLabel: formatMessage({
158
+ id: "content-releases.modal.form.input.clearLabel",
159
+ defaultMessage: "Clear"
160
+ }),
161
+ onClear: () => {
162
+ setFieldValue("date", null);
163
+ },
164
+ selectedDate: values.date || void 0,
165
+ required: true
166
+ }
167
+ ) }),
168
+ /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(
169
+ TimePicker,
170
+ {
171
+ label: formatMessage({
172
+ id: "content-releases.modal.form.input.label.time",
173
+ defaultMessage: "Time"
174
+ }),
175
+ name: "time",
176
+ error: errors.time,
177
+ onChange: (time) => {
178
+ setFieldValue("time", time);
179
+ },
180
+ clearLabel: formatMessage({
181
+ id: "content-releases.modal.form.input.clearLabel",
182
+ defaultMessage: "Clear"
183
+ }),
184
+ onClear: () => {
185
+ setFieldValue("time", "");
186
+ },
187
+ value: values.time || void 0,
188
+ required: true
189
+ }
190
+ ) })
191
+ ] }),
192
+ /* @__PURE__ */ jsx(TimezoneComponent, { timezoneOptions: timezoneList })
193
+ ] })
194
+ ] })
195
+ ] }) }),
60
196
  /* @__PURE__ */ jsx(
61
197
  ModalFooter,
62
198
  {
63
199
  startActions: /* @__PURE__ */ jsx(Button, { onClick: handleClose, variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }),
64
- endActions: /* @__PURE__ */ jsx(
65
- Button,
200
+ endActions: /* @__PURE__ */ jsx(Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
66
201
  {
67
- name: "submit",
68
- loading: isLoading,
69
- disabled: !values.name || values.name === initialValues.name,
70
- type: "submit",
71
- children: formatMessage(
72
- {
73
- id: "content-releases.modal.form.button.submit",
74
- defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
75
- },
76
- { isCreatingRelease }
77
- )
78
- }
79
- )
202
+ id: "content-releases.modal.form.button.submit",
203
+ defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
204
+ },
205
+ { isCreatingRelease }
206
+ ) })
80
207
  }
81
208
  )
82
209
  ] })
@@ -84,16 +211,59 @@ const ReleaseModal = ({
84
211
  )
85
212
  ] });
86
213
  };
214
+ const getTimezones = (selectedDate) => {
215
+ const timezoneList = Intl.supportedValuesOf("timeZone").map((timezone) => {
216
+ const utcOffset = getTimezoneOffset(timezone, selectedDate);
217
+ return { offset: utcOffset, value: `${utcOffset}-${timezone}` };
218
+ });
219
+ const systemTimezone = timezoneList.find(
220
+ (timezone) => timezone.value.split("-")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
221
+ );
222
+ return { timezoneList, systemTimezone };
223
+ };
224
+ const TimezoneComponent = ({ timezoneOptions }) => {
225
+ const { values, errors, setFieldValue } = useFormikContext();
226
+ const { formatMessage } = useIntl();
227
+ const [timezoneList, setTimezoneList] = React.useState(timezoneOptions);
228
+ React.useEffect(() => {
229
+ if (values.date) {
230
+ const { timezoneList: timezoneList2 } = getTimezones(new Date(values.date));
231
+ setTimezoneList(timezoneList2);
232
+ const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("-")[1] === values.timezone.split("-")[1]);
233
+ if (updatedTimezone) {
234
+ setFieldValue("timezone", updatedTimezone.value);
235
+ }
236
+ }
237
+ }, [setFieldValue, values.date, values.timezone]);
238
+ return /* @__PURE__ */ jsx(
239
+ Combobox,
240
+ {
241
+ label: formatMessage({
242
+ id: "content-releases.modal.form.input.label.timezone",
243
+ defaultMessage: "Timezone"
244
+ }),
245
+ name: "timezone",
246
+ value: values.timezone || void 0,
247
+ textValue: values.timezone ? values.timezone.replace("-", " ") : void 0,
248
+ onChange: (timezone) => {
249
+ setFieldValue("timezone", timezone);
250
+ },
251
+ onClear: () => {
252
+ setFieldValue("timezone", "");
253
+ },
254
+ error: errors.timezone,
255
+ required: true,
256
+ children: timezoneList.map((timezone) => /* @__PURE__ */ jsx(ComboboxOption, { value: timezone.value, children: timezone.value.replace("-", " ") }, timezone.value))
257
+ }
258
+ );
259
+ };
87
260
  const ReleaseInfoWrapper = styled(Flex)`
88
261
  align-self: stretch;
89
262
  border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
90
263
  border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
91
264
  border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
92
265
  `;
93
- const StyledFlex = styled(Flex)`
94
- align-self: stretch;
95
- cursor: ${({ disabled }) => disabled ? "not-allowed" : "pointer"};
96
-
266
+ const StyledMenuItem = styled(Menu.Item)`
97
267
  svg path {
98
268
  fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
99
269
  }
@@ -118,24 +288,6 @@ const TrashIcon = styled(Trash)`
118
288
  const TypographyMaxWidth = styled(Typography)`
119
289
  max-width: 300px;
120
290
  `;
121
- const PopoverButton = ({ onClick, disabled, children }) => {
122
- return /* @__PURE__ */ jsx(
123
- StyledFlex,
124
- {
125
- paddingTop: 2,
126
- paddingBottom: 2,
127
- paddingLeft: 4,
128
- paddingRight: 4,
129
- alignItems: "center",
130
- gap: 2,
131
- as: "button",
132
- hasRadius: true,
133
- onClick,
134
- disabled,
135
- children
136
- }
137
- );
138
- };
139
291
  const EntryValidationText = ({ action, schema, components, entry }) => {
140
292
  const { formatMessage } = useIntl();
141
293
  const { validate } = unstable_useDocument();
@@ -184,10 +336,8 @@ const ReleaseDetailsLayout = ({
184
336
  toggleWarningSubmit,
185
337
  children
186
338
  }) => {
187
- const { formatMessage } = useIntl();
339
+ const { formatMessage, formatDate, formatTime } = useIntl();
188
340
  const { releaseId } = useParams();
189
- const [isPopoverVisible, setIsPopoverVisible] = React.useState(false);
190
- const moreButtonRef = React.useRef(null);
191
341
  const {
192
342
  data,
193
343
  isLoading: isLoadingDetails,
@@ -203,13 +353,6 @@ const ReleaseDetailsLayout = ({
203
353
  const dispatch = useTypedDispatch();
204
354
  const { trackUsage } = useTracking();
205
355
  const release = data?.data;
206
- const handleTogglePopover = () => {
207
- setIsPopoverVisible((prev) => !prev);
208
- };
209
- const openReleaseModal = () => {
210
- toggleEditReleaseModal();
211
- handleTogglePopover();
212
- };
213
356
  const handlePublishRelease = async () => {
214
357
  const response = await publishRelease({ id: releaseId });
215
358
  if ("data" in response) {
@@ -238,10 +381,6 @@ const ReleaseDetailsLayout = ({
238
381
  });
239
382
  }
240
383
  };
241
- const openWarningConfirmDialog = () => {
242
- toggleWarningSubmit();
243
- handleTogglePopover();
244
- };
245
384
  const handleRefresh = () => {
246
385
  dispatch(releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
247
386
  };
@@ -279,89 +418,138 @@ const ReleaseDetailsLayout = ({
279
418
  }
280
419
  const totalEntries = release.actions.meta.count || 0;
281
420
  const hasCreatedByUser = Boolean(getCreatedByUser());
421
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
422
+ const isScheduled = release.scheduledAt && release.timezone;
423
+ const numberOfEntriesText = formatMessage(
424
+ {
425
+ id: "content-releases.pages.Details.header-subtitle",
426
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
427
+ },
428
+ { number: totalEntries }
429
+ );
430
+ const scheduledText = isScheduled ? formatMessage(
431
+ {
432
+ id: "content-releases.pages.ReleaseDetails.header-subtitle.scheduled",
433
+ defaultMessage: "Scheduled for {date} at {time} ({offset})"
434
+ },
435
+ {
436
+ date: formatDate(new Date(release.scheduledAt), {
437
+ weekday: "long",
438
+ day: "numeric",
439
+ month: "long",
440
+ year: "numeric",
441
+ timeZone: release.timezone
442
+ }),
443
+ time: formatTime(new Date(release.scheduledAt), {
444
+ hour12: false,
445
+ timeZone: release.timezone
446
+ }),
447
+ offset: getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
448
+ }
449
+ ) : "";
282
450
  return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoadingDetails, children: [
283
451
  /* @__PURE__ */ jsx(
284
452
  HeaderLayout,
285
453
  {
286
454
  title: release.name,
287
- subtitle: formatMessage(
288
- {
289
- id: "content-releases.pages.Details.header-subtitle",
290
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
291
- },
292
- { number: totalEntries }
293
- ),
455
+ subtitle: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? `- ${scheduledText}` : ""),
294
456
  navigationAction: /* @__PURE__ */ jsx(Link, { startIcon: /* @__PURE__ */ jsx(ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
295
457
  id: "global.back",
296
458
  defaultMessage: "Back"
297
459
  }) }),
298
460
  primaryAction: !release.releasedAt && /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
299
- /* @__PURE__ */ jsx(
300
- IconButton,
301
- {
302
- label: formatMessage({
303
- id: "content-releases.header.actions.open-release-actions",
304
- defaultMessage: "Release edit and delete menu"
305
- }),
306
- ref: moreButtonRef,
307
- onClick: handleTogglePopover,
308
- children: /* @__PURE__ */ jsx(More, {})
309
- }
310
- ),
311
- isPopoverVisible && /* @__PURE__ */ jsxs(
312
- Popover,
313
- {
314
- source: moreButtonRef,
315
- placement: "bottom-end",
316
- onDismiss: handleTogglePopover,
317
- spacing: 4,
318
- minWidth: "242px",
319
- children: [
320
- /* @__PURE__ */ jsxs(Flex, { alignItems: "center", justifyContent: "center", direction: "column", padding: 1, children: [
321
- /* @__PURE__ */ jsxs(PopoverButton, { disabled: !canUpdate, onClick: openReleaseModal, children: [
322
- /* @__PURE__ */ jsx(PencilIcon, {}),
323
- /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
324
- id: "content-releases.header.actions.edit",
325
- defaultMessage: "Edit"
326
- }) })
327
- ] }),
328
- /* @__PURE__ */ jsxs(PopoverButton, { disabled: !canDelete, onClick: openWarningConfirmDialog, children: [
329
- /* @__PURE__ */ jsx(TrashIcon, {}),
330
- /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
331
- id: "content-releases.header.actions.delete",
332
- defaultMessage: "Delete"
333
- }) })
334
- ] })
335
- ] }),
336
- /* @__PURE__ */ jsxs(
337
- ReleaseInfoWrapper,
338
- {
339
- direction: "column",
340
- justifyContent: "center",
341
- alignItems: "flex-start",
342
- gap: 1,
343
- padding: 5,
344
- children: [
345
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
346
- id: "content-releases.header.actions.created",
347
- defaultMessage: "Created"
348
- }) }),
349
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", color: "neutral300", children: [
350
- /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(release.createdAt) }),
351
- formatMessage(
352
- {
353
- id: "content-releases.header.actions.created.description",
354
- defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
355
- },
356
- { createdBy: getCreatedByUser(), hasCreatedByUser }
357
- )
358
- ] })
359
- ]
360
- }
361
- )
362
- ]
363
- }
364
- ),
461
+ /* @__PURE__ */ jsxs(Menu.Root, { children: [
462
+ /* @__PURE__ */ jsx(
463
+ Menu.Trigger,
464
+ {
465
+ as: IconButton,
466
+ paddingLeft: 2,
467
+ paddingRight: 2,
468
+ "aria-label": formatMessage({
469
+ id: "content-releases.header.actions.open-release-actions",
470
+ defaultMessage: "Release edit and delete menu"
471
+ }),
472
+ icon: /* @__PURE__ */ jsx(More, {}),
473
+ variant: "tertiary"
474
+ }
475
+ ),
476
+ /* @__PURE__ */ jsxs(Menu.Content, { top: 1, popoverPlacement: "bottom-end", children: [
477
+ /* @__PURE__ */ jsxs(
478
+ Flex,
479
+ {
480
+ alignItems: "center",
481
+ justifyContent: "center",
482
+ direction: "column",
483
+ padding: 1,
484
+ width: "100%",
485
+ children: [
486
+ /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(
487
+ Flex,
488
+ {
489
+ paddingTop: 2,
490
+ paddingBottom: 2,
491
+ alignItems: "center",
492
+ gap: 2,
493
+ hasRadius: true,
494
+ width: "100%",
495
+ children: [
496
+ /* @__PURE__ */ jsx(PencilIcon, {}),
497
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
498
+ id: "content-releases.header.actions.edit",
499
+ defaultMessage: "Edit"
500
+ }) })
501
+ ]
502
+ }
503
+ ) }),
504
+ /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxs(
505
+ Flex,
506
+ {
507
+ paddingTop: 2,
508
+ paddingBottom: 2,
509
+ alignItems: "center",
510
+ gap: 2,
511
+ hasRadius: true,
512
+ width: "100%",
513
+ children: [
514
+ /* @__PURE__ */ jsx(TrashIcon, {}),
515
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
516
+ id: "content-releases.header.actions.delete",
517
+ defaultMessage: "Delete"
518
+ }) })
519
+ ]
520
+ }
521
+ ) })
522
+ ]
523
+ }
524
+ ),
525
+ /* @__PURE__ */ jsxs(
526
+ ReleaseInfoWrapper,
527
+ {
528
+ direction: "column",
529
+ justifyContent: "center",
530
+ alignItems: "flex-start",
531
+ gap: 1,
532
+ padding: 5,
533
+ children: [
534
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
535
+ id: "content-releases.header.actions.created",
536
+ defaultMessage: "Created"
537
+ }) }),
538
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", color: "neutral300", children: [
539
+ /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(release.createdAt) }),
540
+ formatMessage(
541
+ {
542
+ id: "content-releases.header.actions.created.description",
543
+ defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
544
+ },
545
+ { createdBy: getCreatedByUser(), hasCreatedByUser }
546
+ )
547
+ ] })
548
+ ]
549
+ }
550
+ )
551
+ ] })
552
+ ] }),
365
553
  /* @__PURE__ */ jsx(Button, { size: "S", variant: "tertiary", onClick: handleRefresh, children: formatMessage({
366
554
  id: "content-releases.header.actions.refresh",
367
555
  defaultMessage: "Refresh"
@@ -417,6 +605,9 @@ const ReleaseDetailsBody = () => {
417
605
  isError: isReleaseError,
418
606
  error: releaseError
419
607
  } = useGetReleaseQuery({ id: releaseId });
608
+ const {
609
+ allowedActions: { canUpdate }
610
+ } = useRBAC(PERMISSIONS);
420
611
  const release = releaseData?.data;
421
612
  const selectedGroupBy = query?.groupBy || "contentType";
422
613
  const {
@@ -523,7 +714,7 @@ const ReleaseDetailsBody = () => {
523
714
  SingleSelect,
524
715
  {
525
716
  "aria-label": formatMessage({
526
- id: "content-releases.pages.ReleaseDetails.groupBy.label",
717
+ id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
527
718
  defaultMessage: "Group by"
528
719
  }),
529
720
  customizeContent: (value) => formatMessage(
@@ -541,7 +732,7 @@ const ReleaseDetailsBody = () => {
541
732
  }
542
733
  ) }),
543
734
  Object.keys(releaseActions).map((key) => /* @__PURE__ */ jsxs(Flex, { gap: 4, direction: "column", alignItems: "stretch", children: [
544
- /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(Badge, { children: key }) }),
735
+ /* @__PURE__ */ jsx(Flex, { role: "separator", "aria-label": key, children: /* @__PURE__ */ jsx(Badge, { children: key }) }),
545
736
  /* @__PURE__ */ jsx(
546
737
  Table.Root,
547
738
  {
@@ -630,7 +821,8 @@ const ReleaseDetailsBody = () => {
630
821
  {
631
822
  selected: type,
632
823
  handleChange: (e) => handleChangeType(e, id, [key, actionIndex]),
633
- name: `release-action-${id}-type`
824
+ name: `release-action-${id}-type`,
825
+ disabled: !canUpdate
634
826
  }
635
827
  ) }),
636
828
  !release.releasedAt && /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -709,11 +901,18 @@ const ReleaseDetailsPage = () => {
709
901
  }
710
902
  );
711
903
  }
712
- const title = isSuccessDetails && data?.data?.name || "";
904
+ const releaseData = isSuccessDetails && data?.data || null;
905
+ const title = releaseData?.name || "";
906
+ const timezone = releaseData?.timezone ?? null;
907
+ const scheduledAt = releaseData?.scheduledAt && timezone ? utcToZonedTime(releaseData.scheduledAt, timezone) : null;
908
+ const date = scheduledAt ? new Date(format(scheduledAt, "yyyy-MM-dd")) : null;
909
+ const time = scheduledAt ? format(scheduledAt, "HH:mm") : "";
713
910
  const handleEditRelease = async (values) => {
714
911
  const response = await updateRelease({
715
912
  id: releaseId,
716
- name: values.name
913
+ name: values.name,
914
+ scheduledAt: values.scheduledAt,
915
+ timezone: values.timezone
717
916
  });
718
917
  if ("data" in response) {
719
918
  toggleNotification({
@@ -767,7 +966,14 @@ const ReleaseDetailsPage = () => {
767
966
  handleClose: toggleEditReleaseModal,
768
967
  handleSubmit: handleEditRelease,
769
968
  isLoading: isLoadingDetails || isSubmittingForm,
770
- initialValues: { name: title || "" }
969
+ initialValues: {
970
+ name: title || "",
971
+ scheduledAt,
972
+ date,
973
+ time,
974
+ isScheduled: Boolean(scheduledAt),
975
+ timezone
976
+ }
771
977
  }
772
978
  ),
773
979
  /* @__PURE__ */ jsx(
@@ -792,6 +998,7 @@ const LinkCard = styled(Link$2)`
792
998
  `;
793
999
  const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
794
1000
  const { formatMessage } = useIntl();
1001
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
795
1002
  if (isError) {
796
1003
  return /* @__PURE__ */ jsx(AnErrorOccurred, {});
797
1004
  }
@@ -812,7 +1019,7 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
812
1019
  }
813
1020
  );
814
1021
  }
815
- return /* @__PURE__ */ jsx(Grid, { gap: 4, children: releases.map(({ id, name, actions }) => /* @__PURE__ */ jsx(GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxs(
1022
+ return /* @__PURE__ */ jsx(Grid, { gap: 4, children: releases.map(({ id, name, actions, scheduledAt }) => /* @__PURE__ */ jsx(GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxs(
816
1023
  Flex,
817
1024
  {
818
1025
  direction: "column",
@@ -827,7 +1034,10 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
827
1034
  gap: 2,
828
1035
  children: [
829
1036
  /* @__PURE__ */ jsx(Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
830
- /* @__PURE__ */ jsx(Typography, { variant: "pi", children: formatMessage(
1037
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: IsSchedulingEnabled ? scheduledAt ? /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
1038
+ id: "content-releases.pages.Releases.not-scheduled",
1039
+ defaultMessage: "Not scheduled"
1040
+ }) : formatMessage(
831
1041
  {
832
1042
  id: "content-releases.page.Releases.release-item.entries",
833
1043
  defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
@@ -847,7 +1057,13 @@ const StyledAlert = styled(Alert)`
847
1057
  }
848
1058
  `;
849
1059
  const INITIAL_FORM_VALUES = {
850
- name: ""
1060
+ name: "",
1061
+ date: null,
1062
+ time: "",
1063
+ // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
1064
+ isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
1065
+ scheduledAt: null,
1066
+ timezone: null
851
1067
  };
852
1068
  const ReleasesPage = () => {
853
1069
  const tabRef = React.useRef(null);
@@ -907,9 +1123,11 @@ const ReleasesPage = () => {
907
1123
  }
908
1124
  });
909
1125
  };
910
- const handleAddRelease = async (values) => {
1126
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
911
1127
  const response2 = await createRelease({
912
- name: values.name
1128
+ name,
1129
+ scheduledAt,
1130
+ timezone
913
1131
  });
914
1132
  if ("data" in response2) {
915
1133
  toggleNotification({
@@ -1071,4 +1289,4 @@ const App = () => {
1071
1289
  export {
1072
1290
  App
1073
1291
  };
1074
- //# sourceMappingURL=App-M11U5GPh.mjs.map
1292
+ //# sourceMappingURL=App-eVYKuxUc.mjs.map