@strapi/content-releases 0.0.0-experimental.d4cb32ce579e12a4436d68036f2327132fba1309 → 0.0.0-experimental.d53e940834bf72ddc725f1d2fd36dac9abec30cb

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 (110) hide show
  1. package/dist/_chunks/{App-xAkiD42p.mjs → App-B2R2exNT.mjs} +656 -625
  2. package/dist/_chunks/App-B2R2exNT.mjs.map +1 -0
  3. package/dist/_chunks/{App-OK4Xac-O.js → App-CEwOQkKT.js} +671 -641
  4. package/dist/_chunks/App-CEwOQkKT.js.map +1 -0
  5. package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases-Be3acS2L.js} +8 -7
  6. package/dist/_chunks/PurchaseContentReleases-Be3acS2L.js.map +1 -0
  7. package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-_MxP6-Dt.mjs} +9 -8
  8. package/dist/_chunks/PurchaseContentReleases-_MxP6-Dt.mjs.map +1 -0
  9. package/dist/_chunks/{en-veqvqeEr.mjs → en-B9Ur3VsE.mjs} +14 -5
  10. package/dist/_chunks/en-B9Ur3VsE.mjs.map +1 -0
  11. package/dist/_chunks/{en-r0otWaln.js → en-DtFJ5ViE.js} +14 -5
  12. package/dist/_chunks/en-DtFJ5ViE.js.map +1 -0
  13. package/dist/_chunks/{index-JvA2_26n.js → index-BrWv-zV4.js} +258 -244
  14. package/dist/_chunks/index-BrWv-zV4.js.map +1 -0
  15. package/dist/_chunks/{index-exoiSU3V.mjs → index-DbmynICx.mjs} +264 -248
  16. package/dist/_chunks/index-DbmynICx.mjs.map +1 -0
  17. package/dist/admin/index.js +1 -15
  18. package/dist/admin/index.js.map +1 -1
  19. package/dist/admin/index.mjs +2 -16
  20. package/dist/admin/index.mjs.map +1 -1
  21. package/dist/admin/src/components/CMReleasesContainer.d.ts +22 -0
  22. package/dist/admin/src/components/RelativeTime.d.ts +28 -0
  23. package/dist/admin/src/components/ReleaseAction.d.ts +3 -0
  24. package/dist/admin/src/components/ReleaseActionMenu.d.ts +26 -0
  25. package/dist/admin/src/components/ReleaseActionOptions.d.ts +9 -0
  26. package/dist/admin/src/components/ReleaseListCell.d.ts +0 -0
  27. package/dist/admin/src/components/ReleaseModal.d.ts +17 -0
  28. package/dist/admin/src/constants.d.ts +58 -0
  29. package/dist/admin/src/index.d.ts +3 -0
  30. package/dist/admin/src/pages/App.d.ts +1 -0
  31. package/dist/admin/src/pages/PurchaseContentReleases.d.ts +2 -0
  32. package/dist/admin/src/pages/ReleaseDetailsPage.d.ts +2 -0
  33. package/dist/admin/src/pages/ReleasesPage.d.ts +8 -0
  34. package/dist/admin/src/pages/tests/mockReleaseDetailsPageData.d.ts +181 -0
  35. package/dist/admin/src/pages/tests/mockReleasesPageData.d.ts +39 -0
  36. package/dist/admin/src/pluginId.d.ts +1 -0
  37. package/dist/admin/src/services/release.d.ts +105 -0
  38. package/dist/admin/src/store/hooks.d.ts +7 -0
  39. package/dist/admin/src/utils/api.d.ts +6 -0
  40. package/dist/admin/src/utils/prefixPluginTranslations.d.ts +3 -0
  41. package/dist/admin/src/utils/time.d.ts +1 -0
  42. package/dist/server/index.js +675 -232
  43. package/dist/server/index.js.map +1 -1
  44. package/dist/server/index.mjs +675 -232
  45. package/dist/server/index.mjs.map +1 -1
  46. package/dist/server/src/bootstrap.d.ts +5 -0
  47. package/dist/server/src/bootstrap.d.ts.map +1 -0
  48. package/dist/server/src/constants.d.ts +12 -0
  49. package/dist/server/src/constants.d.ts.map +1 -0
  50. package/dist/server/src/content-types/index.d.ts +99 -0
  51. package/dist/server/src/content-types/index.d.ts.map +1 -0
  52. package/dist/server/src/content-types/release/index.d.ts +48 -0
  53. package/dist/server/src/content-types/release/index.d.ts.map +1 -0
  54. package/dist/server/src/content-types/release/schema.d.ts +47 -0
  55. package/dist/server/src/content-types/release/schema.d.ts.map +1 -0
  56. package/dist/server/src/content-types/release-action/index.d.ts +50 -0
  57. package/dist/server/src/content-types/release-action/index.d.ts.map +1 -0
  58. package/dist/server/src/content-types/release-action/schema.d.ts +49 -0
  59. package/dist/server/src/content-types/release-action/schema.d.ts.map +1 -0
  60. package/dist/server/src/controllers/index.d.ts +20 -0
  61. package/dist/server/src/controllers/index.d.ts.map +1 -0
  62. package/dist/server/src/controllers/release-action.d.ts +10 -0
  63. package/dist/server/src/controllers/release-action.d.ts.map +1 -0
  64. package/dist/server/src/controllers/release.d.ts +12 -0
  65. package/dist/server/src/controllers/release.d.ts.map +1 -0
  66. package/dist/server/src/controllers/validation/release-action.d.ts +8 -0
  67. package/dist/server/src/controllers/validation/release-action.d.ts.map +1 -0
  68. package/dist/server/src/controllers/validation/release.d.ts +2 -0
  69. package/dist/server/src/controllers/validation/release.d.ts.map +1 -0
  70. package/dist/server/src/destroy.d.ts +5 -0
  71. package/dist/server/src/destroy.d.ts.map +1 -0
  72. package/dist/server/src/index.d.ts +2096 -0
  73. package/dist/server/src/index.d.ts.map +1 -0
  74. package/dist/server/src/migrations/index.d.ts +13 -0
  75. package/dist/server/src/migrations/index.d.ts.map +1 -0
  76. package/dist/server/src/register.d.ts +5 -0
  77. package/dist/server/src/register.d.ts.map +1 -0
  78. package/dist/server/src/routes/index.d.ts +35 -0
  79. package/dist/server/src/routes/index.d.ts.map +1 -0
  80. package/dist/server/src/routes/release-action.d.ts +18 -0
  81. package/dist/server/src/routes/release-action.d.ts.map +1 -0
  82. package/dist/server/src/routes/release.d.ts +18 -0
  83. package/dist/server/src/routes/release.d.ts.map +1 -0
  84. package/dist/server/src/services/index.d.ts +1826 -0
  85. package/dist/server/src/services/index.d.ts.map +1 -0
  86. package/dist/server/src/services/release.d.ts +66 -0
  87. package/dist/server/src/services/release.d.ts.map +1 -0
  88. package/dist/server/src/services/scheduling.d.ts +18 -0
  89. package/dist/server/src/services/scheduling.d.ts.map +1 -0
  90. package/dist/server/src/services/validation.d.ts +18 -0
  91. package/dist/server/src/services/validation.d.ts.map +1 -0
  92. package/dist/server/src/utils/index.d.ts +14 -0
  93. package/dist/server/src/utils/index.d.ts.map +1 -0
  94. package/dist/shared/contracts/release-actions.d.ts +131 -0
  95. package/dist/shared/contracts/release-actions.d.ts.map +1 -0
  96. package/dist/shared/contracts/releases.d.ts +182 -0
  97. package/dist/shared/contracts/releases.d.ts.map +1 -0
  98. package/dist/shared/types.d.ts +24 -0
  99. package/dist/shared/types.d.ts.map +1 -0
  100. package/dist/shared/validation-schemas.d.ts +2 -0
  101. package/dist/shared/validation-schemas.d.ts.map +1 -0
  102. package/package.json +29 -36
  103. package/dist/_chunks/App-OK4Xac-O.js.map +0 -1
  104. package/dist/_chunks/App-xAkiD42p.mjs.map +0 -1
  105. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
  106. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
  107. package/dist/_chunks/en-r0otWaln.js.map +0 -1
  108. package/dist/_chunks/en-veqvqeEr.mjs.map +0 -1
  109. package/dist/_chunks/index-JvA2_26n.js.map +0 -1
  110. package/dist/_chunks/index-exoiSU3V.mjs.map +0 -1
@@ -1,23 +1,51 @@
1
- import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { useNotification, useAPIErrorHandler, LoadingIndicatorPage, ConfirmDialog, useRBAC, useTracking, RelativeTime, CheckPermissions, useQueryParams, AnErrorOccurred, NoContent, Table, PageSizeURLQuery, PaginationURLQuery, CheckPagePermissions } from "@strapi/helper-plugin";
3
- import { useLocation, useParams, useHistory, Redirect, Link as Link$1, Switch, Route } from "react-router-dom";
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-exoiSU3V.mjs";
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { useNotification, useAPIErrorHandler, useQueryParams, useTracking, useRBAC, Page, Layouts, Pagination, isFetchError, ConfirmDialog, BackButton, useStrapiApp, Table } from "@strapi/admin/strapi-admin";
3
+ import { useLocation, useNavigate, NavLink, useParams, Navigate, Link as Link$1, Routes, Route } from "react-router-dom";
4
+ import { g as getTimezoneOffset, p as pluginId, u as useGetReleasesQuery, a as useCreateReleaseMutation, P as PERMISSIONS, b as useGetReleaseQuery, c as useUpdateReleaseMutation, d as useDeleteReleaseMutation, e as usePublishReleaseMutation, f as useGetReleaseActionsQuery, h as useUpdateReleaseActionMutation, R as ReleaseActionOptions, i as ReleaseActionMenu, r as releaseApi } from "./index-DbmynICx.mjs";
5
5
  import * as React from "react";
