@strapi/content-releases 0.0.0-experimental.d23c1d5b0e45dd06ef09977f526c85468be05403 → 0.0.0-experimental.d2a56e52af31fcf0f0c582a5a2e58e310966e96b

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