@strapi/content-releases 0.0.0-next.836f74517f9a428a4798ed889c3f05057ec6beb1 → 0.0.0-next.840550dc97a3782302ddf918d3a0d07e59dd11eb

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