6
- import { unstable_useDocument, useLicenseLimits } from "@strapi/admin/strapi-admin";
7
- import { ModalLayout, ModalHeader, Typography, ModalBody, Flex, TextInput, Box, Checkbox, 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
- import { Pencil, Trash, ArrowLeft, More, CrossCircle, CheckCircle, Plus, EmptyDocuments } from "@strapi/icons";
6
+ import { unstable_useDocument } from "@strapi/content-manager/strapi-admin";
7
+ import { Modal, Flex, Field, TextInput, Box, Checkbox, Typography, DatePicker, TimePicker, Button, Combobox, ComboboxOption, Link, Alert, Main, Tabs, Divider, EmptyStateLayout, Grid, Badge, Menu, Dialog, LinkButton, SingleSelect, SingleSelectOption, Tr, Td, Tooltip } from "@strapi/design-system";
8
+ import { Plus, Pencil, Trash, More, CrossCircle, CheckCircle } from "@strapi/icons";
9
+ import { EmptyDocuments } from "@strapi/icons/symbols";
10
10
  import format from "date-fns/format";
11
- import { zonedTimeToUtc, utcToZonedTime } from "date-fns-tz";
11
+ import { utcToZonedTime, zonedTimeToUtc } from "date-fns-tz";
12
12
  import { useIntl } from "react-intl";
13
- import styled from "styled-components";
14
- import { formatISO, parse } from "date-fns";
13
+ import { styled } from "styled-components";
14
+ import { intervalToDuration, isPast, formatISO } from "date-fns";
15
15
  import { Formik, Form, useFormikContext } from "formik";
16
16
  import * as yup from "yup";
17
- import "@reduxjs/toolkit/query";
18
- import "axios";
19
- import "@reduxjs/toolkit/query/react";
20
- import "react-redux";
17
+ import { useDispatch } from "react-redux";
18
+ import { useLicenseLimits } from "@strapi/admin/strapi-admin/ee";
19
+ const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
20
+ const RelativeTime$1 = React.forwardRef(
21
+ ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
22
+ const { formatRelativeTime, formatDate, formatTime } = useIntl();
23
+ const interval = intervalToDuration({
24
+ start: timestamp,
25
+ end: Date.now()
26
+ // see https://github.com/date-fns/date-fns/issues/2891 – No idea why it's all partial it returns it every time.
27
+ });
28
+ const unit = intervals.find((intervalUnit) => {
29
+ return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
30
+ });
31
+ const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
32
+ const customInterval = customIntervals.find(
33
+ (custom) => interval[custom.unit] < custom.threshold
34
+ );
35
+ const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, { numeric: "auto" });
36
+ return /* @__PURE__ */ jsx(
37
+ "time",
38
+ {
39
+ ref: forwardedRef,
40
+ dateTime: timestamp.toISOString(),
41
+ role: "time",
42
+ title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
43
+ ...restProps,
44
+ children: displayText
45
+ }
46
+ );
47
+ }
48
+ );
21
49
  const RELEASE_SCHEMA = yup.object().shape({
22
50
  name: yup.string().trim().required(),
23
51
  scheduledAt: yup.string().nullable(),
@@ -40,6 +68,7 @@ const RELEASE_SCHEMA = yup.object().shape({
40
68
  }).required().noUnknown();
41
69
  const ReleaseModal = ({
42
70
  handleClose,
71
+ open,
43
72
  handleSubmit,
44
73
  initialValues,
45
74
  isLoading = false
@@ -47,7 +76,6 @@ const ReleaseModal = ({
47
76
  const { formatMessage } = useIntl();
48
77
  const { pathname } = useLocation();
49
78
  const isCreatingRelease = pathname === `/plugins/${pluginId}`;
50
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
51
79
  const { timezoneList, systemTimezone = { value: "UTC+00:00-Africa/Abidjan " } } = getTimezones(
52
80
  initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : /* @__PURE__ */ new Date()
53
81
  );
@@ -55,18 +83,17 @@ const ReleaseModal = ({
55
83
  const { date, time, timezone } = values;
56
84
  if (!date || !time || !timezone)
57
85
  return null;
58
- const formattedDate = parse(time, "HH:mm", new Date(date));
59
- const timezoneWithoutOffset = timezone.split("_")[1];
60
- return zonedTimeToUtc(formattedDate, timezoneWithoutOffset);
86
+ const timezoneWithoutOffset = timezone.split("&")[1];
87
+ return zonedTimeToUtc(`${date} ${time}`, timezoneWithoutOffset);
61
88
  };
62
89
  const getTimezoneWithOffset = () => {
63
90
  const currentTimezone = timezoneList.find(
64
- (timezone) => timezone.value.split("_")[1] === initialValues.timezone
91
+ (timezone) => timezone.value.split("&")[1] === initialValues.timezone
65
92
  );
66
93
  return currentTimezone?.value || systemTimezone.value;
67
94
  };
68
- return /* @__PURE__ */ jsxs(ModalLayout, { onClose: handleClose, labelledBy: "title", children: [
69
- /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { id: "title", fontWeight: "bold", textColor: "neutral800", children: formatMessage(
95
+ return /* @__PURE__ */ jsx(Modal.Root, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
96
+ /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: formatMessage(
70
97
  {
71
98
  id: "content-releases.modal.title",
72
99
  defaultMessage: "{isCreatingRelease, select, true {New release} other {Edit release}}"
@@ -79,7 +106,7 @@ const ReleaseModal = ({
79
106
  onSubmit: (values) => {
80
107
  handleSubmit({
81
108
  ...values,
82
- timezone: values.timezone ? values.timezone.split("_")[1] : null,
109
+ timezone: values.timezone ? values.timezone.split("&")[1] : null,
83
110
  scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
84
111
  });
85
112
  },
@@ -89,31 +116,25 @@ const ReleaseModal = ({
89
116
  },
90
117
  validationSchema: RELEASE_SCHEMA,
91
118
  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({
119
+ children: ({ values, errors, handleChange, setFieldValue }) => {
120
+ return /* @__PURE__ */ jsxs(Form, { children: [
121
+ /* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
122
+ /* @__PURE__ */ jsxs(Field.Root, { name: "name", error: errors.name, required: true, children: [
123
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
98
124
  id: "content-releases.modal.form.input.label.release-name",
99
125
  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: [
126
+ }) }),
127
+ /* @__PURE__ */ jsx(TextInput, { value: values.name, onChange: handleChange }),
128
+ /* @__PURE__ */ jsx(Field.Error, {})
129
+ ] }),
109
130
  /* @__PURE__ */ jsx(Box, { width: "max-content", children: /* @__PURE__ */ jsx(
110
131
  Checkbox,
111
132
  {
112
133
  name: "isScheduled",
113
- value: values.isScheduled,
114
- onChange: (event) => {
115
- setFieldValue("isScheduled", event.target.checked);
116
- if (!event.target.checked) {
134
+ checked: values.isScheduled,
135
+ onCheckedChange: (checked) => {
136
+ setFieldValue("isScheduled", checked);
137
+ if (!checked) {
117
138
  setFieldValue("date", null);
118
139
  setFieldValue("time", "");
119
140
  setFieldValue("timezone", null);
@@ -141,83 +162,81 @@ const ReleaseModal = ({
141
162
  ) }),
142
163
  values.isScheduled && /* @__PURE__ */ jsxs(Fragment, { children: [
143
164
  /* @__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
- ) })
165
+ /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsxs(Field.Root, { name: "date", error: errors.date, required: true, children: [
166
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
167
+ id: "content-releases.modal.form.input.label.date",
168
+ defaultMessage: "Date"
169
+ }) }),
170
+ /* @__PURE__ */ jsx(
171
+ DatePicker,
172
+ {
173
+ onChange: (date) => {
174
+ const isoFormatDate = date ? formatISO(date, { representation: "date" }) : null;
175
+ setFieldValue("date", isoFormatDate);
176
+ },
177
+ clearLabel: formatMessage({
178
+ id: "content-releases.modal.form.input.clearLabel",
179
+ defaultMessage: "Clear"
180
+ }),
181
+ onClear: () => {
182
+ setFieldValue("date", null);
183
+ },
184
+ value: values.date ? new Date(values.date) : /* @__PURE__ */ new Date(),
185
+ minDate: utcToZonedTime(/* @__PURE__ */ new Date(), values.timezone.split("&")[1])
186
+ }
187
+ ),
188
+ /* @__PURE__ */ jsx(Field.Error, {})
189
+ ] }) }),
190
+ /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsxs(Field.Root, { name: "time", error: errors.time, required: true, children: [
191
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
192
+ id: "content-releases.modal.form.input.label.time",
193
+ defaultMessage: "Time"
194
+ }) }),
195
+ /* @__PURE__ */ jsx(
196
+ TimePicker,
197
+ {
198
+ onChange: (time) => {
199
+ setFieldValue("time", time);
200
+ },
201
+ clearLabel: formatMessage({
202
+ id: "content-releases.modal.form.input.clearLabel",
203
+ defaultMessage: "Clear"
204
+ }),
205
+ onClear: () => {
206
+ setFieldValue("time", "");
207
+ },
208
+ value: values.time || void 0
209
+ }
210
+ ),
211
+ /* @__PURE__ */ jsx(Field.Error, {})
212
+ ] }) })
191
213
  ] }),
192
214
  /* @__PURE__ */ jsx(TimezoneComponent, { timezoneOptions: timezoneList })
193
215
  ] })
194
- ] })
195
- ] }) }),
196
- /* @__PURE__ */ jsx(
197
- ModalFooter,
198
- {
199
- startActions: /* @__PURE__ */ jsx(Button, { onClick: handleClose, variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }),
200
- endActions: /* @__PURE__ */ jsx(Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
216
+ ] }) }),
217
+ /* @__PURE__ */ jsxs(Modal.Footer, { children: [
218
+ /* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }) }),
219
+ /* @__PURE__ */ jsx(Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
201
220
  {
202
221
  id: "content-releases.modal.form.button.submit",
203
222
  defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
204
223
  },
205
224
  { isCreatingRelease }
206
225
  ) })
207
- }
208
- )
209
- ] })
226
+ ] })
227
+ ] });
228
+ }
210
229
  }
211
230
  )
212
- ] });
231
+ ] }) });
213
232
  };
214
233
  const getTimezones = (selectedDate) => {
215
234
  const timezoneList = Intl.supportedValuesOf("timeZone").map((timezone) => {
216
235
  const utcOffset = getTimezoneOffset(timezone, selectedDate);
217
- return { offset: utcOffset, value: `${utcOffset}_${timezone}` };
236
+ return { offset: utcOffset, value: `${utcOffset}&${timezone}` };
218
237
  });
219
238
  const systemTimezone = timezoneList.find(
220
- (timezone) => timezone.value.split("_")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
239
+ (timezone) => timezone.value.split("&")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
221
240
  );
222
241
  return { timezoneList, systemTimezone };
223
242
  };
@@ -229,33 +248,341 @@ const TimezoneComponent = ({ timezoneOptions }) => {
229
248
  if (values.date) {
230
249
  const { timezoneList: timezoneList2 } = getTimezones(new Date(values.date));
231
250
  setTimezoneList(timezoneList2);
232
- const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("_")[1] === values.timezone.split("_")[1]);
251
+ const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("&")[1] === values.timezone.split("&")[1]);
233
252
  if (updatedTimezone) {
234
253
  setFieldValue("timezone", updatedTimezone.value);
235
254
  }
236
255
  }
237
256
  }, [setFieldValue, values.date, values.timezone]);
238
- return /* @__PURE__ */ jsx(
239
- Combobox,
257
+ return /* @__PURE__ */ jsxs(Field.Root, { name: "timezone", error: errors.timezone, required: true, children: [
258
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
259
+ id: "content-releases.modal.form.input.label.timezone",
260
+ defaultMessage: "Timezone"
261
+ }) }),
262
+ /* @__PURE__ */ jsx(
263
+ Combobox,
264
+ {
265
+ autocomplete: { type: "list", filter: "contains" },
266
+ value: values.timezone || void 0,
267
+ textValue: values.timezone ? values.timezone.replace(/&/, " ") : void 0,
268
+ onChange: (timezone) => {
269
+ setFieldValue("timezone", timezone);
270
+ },
271
+ onTextValueChange: (timezone) => {
272
+ setFieldValue("timezone", timezone);
273
+ },
274
+ onClear: () => {
275
+ setFieldValue("timezone", "");
276
+ },
277
+ children: timezoneList.map((timezone) => /* @__PURE__ */ jsx(ComboboxOption, { value: timezone.value, children: timezone.value.replace(/&/, " ") }, timezone.value))
278
+ }
279
+ ),
280
+ /* @__PURE__ */ jsx(Field.Error, {})
281
+ ] });
282
+ };
283
+ const useTypedDispatch = useDispatch;
284
+ const isBaseQueryError = (error) => {
285
+ return typeof error !== "undefined" && error.name !== void 0;
286
+ };
287
+ const LinkCard = styled(Link)`
288
+ display: block;
289
+ `;
290
+ const RelativeTime = styled(RelativeTime$1)`
291
+ display: inline-block;
292
+ &::first-letter {
293
+ text-transform: uppercase;
294
+ }
295
+ `;
296
+ const getBadgeProps = (status) => {
297
+ let color;
298
+ switch (status) {
299
+ case "ready":
300
+ color = "success";
301
+ break;
302
+ case "blocked":
303
+ color = "warning";
304
+ break;
305
+ case "failed":
306
+ color = "danger";
307
+ break;
308
+ case "done":
309
+ color = "primary";
310
+ break;
311
+ case "empty":
312
+ default:
313
+ color = "neutral";
314
+ }
315
+ return {
316
+ textColor: `${color}600`,
317
+ backgroundColor: `${color}100`,
318
+ borderColor: `${color}200`
319
+ };
320
+ };
321
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
322
+ const { formatMessage } = useIntl();
323
+ if (isError) {
324
+ return /* @__PURE__ */ jsx(Page.Error, {});
325
+ }
326
+ if (releases?.length === 0) {
327
+ return /* @__PURE__ */ jsx(
328
+ EmptyStateLayout,
329
+ {
330
+ content: formatMessage(
331
+ {
332
+ id: "content-releases.page.Releases.tab.emptyEntries",
333
+ defaultMessage: "No releases"
334
+ },
335
+ {
336
+ target: sectionTitle
337
+ }
338
+ ),
339
+ icon: /* @__PURE__ */ jsx(EmptyDocuments, { width: "16rem" })
340
+ }
341
+ );
342
+ }
343
+ return /* @__PURE__ */ jsx(Grid.Root, { gap: 4, children: releases.map(({ id, name, scheduledAt, status }) => /* @__PURE__ */ jsx(Grid.Item, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsx(LinkCard, { tag: NavLink, to: `${id}`, isExternal: false, children: /* @__PURE__ */ jsxs(
344
+ Flex,
240
345
  {
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))
346
+ direction: "column",
347
+ justifyContent: "space-between",
348
+ padding: 4,
349
+ hasRadius: true,
350
+ background: "neutral0",
351
+ shadow: "tableShadow",
352
+ height: "100%",
353
+ width: "100%",
354
+ alignItems: "start",
355
+ gap: 4,
356
+ children: [
357
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", gap: 1, children: [
358
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", tag: "h3", variant: "delta", fontWeight: "bold", children: name }),
359
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: scheduledAt ? /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
360
+ id: "content-releases.pages.Releases.not-scheduled",
361
+ defaultMessage: "Not scheduled"
362
+ }) })
363
+ ] }),
364
+ /* @__PURE__ */ jsx(Badge, { ...getBadgeProps(status), children: status })
365
+ ]
257
366
  }
258
- );
367
+ ) }) }, id)) });
368
+ };
369
+ const StyledAlert = styled(Alert)`
370
+ button {
371
+ display: none;
372
+ }
373
+ p + div {
374
+ margin-left: auto;
375
+ }
376
+ `;
377
+ const INITIAL_FORM_VALUES = {
378
+ name: "",
379
+ date: void 0,
380
+ time: "",
381
+ isScheduled: true,
382
+ scheduledAt: null,
383
+ timezone: null
384
+ };
385
+ const ReleasesPage = () => {
386
+ const location = useLocation();
387
+ const [releaseModalShown, setReleaseModalShown] = React.useState(false);
388
+ const { toggleNotification } = useNotification();
389
+ const { formatMessage } = useIntl();
390
+ const navigate = useNavigate();
391
+ const { formatAPIError } = useAPIErrorHandler();
392
+ const [{ query }, setQuery] = useQueryParams();
393
+ const response = useGetReleasesQuery(query);
394
+ const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
395
+ const { getFeature } = useLicenseLimits();
396
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
397
+ const { trackUsage } = useTracking();
398
+ const {
399
+ allowedActions: { canCreate }
400
+ } = useRBAC(PERMISSIONS);
401
+ const { isLoading, isSuccess, isError } = response;
402
+ const activeTab = response?.currentData?.meta?.activeTab || "pending";
403
+ React.useEffect(() => {
404
+ if (location?.state?.errors) {
405
+ toggleNotification({
406
+ type: "danger",
407
+ title: formatMessage({
408
+ id: "content-releases.pages.Releases.notification.error.title",
409
+ defaultMessage: "Your request could not be processed."
410
+ }),
411
+ message: formatMessage({
412
+ id: "content-releases.pages.Releases.notification.error.message",
413
+ defaultMessage: "Please try again or open another release."
414
+ })
415
+ });
416
+ navigate("", { replace: true, state: null });
417
+ }
418
+ }, [formatMessage, location?.state?.errors, navigate, toggleNotification]);
419
+ const toggleAddReleaseModal = () => {
420
+ setReleaseModalShown((prev) => !prev);
421
+ };
422
+ if (isLoading) {
423
+ return /* @__PURE__ */ jsx(Page.Loading, {});
424
+ }
425
+ const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
426
+ const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
427
+ const handleTabChange = (tabValue) => {
428
+ setQuery({
429
+ ...query,
430
+ page: 1,
431
+ pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
432
+ filters: {
433
+ releasedAt: {
434
+ $notNull: tabValue !== "pending"
435
+ }
436
+ }
437
+ });
438
+ };
439
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
440
+ const response2 = await createRelease({
441
+ name,
442
+ scheduledAt,
443
+ timezone
444
+ });
445
+ if ("data" in response2) {
446
+ toggleNotification({
447
+ type: "success",
448
+ message: formatMessage({
449
+ id: "content-releases.modal.release-created-notification-success",
450
+ defaultMessage: "Release created."
451
+ })
452
+ });
453
+ trackUsage("didCreateRelease");
454
+ navigate(response2.data.data.id.toString());
455
+ } else if (isFetchError(response2.error)) {
456
+ toggleNotification({
457
+ type: "danger",
458
+ message: formatAPIError(response2.error)
459
+ });
460
+ } else {
461
+ toggleNotification({
462
+ type: "danger",
463
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
464
+ });
465
+ }
466
+ };
467
+ return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoading, children: [
468
+ /* @__PURE__ */ jsx(
469
+ Layouts.Header,
470
+ {
471
+ title: formatMessage({
472
+ id: "content-releases.pages.Releases.title",
473
+ defaultMessage: "Releases"
474
+ }),
475
+ subtitle: formatMessage({
476
+ id: "content-releases.pages.Releases.header-subtitle",
477
+ defaultMessage: "Create and manage content updates"
478
+ }),
479
+ primaryAction: canCreate ? /* @__PURE__ */ jsx(
480
+ Button,
481
+ {
482
+ startIcon: /* @__PURE__ */ jsx(Plus, {}),
483
+ onClick: toggleAddReleaseModal,
484
+ disabled: hasReachedMaximumPendingReleases,
485
+ children: formatMessage({
486
+ id: "content-releases.header.actions.add-release",
487
+ defaultMessage: "New release"
488
+ })
489
+ }
490
+ ) : null
491
+ }
492
+ ),
493
+ /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
494
+ hasReachedMaximumPendingReleases && /* @__PURE__ */ jsx(
495
+ StyledAlert,
496
+ {
497
+ marginBottom: 6,
498
+ action: /* @__PURE__ */ jsx(Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
499
+ id: "content-releases.pages.Releases.max-limit-reached.action",
500
+ defaultMessage: "Explore plans"
501
+ }) }),
502
+ title: formatMessage(
503
+ {
504
+ id: "content-releases.pages.Releases.max-limit-reached.title",
505
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
506
+ },
507
+ { number: maximumReleases }
508
+ ),
509
+ onClose: () => {
510
+ },
511
+ closeLabel: "",
512
+ children: formatMessage({
513
+ id: "content-releases.pages.Releases.max-limit-reached.message",
514
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
515
+ })
516
+ }
517
+ ),
518
+ /* @__PURE__ */ jsxs(Tabs.Root, { variant: "simple", onValueChange: handleTabChange, value: activeTab, children: [
519
+ /* @__PURE__ */ jsxs(Box, { paddingBottom: 8, children: [
520
+ /* @__PURE__ */ jsxs(
521
+ Tabs.List,
522
+ {
523
+ "aria-label": formatMessage({
524
+ id: "content-releases.pages.Releases.tab-group.label",
525
+ defaultMessage: "Releases list"
526
+ }),
527
+ children: [
528
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "pending", children: formatMessage(
529
+ {
530
+ id: "content-releases.pages.Releases.tab.pending",
531
+ defaultMessage: "Pending ({count})"
532
+ },
533
+ {
534
+ count: totalPendingReleases
535
+ }
536
+ ) }),
537
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "done", children: formatMessage({
538
+ id: "content-releases.pages.Releases.tab.done",
539
+ defaultMessage: "Done"
540
+ }) })
541
+ ]
542
+ }
543
+ ),
544
+ /* @__PURE__ */ jsx(Divider, {})
545
+ ] }),
546
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "pending", children: /* @__PURE__ */ jsx(
547
+ ReleasesGrid,
548
+ {
549
+ sectionTitle: "pending",
550
+ releases: response?.currentData?.data,
551
+ isError
552
+ }
553
+ ) }),
554
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "done", children: /* @__PURE__ */ jsx(
555
+ ReleasesGrid,
556
+ {
557
+ sectionTitle: "done",
558
+ releases: response?.currentData?.data,
559
+ isError
560
+ }
561
+ ) })
562
+ ] }),
563
+ /* @__PURE__ */ jsxs(
564
+ Pagination.Root,
565
+ {
566
+ ...response?.currentData?.meta?.pagination,
567
+ defaultPageSize: response?.currentData?.meta?.pagination?.pageSize,
568
+ children: [
569
+ /* @__PURE__ */ jsx(Pagination.PageSize, { options: ["8", "16", "32", "64"] }),
570
+ /* @__PURE__ */ jsx(Pagination.Links, {})
571
+ ]
572
+ }
573
+ )
574
+ ] }) }),
575
+ /* @__PURE__ */ jsx(
576
+ ReleaseModal,
577
+ {
578
+ open: releaseModalShown,
579
+ handleClose: toggleAddReleaseModal,
580
+ handleSubmit: handleAddRelease,
581
+ isLoading: isSubmittingForm,
582
+ initialValues: INITIAL_FORM_VALUES
583
+ }
584
+ )
585
+ ] });
259
586
  };
260
587
  const ReleaseInfoWrapper = styled(Flex)`
261
588
  align-self: stretch;
@@ -270,6 +597,10 @@ const StyledMenuItem = styled(Menu.Item)`
270
597
  span {
271
598
  color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
272
599
  }
600
+
601
+ &:hover {
602
+ background: ${({ theme, $variant = "neutral" }) => theme.colors[`${$variant}100`]};
603
+ }
273
604
  `;
274
605
  const PencilIcon = styled(Pencil)`
275
606
  width: ${({ theme }) => theme.spaces[3]};
@@ -288,29 +619,34 @@ const TrashIcon = styled(Trash)`
288
619
  const TypographyMaxWidth = styled(Typography)`
289
620
  max-width: 300px;
290
621
  `;
291
- const EntryValidationText = ({ action, schema, components, entry }) => {
622
+ const EntryValidationText = ({ action, schema, entry }) => {
292
623
  const { formatMessage } = useIntl();
293
- const { validate } = unstable_useDocument();
294
- const { errors } = validate(entry, {
295
- contentType: schema,
296
- components,
297
- isCreatingEntry: false
298
- });
624
+ const { validate } = unstable_useDocument(
625
+ {
626
+ collectionType: schema?.kind ?? "",
627
+ model: schema?.uid ?? ""
628
+ },
629
+ {
630
+ skip: !schema
631
+ }
632
+ );
633
+ const errors = validate(entry) ?? {};
299
634
  if (Object.keys(errors).length > 0) {
300
635
  const validationErrorsMessages = Object.entries(errors).map(
301
636
  ([key, value]) => formatMessage(
637
+ // @ts-expect-error – TODO: fix this will better checks
302
638
  { id: `${value.id}.withField`, defaultMessage: value.defaultMessage },
303
639
  { field: key }
304
640
  )
305
641
  ).join(" ");
306
642
  return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
307
- /* @__PURE__ */ jsx(Icon, { color: "danger600", as: CrossCircle }),
643
+ /* @__PURE__ */ jsx(CrossCircle, { fill: "danger600" }),
308
644
  /* @__PURE__ */ jsx(Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
309
645
  ] });
310
646
  }
311
647
  if (action == "publish") {
312
648
  return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
313
- /* @__PURE__ */ jsx(Icon, { color: "success600", as: CheckCircle }),
649
+ /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
314
650
  entry.publishedAt ? /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
315
651
  id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
316
652
  defaultMessage: "Already published"
@@ -321,7 +657,7 @@ const EntryValidationText = ({ action, schema, components, entry }) => {
321
657
  ] });
322
658
  }
323
659
  return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
324
- /* @__PURE__ */ jsx(Icon, { color: "success600", as: CheckCircle }),
660
+ /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
325
661
  !entry.publishedAt ? /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
326
662
  id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
327
663
  defaultMessage: "Already unpublished"
@@ -341,20 +677,23 @@ const ReleaseDetailsLayout = ({
341
677
  const {
342
678
  data,
343
679
  isLoading: isLoadingDetails,
344
- isError,
345
680
  error
346
- } = useGetReleaseQuery({ id: releaseId });
681
+ } = useGetReleaseQuery(
682
+ { id: releaseId },
683
+ {
684
+ skip: !releaseId
685
+ }
686
+ );
347
687
  const [publishRelease, { isLoading: isPublishing }] = usePublishReleaseMutation();
348
- const toggleNotification = useNotification();
688
+ const { toggleNotification } = useNotification();
349
689
  const { formatAPIError } = useAPIErrorHandler();
350
- const {
351
- allowedActions: { canUpdate, canDelete }
352
- } = useRBAC(PERMISSIONS);
690
+ const { allowedActions } = useRBAC(PERMISSIONS);
691
+ const { canUpdate, canDelete, canPublish } = allowedActions;
353
692
  const dispatch = useTypedDispatch();
354
693
  const { trackUsage } = useTracking();
355
694
  const release = data?.data;
356
- const handlePublishRelease = async () => {
357
- const response = await publishRelease({ id: releaseId });
695
+ const handlePublishRelease = (id) => async () => {
696
+ const response = await publishRelease({ id });
358
697
  if ("data" in response) {
359
698
  toggleNotification({
360
699
  type: "success",
@@ -369,20 +708,25 @@ const ReleaseDetailsLayout = ({
369
708
  totalPublishedEntries,
370
709
  totalUnpublishedEntries
371
710
  });
372
- } else if (isAxiosError(response.error)) {
711
+ } else if (isFetchError(response.error)) {
373
712
  toggleNotification({
374
- type: "warning",
713
+ type: "danger",
375
714
  message: formatAPIError(response.error)
376
715
  });
377
716
  } else {
378
717
  toggleNotification({
379
- type: "warning",
718
+ type: "danger",
380
719
  message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
381
720
  });
382
721
  }
383
722
  };
384
723
  const handleRefresh = () => {
385
- dispatch(releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
724
+ dispatch(
725
+ releaseApi.util.invalidateTags([
726
+ { type: "ReleaseAction", id: "LIST" },
727
+ { type: "Release", id: releaseId }
728
+ ])
729
+ );
386
730
  };
387
731
  const getCreatedByUser = () => {
388
732
  if (!release?.createdBy) {
@@ -397,28 +741,26 @@ const ReleaseDetailsLayout = ({
397
741
  return release.createdBy.email;
398
742
  };
399
743
  if (isLoadingDetails) {
400
- return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
744
+ return /* @__PURE__ */ jsx(Page.Loading, {});
401
745
  }
402
- if (isError || !release) {
746
+ if (isBaseQueryError(error) && "code" in error || !release) {
403
747
  return /* @__PURE__ */ jsx(
404
- Redirect,
748
+ Navigate,
405
749
  {
406
- to: {
407
- pathname: "/plugins/content-releases",
408
- state: {
409
- errors: [
410
- {
411
- code: error?.code
412
- }
413
- ]
414
- }
750
+ to: "..",
751
+ state: {
752
+ errors: [
753
+ {
754
+ // @ts-expect-error – TODO: fix this weird error flow
755
+ code: error?.code
756
+ }
757
+ ]
415
758
  }
416
759
  }
417
760
  );
418
761
  }
419
762
  const totalEntries = release.actions.meta.count || 0;
420
763
  const hasCreatedByUser = Boolean(getCreatedByUser());
421
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
422
764
  const isScheduled = release.scheduledAt && release.timezone;
423
765
  const numberOfEntriesText = formatMessage(
424
766
  {
@@ -449,31 +791,30 @@ const ReleaseDetailsLayout = ({
449
791
  ) : "";
450
792
  return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoadingDetails, children: [
451
793
  /* @__PURE__ */ jsx(
452
- HeaderLayout,
794
+ Layouts.Header,
453
795
  {
454
796
  title: release.name,
455
- subtitle: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? ` - ${scheduledText}` : ""),
456
- navigationAction: /* @__PURE__ */ jsx(Link, { startIcon: /* @__PURE__ */ jsx(ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
457
- id: "global.back",
458
- defaultMessage: "Back"
459
- }) }),
797
+ subtitle: /* @__PURE__ */ jsxs(Flex, { gap: 2, lineHeight: 6, children: [
798
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", variant: "epsilon", children: numberOfEntriesText + (isScheduled ? ` - ${scheduledText}` : "") }),
799
+ /* @__PURE__ */ jsx(Badge, { ...getBadgeProps(release.status), children: release.status })
800
+ ] }),
801
+ navigationAction: /* @__PURE__ */ jsx(BackButton, {}),
460
802
  primaryAction: !release.releasedAt && /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
461
803
  /* @__PURE__ */ jsxs(Menu.Root, { children: [
462
804
  /* @__PURE__ */ jsx(
463
805
  Menu.Trigger,
464
806
  {
465
- as: IconButton,
466
807
  paddingLeft: 2,
467
808
  paddingRight: 2,
468
809
  "aria-label": formatMessage({
469
810
  id: "content-releases.header.actions.open-release-actions",
470
811
  defaultMessage: "Release edit and delete menu"
471
812
  }),
472
- icon: /* @__PURE__ */ jsx(More, {}),
473
- variant: "tertiary"
813
+ variant: "tertiary",
814
+ children: /* @__PURE__ */ jsx(More, {})
474
815
  }
475
816
  ),
476
- /* @__PURE__ */ jsxs(Menu.Content, { top: 1, popoverPlacement: "bottom-end", children: [
817
+ /* @__PURE__ */ jsxs(Menu.Content, { top: 1, popoverPlacement: "bottom-end", maxHeight: void 0, children: [
477
818
  /* @__PURE__ */ jsxs(
478
819
  Flex,
479
820
  {
@@ -483,42 +824,28 @@ const ReleaseDetailsLayout = ({
483
824
  padding: 1,
484
825
  width: "100%",
485
826
  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,
827
+ /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
828
+ /* @__PURE__ */ jsx(PencilIcon, {}),
829
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
830
+ id: "content-releases.header.actions.edit",
831
+ defaultMessage: "Edit"
832
+ }) })
833
+ ] }) }),
834
+ /* @__PURE__ */ jsx(
835
+ StyledMenuItem,
506
836
  {
507
- paddingTop: 2,
508
- paddingBottom: 2,
509
- alignItems: "center",
510
- gap: 2,
511
- hasRadius: true,
512
- width: "100%",
513
- children: [
837
+ disabled: !canDelete,
838
+ onSelect: toggleWarningSubmit,
839
+ $variant: "danger",
840
+ children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
514
841
  /* @__PURE__ */ jsx(TrashIcon, {}),
515
842
  /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
516
843
  id: "content-releases.header.actions.delete",
517
844
  defaultMessage: "Delete"
518
845
  }) })
519
- ]
846
+ ] })
520
847
  }
521
- ) })
848
+ )
522
849
  ]
523
850
  }
524
851
  ),
@@ -536,7 +863,7 @@ const ReleaseDetailsLayout = ({
536
863
  defaultMessage: "Created"
537
864
  }) }),
538
865
  /* @__PURE__ */ jsxs(Typography, { variant: "pi", color: "neutral300", children: [
539
- /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(release.createdAt) }),
866
+ /* @__PURE__ */ jsx(RelativeTime$1, { timestamp: new Date(release.createdAt) }),
540
867
  formatMessage(
541
868
  {
542
869
  id: "content-releases.header.actions.created.description",
@@ -554,12 +881,12 @@ const ReleaseDetailsLayout = ({
554
881
  id: "content-releases.header.actions.refresh",
555
882
  defaultMessage: "Refresh"
556
883
  }) }),
557
- /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.publish, children: /* @__PURE__ */ jsx(
884
+ canPublish ? /* @__PURE__ */ jsx(
558
885
  Button,
559
886
  {
560
887
  size: "S",
561
888
  variant: "default",
562
- onClick: handlePublishRelease,
889
+ onClick: handlePublishRelease(release.id.toString()),
563
890
  loading: isPublishing,
564
891
  disabled: release.actions.meta.count === 0,
565
892
  children: formatMessage({
@@ -567,7 +894,7 @@ const ReleaseDetailsLayout = ({
567
894
  defaultMessage: "Publish"
568
895
  })
569
896
  }
570
- ) })
897
+ ) : null
571
898
  ] })
572
899
  }
573
900
  ),
@@ -575,6 +902,7 @@ const ReleaseDetailsLayout = ({
575
902
  ] });
576
903
  };
577
904
  const GROUP_BY_OPTIONS = ["contentType", "locale", "action"];
905
+ const GROUP_BY_OPTIONS_NO_LOCALE = ["contentType", "action"];
578
906
  const getGroupByOptionLabel = (value) => {
579
907
  if (value === "locale") {
580
908
  return {
@@ -593,21 +921,33 @@ const getGroupByOptionLabel = (value) => {
593
921
  defaultMessage: "Content-Types"
594
922
  };
595
923
  };
596
- const ReleaseDetailsBody = () => {
924
+ const ReleaseDetailsBody = ({ releaseId }) => {
597
925
  const { formatMessage } = useIntl();
598
- const { releaseId } = useParams();
599
926
  const [{ query }, setQuery] = useQueryParams();
600
- const toggleNotification = useNotification();
927
+ const { toggleNotification } = useNotification();
601
928
  const { formatAPIError } = useAPIErrorHandler();
602
929
  const {
603
930
  data: releaseData,
604
931
  isLoading: isReleaseLoading,
605
- isError: isReleaseError,
606
932
  error: releaseError
607
933
  } = useGetReleaseQuery({ id: releaseId });
608
934
  const {
609
935
  allowedActions: { canUpdate }
610
936
  } = useRBAC(PERMISSIONS);
937
+ const runHookWaterfall = useStrapiApp("ReleaseDetailsPage", (state) => state.runHookWaterfall);
938
+ const { hasI18nEnabled } = runHookWaterfall(
939
+ "ContentReleases/pages/ReleaseDetails/add-locale-in-releases",
940
+ {
941
+ displayedHeaders: {
942
+ label: formatMessage({
943
+ id: "content-releases.page.ReleaseDetails.table.header.label.locale",
944
+ defaultMessage: "locale"
945
+ }),
946
+ name: "locale"
947
+ },
948
+ hasI18nEnabled: false
949
+ }
950
+ );
611
951
  const release = releaseData?.data;
612
952
  const selectedGroupBy = query?.groupBy || "contentType";
613
953
  const {
@@ -636,65 +976,59 @@ const ReleaseDetailsBody = () => {
636
976
  // We are passing the action path to found the position in the cache of the action for optimistic updates
637
977
  });
638
978
  if ("error" in response) {
639
- if (isAxiosError(response.error)) {
979
+ if (isFetchError(response.error)) {
640
980
  toggleNotification({
641
- type: "warning",
981
+ type: "danger",
642
982
  message: formatAPIError(response.error)
643
983
  });
644
984
  } else {
645
985
  toggleNotification({
646
- type: "warning",
986
+ type: "danger",
647
987
  message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
648
988
  });
649
989
  }
650
990
  }
651
991
  };
652
992
  if (isLoading || isReleaseLoading) {
653
- return /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
993
+ return /* @__PURE__ */ jsx(Page.Loading, {});
654
994
  }
655
995
  const releaseActions = data?.data;
656
996
  const releaseMeta = data?.meta;
657
997
  const contentTypes = releaseMeta?.contentTypes || {};
658
998
  const components = releaseMeta?.components || {};
659
- if (isReleaseError || !release) {
999
+ if (isBaseQueryError(releaseError) || !release) {
660
1000
  const errorsArray = [];
661
- if (releaseError) {
1001
+ if (releaseError && "code" in releaseError) {
662
1002
  errorsArray.push({
663
1003
  code: releaseError.code
664
1004
  });
665
1005
  }
666
- if (releaseActionsError) {
1006
+ if (releaseActionsError && "code" in releaseActionsError) {
667
1007
  errorsArray.push({
668
1008
  code: releaseActionsError.code
669
1009
  });
670
1010
  }
671
1011
  return /* @__PURE__ */ jsx(
672
- Redirect,
1012
+ Navigate,
673
1013
  {
674
- to: {
675
- pathname: "/plugins/content-releases",
676
- state: {
677
- errors: errorsArray
678
- }
1014
+ to: "..",
1015
+ state: {
1016
+ errors: errorsArray
679
1017
  }
680
1018
  }
681
1019
  );
682
1020
  }
683
1021
  if (isError || !releaseActions) {
684
- return /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(AnErrorOccurred, {}) });
1022
+ return /* @__PURE__ */ jsx(Page.Error, {});
685
1023
  }
686
1024
  if (Object.keys(releaseActions).length === 0) {
687
- return /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(
688
- NoContent,
1025
+ return /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(
1026
+ EmptyStateLayout,
689
1027
  {
690
- content: {
691
- id: "content-releases.pages.Details.tab.emptyEntries",
692
- defaultMessage: "This release is empty. Open the Content Manager, select an entry and add it to the release."
693
- },
694
1028
  action: /* @__PURE__ */ jsx(
695
1029
  LinkButton,
696
1030
  {
697
- as: Link$1,
1031
+ tag: Link$1,
698
1032
  to: {
699
1033
  pathname: "/content-manager"
700
1034
  },
@@ -705,18 +1039,59 @@ const ReleaseDetailsBody = () => {
705
1039
  defaultMessage: "Open the Content Manager"
706
1040
  })
707
1041
  }
708
- )
1042
+ ),
1043
+ icon: /* @__PURE__ */ jsx(EmptyDocuments, { width: "16rem" }),
1044
+ content: formatMessage({
1045
+ id: "content-releases.pages.Details.tab.emptyEntries",
1046
+ defaultMessage: "This release is empty. Open the Content Manager, select an entry and add it to the release."
1047
+ })
709
1048
  }
710
1049
  ) });
711
1050
  }
712
- return /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsxs(Flex, { gap: 8, direction: "column", alignItems: "stretch", children: [
1051
+ const groupByLabel = formatMessage({
1052
+ id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
1053
+ defaultMessage: "Group by"
1054
+ });
1055
+ const headers = [
1056
+ // ...displayedHeaders,
1057
+ {
1058
+ label: formatMessage({
1059
+ id: "content-releases.page.ReleaseDetails.table.header.label.name",
1060
+ defaultMessage: "name"
1061
+ }),
1062
+ name: "name"
1063
+ },
1064
+ {
1065
+ label: formatMessage({
1066
+ id: "content-releases.page.ReleaseDetails.table.header.label.content-type",
1067
+ defaultMessage: "content-type"
1068
+ }),
1069
+ name: "content-type"
1070
+ },
1071
+ {
1072
+ label: formatMessage({
1073
+ id: "content-releases.page.ReleaseDetails.table.header.label.action",
1074
+ defaultMessage: "action"
1075
+ }),
1076
+ name: "action"
1077
+ },
1078
+ ...!release.releasedAt ? [
1079
+ {
1080
+ label: formatMessage({
1081
+ id: "content-releases.page.ReleaseDetails.table.header.label.status",
1082
+ defaultMessage: "status"
1083
+ }),
1084
+ name: "status"
1085
+ }
1086
+ ] : []
1087
+ ];
1088
+ const options = hasI18nEnabled ? GROUP_BY_OPTIONS : GROUP_BY_OPTIONS_NO_LOCALE;
1089
+ return /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsxs(Flex, { gap: 8, direction: "column", alignItems: "stretch", children: [
713
1090
  /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
714
1091
  SingleSelect,
715
1092
  {
716
- "aria-label": formatMessage({
717
- id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
718
- defaultMessage: "Group by"
719
- }),
1093
+ placeholder: groupByLabel,
1094
+ "aria-label": groupByLabel,
720
1095
  customizeContent: (value) => formatMessage(
721
1096
  {
722
1097
  id: `content-releases.pages.ReleaseDetails.groupBy.label`,
@@ -728,7 +1103,7 @@ const ReleaseDetailsBody = () => {
728
1103
  ),
729
1104
  value: formatMessage(getGroupByOptionLabel(selectedGroupBy)),
730
1105
  onChange: (value) => setQuery({ groupBy: value }),
731
- children: GROUP_BY_OPTIONS.map((option) => /* @__PURE__ */ jsx(SingleSelectOption, { value: option, children: formatMessage(getGroupByOptionLabel(option)) }, option))
1106
+ children: options.map((option) => /* @__PURE__ */ jsx(SingleSelectOption, { value: option, children: formatMessage(getGroupByOptionLabel(option)) }, option))
732
1107
  }
733
1108
  ) }),
734
1109
  Object.keys(releaseActions).map((key) => /* @__PURE__ */ jsxs(Flex, { gap: 4, direction: "column", alignItems: "stretch", children: [
@@ -740,72 +1115,15 @@ const ReleaseDetailsBody = () => {
740
1115
  ...item,
741
1116
  id: Number(item.entry.id)
742
1117
  })),
743
- colCount: releaseActions[key].length,
744
- isLoading,
745
- isFetching,
1118
+ headers,
1119
+ isLoading: isLoading || isFetching,
746
1120
  children: /* @__PURE__ */ jsxs(Table.Content, { children: [
747
- /* @__PURE__ */ jsxs(Table.Head, { children: [
748
- /* @__PURE__ */ jsx(
749
- Table.HeaderCell,
750
- {
751
- fieldSchemaType: "string",
752
- label: formatMessage({
753
- id: "content-releases.page.ReleaseDetails.table.header.label.name",
754
- defaultMessage: "name"
755
- }),
756
- name: "name"
757
- }
758
- ),
759
- /* @__PURE__ */ jsx(
760
- Table.HeaderCell,
761
- {
762
- fieldSchemaType: "string",
763
- label: formatMessage({
764
- id: "content-releases.page.ReleaseDetails.table.header.label.locale",
765
- defaultMessage: "locale"
766
- }),
767
- name: "locale"
768
- }
769
- ),
770
- /* @__PURE__ */ jsx(
771
- Table.HeaderCell,
772
- {
773
- fieldSchemaType: "string",
774
- label: formatMessage({
775
- id: "content-releases.page.ReleaseDetails.table.header.label.content-type",
776
- defaultMessage: "content-type"
777
- }),
778
- name: "content-type"
779
- }
780
- ),
781
- /* @__PURE__ */ jsx(
782
- Table.HeaderCell,
783
- {
784
- fieldSchemaType: "string",
785
- label: formatMessage({
786
- id: "content-releases.page.ReleaseDetails.table.header.label.action",
787
- defaultMessage: "action"
788
- }),
789
- name: "action"
790
- }
791
- ),
792
- !release.releasedAt && /* @__PURE__ */ jsx(
793
- Table.HeaderCell,
794
- {
795
- fieldSchemaType: "string",
796
- label: formatMessage({
797
- id: "content-releases.page.ReleaseDetails.table.header.label.status",
798
- defaultMessage: "status"
799
- }),
800
- name: "status"
801
- }
802
- )
803
- ] }),
804
- /* @__PURE__ */ jsx(Table.LoadingBody, {}),
1121
+ /* @__PURE__ */ jsx(Table.Head, { children: headers.map((header) => /* @__PURE__ */ jsx(Table.HeaderCell, { ...header }, header.name)) }),
1122
+ /* @__PURE__ */ jsx(Table.Loading, {}),
805
1123
  /* @__PURE__ */ jsx(Table.Body, { children: releaseActions[key].map(
806
1124
  ({ id, contentType, locale, type, entry }, actionIndex) => /* @__PURE__ */ jsxs(Tr, { children: [
807
1125
  /* @__PURE__ */ jsx(Td, { width: "25%", maxWidth: "200px", children: /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: `${contentType.mainFieldValue || entry.id}` }) }),
808
- /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
1126
+ hasI18nEnabled && /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
809
1127
  /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: contentType.displayName || "" }) }),
810
1128
  /* @__PURE__ */ jsx(Td, { width: "20%", children: release.releasedAt ? /* @__PURE__ */ jsx(Typography, { children: formatMessage(
811
1129
  {
@@ -859,34 +1177,39 @@ const ReleaseDetailsBody = () => {
859
1177
  }
860
1178
  )
861
1179
  ] }, `releases-group-${key}`)),
862
- /* @__PURE__ */ jsxs(Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
863
- /* @__PURE__ */ jsx(PageSizeURLQuery, { defaultValue: releaseMeta?.pagination?.pageSize.toString() }),
864
- /* @__PURE__ */ jsx(
865
- PaginationURLQuery,
866
- {
867
- pagination: {
868
- pageCount: releaseMeta?.pagination?.pageCount || 0
869
- }
870
- }
871
- )
872
- ] })
1180
+ /* @__PURE__ */ jsxs(
1181
+ Pagination.Root,
1182
+ {
1183
+ ...releaseMeta?.pagination,
1184
+ defaultPageSize: releaseMeta?.pagination?.pageSize,
1185
+ children: [
1186
+ /* @__PURE__ */ jsx(Pagination.PageSize, {}),
1187
+ /* @__PURE__ */ jsx(Pagination.Links, {})
1188
+ ]
1189
+ }
1190
+ )
873
1191
  ] }) });
874
1192
  };
875
1193
  const ReleaseDetailsPage = () => {
876
1194
  const { formatMessage } = useIntl();
877
1195
  const { releaseId } = useParams();
878
- const toggleNotification = useNotification();
1196
+ const { toggleNotification } = useNotification();
879
1197
  const { formatAPIError } = useAPIErrorHandler();
880
- const { push } = useHistory();
1198
+ const navigate = useNavigate();
881
1199
  const [releaseModalShown, setReleaseModalShown] = React.useState(false);
882
1200
  const [showWarningSubmit, setWarningSubmit] = React.useState(false);
883
1201
  const {
884
1202
  isLoading: isLoadingDetails,
885
1203
  data,
886
1204
  isSuccess: isSuccessDetails
887
- } = useGetReleaseQuery({ id: releaseId });
1205
+ } = useGetReleaseQuery(
1206
+ { id: releaseId },
1207
+ {
1208
+ skip: !releaseId
1209
+ }
1210
+ );
888
1211
  const [updateRelease, { isLoading: isSubmittingForm }] = useUpdateReleaseMutation();
889
- const [deleteRelease, { isLoading: isDeletingRelease }] = useDeleteReleaseMutation();
1212
+ const [deleteRelease] = useDeleteReleaseMutation();
890
1213
  const toggleEditReleaseModal = () => {
891
1214
  setReleaseModalShown((prev) => !prev);
892
1215
  };
@@ -897,15 +1220,18 @@ const ReleaseDetailsPage = () => {
897
1220
  {
898
1221
  toggleEditReleaseModal,
899
1222
  toggleWarningSubmit,
900
- children: /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) })
1223
+ children: /* @__PURE__ */ jsx(Page.Loading, {})
901
1224
  }
902
1225
  );
903
1226
  }
1227
+ if (!releaseId) {
1228
+ return /* @__PURE__ */ jsx(Navigate, { to: ".." });
1229
+ }
904
1230
  const releaseData = isSuccessDetails && data?.data || null;
905
1231
  const title = releaseData?.name || "";
906
1232
  const timezone = releaseData?.timezone ?? null;
907
1233
  const scheduledAt = releaseData?.scheduledAt && timezone ? utcToZonedTime(releaseData.scheduledAt, timezone) : null;
908
- const date = scheduledAt ? new Date(format(scheduledAt, "yyyy-MM-dd")) : null;
1234
+ const date = scheduledAt ? format(scheduledAt, "yyyy-MM-dd") : void 0;
909
1235
  const time = scheduledAt ? format(scheduledAt, "HH:mm") : "";
910
1236
  const handleEditRelease = async (values) => {
911
1237
  const response = await updateRelease({
@@ -922,33 +1248,33 @@ const ReleaseDetailsPage = () => {
922
1248
  defaultMessage: "Release updated."
923
1249
  })
924
1250
  });
925
- } else if (isAxiosError(response.error)) {
1251
+ toggleEditReleaseModal();
1252
+ } else if (isFetchError(response.error)) {
926
1253
  toggleNotification({
927
- type: "warning",
1254
+ type: "danger",
928
1255
  message: formatAPIError(response.error)
929
1256
  });
930
1257
  } else {
931
1258
  toggleNotification({
932
- type: "warning",
1259
+ type: "danger",
933
1260
  message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
934
1261
  });
935
1262
  }
936
- toggleEditReleaseModal();
937
1263
  };
938
1264
  const handleDeleteRelease = async () => {
939
1265
  const response = await deleteRelease({
940
1266
  id: releaseId
941
1267
  });
942
1268
  if ("data" in response) {
943
- push("/plugins/content-releases");
944
- } else if (isAxiosError(response.error)) {
1269
+ navigate("..");
1270
+ } else if (isFetchError(response.error)) {
945
1271
  toggleNotification({
946
- type: "warning",
1272
+ type: "danger",
947
1273
  message: formatAPIError(response.error)
948
1274
  });
949
1275
  } else {
950
1276
  toggleNotification({
951
- type: "warning",
1277
+ type: "danger",
952
1278
  message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
953
1279
  });
954
1280
  }
@@ -959,10 +1285,11 @@ const ReleaseDetailsPage = () => {
959
1285
  toggleEditReleaseModal,
960
1286
  toggleWarningSubmit,
961
1287
  children: [
962
- /* @__PURE__ */ jsx(ReleaseDetailsBody, {}),
963
- releaseModalShown && /* @__PURE__ */ jsx(
1288
+ /* @__PURE__ */ jsx(ReleaseDetailsBody, { releaseId }),
1289
+ /* @__PURE__ */ jsx(
964
1290
  ReleaseModal,
965
1291
  {
1292
+ open: releaseModalShown,
966
1293
  handleClose: toggleEditReleaseModal,
967
1294
  handleSubmit: handleEditRelease,
968
1295
  isLoading: isLoadingDetails || isSubmittingForm,
@@ -976,317 +1303,21 @@ const ReleaseDetailsPage = () => {
976
1303
  }
977
1304
  }
978
1305
  ),
979
- /* @__PURE__ */ jsx(
980
- ConfirmDialog,
981
- {
982
- bodyText: {
983
- id: "content-releases.dialog.confirmation-message",
984
- defaultMessage: "Are you sure you want to delete this release?"
985
- },
986
- isOpen: showWarningSubmit,
987
- isConfirmButtonLoading: isDeletingRelease,
988
- onToggleDialog: toggleWarningSubmit,
989
- onConfirm: handleDeleteRelease
990
- }
991
- )
1306
+ /* @__PURE__ */ jsx(Dialog.Root, { open: showWarningSubmit, onOpenChange: toggleWarningSubmit, children: /* @__PURE__ */ jsx(ConfirmDialog, { onConfirm: handleDeleteRelease, children: formatMessage({
1307
+ id: "content-releases.dialog.confirmation-message",
1308
+ defaultMessage: "Are you sure you want to delete this release?"
1309
+ }) }) })
992
1310
  ]
993
1311
  }
994
1312
  );
995
1313
  };
996
- const LinkCard = styled(Link$2)`
997
- display: block;
998
- `;
999
- const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
1000
- const { formatMessage } = useIntl();
1001
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
1002
- if (isError) {
1003
- return /* @__PURE__ */ jsx(AnErrorOccurred, {});
1004
- }
1005
- if (releases?.length === 0) {
1006
- return /* @__PURE__ */ jsx(
1007
- EmptyStateLayout,
1008
- {
1009
- content: formatMessage(
1010
- {
1011
- id: "content-releases.page.Releases.tab.emptyEntries",
1012
- defaultMessage: "No releases"
1013
- },
1014
- {
1015
- target: sectionTitle
1016
- }
1017
- ),
1018
- icon: /* @__PURE__ */ jsx(EmptyDocuments, { width: "10rem" })
1019
- }
1020
- );
1021
- }
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(
1023
- Flex,
1024
- {
1025
- direction: "column",
1026
- justifyContent: "space-between",
1027
- padding: 4,
1028
- hasRadius: true,
1029
- background: "neutral0",
1030
- shadow: "tableShadow",
1031
- height: "100%",
1032
- width: "100%",
1033
- alignItems: "start",
1034
- gap: 2,
1035
- children: [
1036
- /* @__PURE__ */ jsx(Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
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(
1041
- {
1042
- id: "content-releases.page.Releases.release-item.entries",
1043
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
1044
- },
1045
- { number: actions.meta.count }
1046
- ) })
1047
- ]
1048
- }
1049
- ) }) }, id)) });
1050
- };
1051
- const StyledAlert = styled(Alert)`
1052
- button {
1053
- display: none;
1054
- }
1055
- p + div {
1056
- margin-left: auto;
1057
- }
1058
- `;
1059
- const INITIAL_FORM_VALUES = {
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
1067
- };
1068
- const ReleasesPage = () => {
1069
- const tabRef = React.useRef(null);
1070
- const location = useLocation();
1071
- const [releaseModalShown, setReleaseModalShown] = React.useState(false);
1072
- const toggleNotification = useNotification();
1073
- const { formatMessage } = useIntl();
1074
- const { push, replace } = useHistory();
1075
- const { formatAPIError } = useAPIErrorHandler();
1076
- const [{ query }, setQuery] = useQueryParams();
1077
- const response = useGetReleasesQuery(query);
1078
- const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
1079
- const { getFeature } = useLicenseLimits();
1080
- const { maximumReleases = 3 } = getFeature("cms-content-releases");
1081
- const { trackUsage } = useTracking();
1082
- const { isLoading, isSuccess, isError } = response;
1083
- const activeTab = response?.currentData?.meta?.activeTab || "pending";
1084
- const activeTabIndex = ["pending", "done"].indexOf(activeTab);
1085
- React.useEffect(() => {
1086
- if (location?.state?.errors) {
1087
- toggleNotification({
1088
- type: "warning",
1089
- title: formatMessage({
1090
- id: "content-releases.pages.Releases.notification.error.title",
1091
- defaultMessage: "Your request could not be processed."
1092
- }),
1093
- message: formatMessage({
1094
- id: "content-releases.pages.Releases.notification.error.message",
1095
- defaultMessage: "Please try again or open another release."
1096
- })
1097
- });
1098
- replace({ state: null });
1099
- }
1100
- }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
1101
- React.useEffect(() => {
1102
- if (tabRef.current) {
1103
- tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
1104
- }
1105
- }, [activeTabIndex]);
1106
- const toggleAddReleaseModal = () => {
1107
- setReleaseModalShown((prev) => !prev);
1108
- };
1109
- if (isLoading) {
1110
- return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
1111
- }
1112
- const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
1113
- const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
1114
- const handleTabChange = (index) => {
1115
- setQuery({
1116
- ...query,
1117
- page: 1,
1118
- pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
1119
- filters: {
1120
- releasedAt: {
1121
- $notNull: index === 0 ? false : true
1122
- }
1123
- }
1124
- });
1125
- };
1126
- const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
1127
- const response2 = await createRelease({
1128
- name,
1129
- scheduledAt,
1130
- timezone
1131
- });
1132
- if ("data" in response2) {
1133
- toggleNotification({
1134
- type: "success",
1135
- message: formatMessage({
1136
- id: "content-releases.modal.release-created-notification-success",
1137
- defaultMessage: "Release created."
1138
- })
1139
- });
1140
- trackUsage("didCreateRelease");
1141
- push(`/plugins/content-releases/${response2.data.data.id}`);
1142
- } else if (isAxiosError(response2.error)) {
1143
- toggleNotification({
1144
- type: "warning",
1145
- message: formatAPIError(response2.error)
1146
- });
1147
- } else {
1148
- toggleNotification({
1149
- type: "warning",
1150
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1151
- });
1152
- }
1153
- };
1154
- return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoading, children: [
1155
- /* @__PURE__ */ jsx(
1156
- HeaderLayout,
1157
- {
1158
- title: formatMessage({
1159
- id: "content-releases.pages.Releases.title",
1160
- defaultMessage: "Releases"
1161
- }),
1162
- subtitle: formatMessage(
1163
- {
1164
- id: "content-releases.pages.Releases.header-subtitle",
1165
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
1166
- },
1167
- { number: totalReleases }
1168
- ),
1169
- primaryAction: /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.create, children: /* @__PURE__ */ jsx(
1170
- Button,
1171
- {
1172
- startIcon: /* @__PURE__ */ jsx(Plus, {}),
1173
- onClick: toggleAddReleaseModal,
1174
- disabled: hasReachedMaximumPendingReleases,
1175
- children: formatMessage({
1176
- id: "content-releases.header.actions.add-release",
1177
- defaultMessage: "New release"
1178
- })
1179
- }
1180
- ) })
1181
- }
1182
- ),
1183
- /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
1184
- activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsx(
1185
- StyledAlert,
1186
- {
1187
- marginBottom: 6,
1188
- action: /* @__PURE__ */ jsx(Link$2, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
1189
- id: "content-releases.pages.Releases.max-limit-reached.action",
1190
- defaultMessage: "Explore plans"
1191
- }) }),
1192
- title: formatMessage(
1193
- {
1194
- id: "content-releases.pages.Releases.max-limit-reached.title",
1195
- defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
1196
- },
1197
- { number: maximumReleases }
1198
- ),
1199
- onClose: () => {
1200
- },
1201
- closeLabel: "",
1202
- children: formatMessage({
1203
- id: "content-releases.pages.Releases.max-limit-reached.message",
1204
- defaultMessage: "Upgrade to manage an unlimited number of releases."
1205
- })
1206
- }
1207
- ),
1208
- /* @__PURE__ */ jsxs(
1209
- TabGroup,
1210
- {
1211
- label: formatMessage({
1212
- id: "content-releases.pages.Releases.tab-group.label",
1213
- defaultMessage: "Releases list"
1214
- }),
1215
- variant: "simple",
1216
- initialSelectedTabIndex: activeTabIndex,
1217
- onTabChange: handleTabChange,
1218
- ref: tabRef,
1219
- children: [
1220
- /* @__PURE__ */ jsxs(Box, { paddingBottom: 8, children: [
1221
- /* @__PURE__ */ jsxs(Tabs, { children: [
1222
- /* @__PURE__ */ jsx(Tab, { children: formatMessage({
1223
- id: "content-releases.pages.Releases.tab.pending",
1224
- defaultMessage: "Pending"
1225
- }) }),
1226
- /* @__PURE__ */ jsx(Tab, { children: formatMessage({
1227
- id: "content-releases.pages.Releases.tab.done",
1228
- defaultMessage: "Done"
1229
- }) })
1230
- ] }),
1231
- /* @__PURE__ */ jsx(Divider, {})
1232
- ] }),
1233
- /* @__PURE__ */ jsxs(TabPanels, { children: [
1234
- /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
1235
- ReleasesGrid,
1236
- {
1237
- sectionTitle: "pending",
1238
- releases: response?.currentData?.data,
1239
- isError
1240
- }
1241
- ) }),
1242
- /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
1243
- ReleasesGrid,
1244
- {
1245
- sectionTitle: "done",
1246
- releases: response?.currentData?.data,
1247
- isError
1248
- }
1249
- ) })
1250
- ] })
1251
- ]
1252
- }
1253
- ),
1254
- totalReleases > 0 && /* @__PURE__ */ jsxs(Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1255
- /* @__PURE__ */ jsx(
1256
- PageSizeURLQuery,
1257
- {
1258
- options: ["8", "16", "32", "64"],
1259
- defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
1260
- }
1261
- ),
1262
- /* @__PURE__ */ jsx(
1263
- PaginationURLQuery,
1264
- {
1265
- pagination: {
1266
- pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
1267
- }
1268
- }
1269
- )
1270
- ] })
1271
- ] }) }),
1272
- releaseModalShown && /* @__PURE__ */ jsx(
1273
- ReleaseModal,
1274
- {
1275
- handleClose: toggleAddReleaseModal,
1276
- handleSubmit: handleAddRelease,
1277
- isLoading: isSubmittingForm,
1278
- initialValues: INITIAL_FORM_VALUES
1279
- }
1280
- )
1281
- ] });
1282
- };
1283
1314
  const App = () => {
1284
- return /* @__PURE__ */ jsx(CheckPagePermissions, { permissions: PERMISSIONS.main, children: /* @__PURE__ */ jsxs(Switch, { children: [
1285
- /* @__PURE__ */ jsx(Route, { exact: true, path: `/plugins/${pluginId}`, component: ReleasesPage }),
1286
- /* @__PURE__ */ jsx(Route, { exact: true, path: `/plugins/${pluginId}/:releaseId`, component: ReleaseDetailsPage })
1315
+ return /* @__PURE__ */ jsx(Page.Protect, { permissions: PERMISSIONS.main, children: /* @__PURE__ */ jsxs(Routes, { children: [
1316
+ /* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(ReleasesPage, {}) }),
1317
+ /* @__PURE__ */ jsx(Route, { path: ":releaseId", element: /* @__PURE__ */ jsx(ReleaseDetailsPage, {}) })
1287
1318
  ] }) });
1288
1319
  };
1289
1320
  export {
1290
1321
  App
1291
1322
  };
1292
- //# sourceMappingURL=App-xAkiD42p.mjs.map
1323
+ //# sourceMappingURL=App-B2R2exNT.mjs.map