@strapi/content-releases 5.8.1 → 5.10.0

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 (69) hide show
  1. package/dist/admin/chunks/App-B9yCdSLJ.js +1866 -0
  2. package/dist/admin/chunks/App-B9yCdSLJ.js.map +1 -0
  3. package/dist/admin/chunks/App-CuOosufQ.mjs +1845 -0
  4. package/dist/admin/chunks/App-CuOosufQ.mjs.map +1 -0
  5. package/dist/admin/chunks/PurchaseContentReleases-BCME5SQU.js +55 -0
  6. package/dist/admin/chunks/PurchaseContentReleases-BCME5SQU.js.map +1 -0
  7. package/dist/admin/chunks/PurchaseContentReleases-S1ccDSwp.mjs +53 -0
  8. package/dist/admin/chunks/PurchaseContentReleases-S1ccDSwp.mjs.map +1 -0
  9. package/dist/admin/chunks/ReleasesSettingsPage-BzdLEfxa.mjs +206 -0
  10. package/dist/admin/chunks/ReleasesSettingsPage-BzdLEfxa.mjs.map +1 -0
  11. package/dist/admin/chunks/ReleasesSettingsPage-DFVGppsl.js +208 -0
  12. package/dist/admin/chunks/ReleasesSettingsPage-DFVGppsl.js.map +1 -0
  13. package/dist/admin/chunks/en-B2EeDoOz.mjs +101 -0
  14. package/dist/{_chunks/en-D9Q4YW03.mjs.map → admin/chunks/en-B2EeDoOz.mjs.map} +1 -1
  15. package/dist/admin/chunks/en-BzpFfVeO.js +103 -0
  16. package/dist/{_chunks/en-BWPPsSH-.js.map → admin/chunks/en-BzpFfVeO.js.map} +1 -1
  17. package/dist/admin/chunks/index-1nn-zHX-.js +1657 -0
  18. package/dist/admin/chunks/index-1nn-zHX-.js.map +1 -0
  19. package/dist/admin/chunks/index-CmAFGQWf.mjs +1618 -0
  20. package/dist/admin/chunks/index-CmAFGQWf.mjs.map +1 -0
  21. package/dist/admin/chunks/schemas-DMt8h1z-.mjs +43 -0
  22. package/dist/admin/chunks/schemas-DMt8h1z-.mjs.map +1 -0
  23. package/dist/admin/chunks/schemas-DS7NeFDN.js +65 -0
  24. package/dist/admin/chunks/schemas-DS7NeFDN.js.map +1 -0
  25. package/dist/admin/index.js +18 -3
  26. package/dist/admin/index.js.map +1 -1
  27. package/dist/admin/index.mjs +13 -5
  28. package/dist/admin/index.mjs.map +1 -1
  29. package/dist/admin/src/components/ReleaseListCell.d.ts +1 -1
  30. package/dist/server/index.js +2166 -1870
  31. package/dist/server/index.js.map +1 -1
  32. package/dist/server/index.mjs +2156 -1861
  33. package/dist/server/index.mjs.map +1 -1
  34. package/dist/server/src/destroy.d.ts +1 -1
  35. package/dist/server/src/destroy.d.ts.map +1 -1
  36. package/dist/server/src/middlewares/documents.d.ts +1 -1
  37. package/dist/server/src/middlewares/documents.d.ts.map +1 -1
  38. package/dist/server/src/services/scheduling.d.ts +1 -1
  39. package/dist/server/src/services/scheduling.d.ts.map +1 -1
  40. package/dist/server/src/services/validation.d.ts +1 -1
  41. package/dist/server/src/services/validation.d.ts.map +1 -1
  42. package/dist/shared/contracts/release-actions.d.ts +0 -1
  43. package/dist/shared/contracts/releases.d.ts +0 -1
  44. package/dist/shared/contracts/settings.d.ts +1 -2
  45. package/dist/shared/contracts/settings.d.ts.map +1 -1
  46. package/dist/shared/types.d.ts +0 -1
  47. package/package.json +16 -13
  48. package/dist/_chunks/App-CiZCkScI.mjs +0 -1558
  49. package/dist/_chunks/App-CiZCkScI.mjs.map +0 -1
  50. package/dist/_chunks/App-SGjO5UPV.js +0 -1578
  51. package/dist/_chunks/App-SGjO5UPV.js.map +0 -1
  52. package/dist/_chunks/PurchaseContentReleases--qQepXpP.js +0 -52
  53. package/dist/_chunks/PurchaseContentReleases--qQepXpP.js.map +0 -1
  54. package/dist/_chunks/PurchaseContentReleases-D-n-w-st.mjs +0 -52
  55. package/dist/_chunks/PurchaseContentReleases-D-n-w-st.mjs.map +0 -1
  56. package/dist/_chunks/ReleasesSettingsPage-Cto_NLUd.js +0 -178
  57. package/dist/_chunks/ReleasesSettingsPage-Cto_NLUd.js.map +0 -1
  58. package/dist/_chunks/ReleasesSettingsPage-DQT8N3A-.mjs +0 -178
  59. package/dist/_chunks/ReleasesSettingsPage-DQT8N3A-.mjs.map +0 -1
  60. package/dist/_chunks/en-BWPPsSH-.js +0 -102
  61. package/dist/_chunks/en-D9Q4YW03.mjs +0 -102
  62. package/dist/_chunks/index-BjvFfTtA.mjs +0 -1386
  63. package/dist/_chunks/index-BjvFfTtA.mjs.map +0 -1
  64. package/dist/_chunks/index-CyU534vL.js +0 -1404
  65. package/dist/_chunks/index-CyU534vL.js.map +0 -1
  66. package/dist/_chunks/schemas-DBYv9gK8.js +0 -61
  67. package/dist/_chunks/schemas-DBYv9gK8.js.map +0 -1
  68. package/dist/_chunks/schemas-DdA2ic2U.mjs +0 -44
  69. package/dist/_chunks/schemas-DdA2ic2U.mjs.map +0 -1
@@ -1,1558 +0,0 @@
1
- import { jsxs, jsx, 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 { Link, useLocation, useNavigate, NavLink, useParams, Navigate, 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-BjvFfTtA.mjs";
5
- import * as React from "react";
6
- import { Flex, Popover, Button, Typography, LinkButton, Modal, Field, TextInput, Box, Checkbox, DatePicker, TimePicker, Combobox, ComboboxOption, Link as Link$1, Alert, Main, Tabs, Divider, EmptyStateLayout, Grid, Badge, MenuItem, SimpleMenu, Dialog, SingleSelect, SingleSelectOption, Tr, Td } from "@strapi/design-system";
7
- import { CrossCircle, CaretDown, CheckCircle, ArrowsCounterClockwise, Plus, Pencil, Trash, More } from "@strapi/icons";
8
- import { EmptyDocuments } from "@strapi/icons/symbols";
9
- import format$1 from "date-fns/format";
10
- import { utcToZonedTime, zonedTimeToUtc } from "date-fns-tz";
11
- import { useIntl } from "react-intl";
12
- import { styled } from "styled-components";
13
- import { unstable_useDocument } from "@strapi/content-manager/strapi-admin";
14
- import { stringify } from "qs";
15
- import { intervalToDuration, isPast, formatISO, format } from "date-fns";
16
- import { Formik, Form, useFormikContext } from "formik";
17
- import { R as RELEASE_SCHEMA } from "./schemas-DdA2ic2U.mjs";
18
- import { useDispatch } from "react-redux";
19
- import { useLicenseLimits } from "@strapi/admin/strapi-admin/ee";
20
- const StyledPopoverFlex = styled(Flex)`
21
- width: 100%;
22
- max-width: 256px;
23
-
24
- & > * {
25
- border-bottom: 1px solid ${({ theme }) => theme.colors.neutral150};
26
- }
27
-
28
- & > *:last-child {
29
- border-bottom: none;
30
- }
31
- `;
32
- const EntryStatusTrigger = ({
33
- action,
34
- status,
35
- hasErrors,
36
- requiredStage,
37
- entryStage
38
- }) => {
39
- const { formatMessage } = useIntl();
40
- if (action === "publish") {
41
- if (hasErrors || requiredStage && requiredStage.id !== entryStage?.id) {
42
- return /* @__PURE__ */ jsx(Popover.Trigger, { children: /* @__PURE__ */ jsx(
43
- Button,
44
- {
45
- variant: "ghost",
46
- startIcon: /* @__PURE__ */ jsx(CrossCircle, { fill: "danger600" }),
47
- endIcon: /* @__PURE__ */ jsx(CaretDown, {}),
48
- children: /* @__PURE__ */ jsx(Typography, { textColor: "danger600", variant: "omega", fontWeight: "bold", children: formatMessage({
49
- id: "content-releases.pages.ReleaseDetails.entry-validation.not-ready",
50
- defaultMessage: "Not ready to publish"
51
- }) })
52
- }
53
- ) });
54
- }
55
- if (status === "draft") {
56
- return /* @__PURE__ */ jsx(Popover.Trigger, { children: /* @__PURE__ */ jsx(
57
- Button,
58
- {
59
- variant: "ghost",
60
- startIcon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
61
- endIcon: /* @__PURE__ */ jsx(CaretDown, {}),
62
- children: /* @__PURE__ */ jsx(Typography, { textColor: "success600", variant: "omega", fontWeight: "bold", children: formatMessage({
63
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
64
- defaultMessage: "Ready to publish"
65
- }) })
66
- }
67
- ) });
68
- }
69
- if (status === "modified") {
70
- return /* @__PURE__ */ jsx(Popover.Trigger, { children: /* @__PURE__ */ jsx(
71
- Button,
72
- {
73
- variant: "ghost",
74
- startIcon: /* @__PURE__ */ jsx(ArrowsCounterClockwise, { fill: "alternative600" }),
75
- endIcon: /* @__PURE__ */ jsx(CaretDown, {}),
76
- children: /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "alternative600", children: formatMessage({
77
- id: "content-releases.pages.ReleaseDetails.entry-validation.modified",
78
- defaultMessage: "Ready to publish changes"
79
- }) })
80
- }
81
- ) });
82
- }
83
- return /* @__PURE__ */ jsx(Popover.Trigger, { children: /* @__PURE__ */ jsx(
84
- Button,
85
- {
86
- variant: "ghost",
87
- startIcon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
88
- endIcon: /* @__PURE__ */ jsx(CaretDown, {}),
89
- children: /* @__PURE__ */ jsx(Typography, { textColor: "success600", variant: "omega", fontWeight: "bold", children: formatMessage({
90
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
91
- defaultMessage: "Already published"
92
- }) })
93
- }
94
- ) });
95
- }
96
- if (status === "published") {
97
- return /* @__PURE__ */ jsx(Popover.Trigger, { children: /* @__PURE__ */ jsx(
98
- Button,
99
- {
100
- variant: "ghost",
101
- startIcon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }),
102
- endIcon: /* @__PURE__ */ jsx(CaretDown, {}),
103
- children: /* @__PURE__ */ jsx(Typography, { textColor: "success600", variant: "omega", fontWeight: "bold", children: formatMessage({
104
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
105
- defaultMessage: "Ready to unpublish"
106
- }) })
107
- }
108
- ) });
109
- }
110
- return /* @__PURE__ */ jsx(Popover.Trigger, { children: /* @__PURE__ */ jsx(Button, { variant: "ghost", startIcon: /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" }), endIcon: /* @__PURE__ */ jsx(CaretDown, {}), children: /* @__PURE__ */ jsx(Typography, { textColor: "success600", variant: "omega", fontWeight: "bold", children: formatMessage({
111
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
112
- defaultMessage: "Already unpublished"
113
- }) }) }) });
114
- };
115
- const FieldsValidation = ({
116
- hasErrors,
117
- errors,
118
- kind,
119
- contentTypeUid,
120
- documentId,
121
- locale
122
- }) => {
123
- const { formatMessage } = useIntl();
124
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, width: "100%", padding: 5, children: [
125
- /* @__PURE__ */ jsxs(Flex, { gap: 2, width: "100%", children: [
126
- /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: formatMessage({
127
- id: "content-releases.pages.ReleaseDetails.entry-validation.fields",
128
- defaultMessage: "Fields"
129
- }) }),
130
- hasErrors ? /* @__PURE__ */ jsx(CrossCircle, { fill: "danger600" }) : /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" })
131
- ] }),
132
- /* @__PURE__ */ jsx(Typography, { width: "100%", textColor: "neutral600", children: hasErrors ? formatMessage(
133
- {
134
- id: "content-releases.pages.ReleaseDetails.entry-validation.fields.error",
135
- defaultMessage: "{errors} errors on fields."
136
- },
137
- { errors: errors ? Object.keys(errors).length : 0 }
138
- ) : formatMessage({
139
- id: "content-releases.pages.ReleaseDetails.entry-validation.fields.success",
140
- defaultMessage: "All fields are filled correctly."
141
- }) }),
142
- hasErrors && /* @__PURE__ */ jsx(
143
- LinkButton,
144
- {
145
- tag: Link,
146
- to: {
147
- pathname: `/content-manager/${kind === "collectionType" ? "collection-types" : "single-types"}/${contentTypeUid}/${documentId}`,
148
- search: locale ? stringify({
149
- plugins: {
150
- i18n: {
151
- locale
152
- }
153
- }
154
- }) : ""
155
- },
156
- variant: "secondary",
157
- fullWidth: true,
158
- state: { forceValidation: true },
159
- children: formatMessage({
160
- id: "content-releases.pages.ReleaseDetails.entry-validation.fields.see-errors",
161
- defaultMessage: "See errors"
162
- })
163
- }
164
- )
165
- ] });
166
- };
167
- const getReviewStageIcon = ({
168
- contentTypeHasReviewWorkflow,
169
- requiredStage,
170
- entryStage
171
- }) => {
172
- if (!contentTypeHasReviewWorkflow) {
173
- return /* @__PURE__ */ jsx(CheckCircle, { fill: "neutral200" });
174
- }
175
- if (requiredStage && requiredStage.id !== entryStage?.id) {
176
- return /* @__PURE__ */ jsx(CrossCircle, { fill: "danger600" });
177
- }
178
- return /* @__PURE__ */ jsx(CheckCircle, { fill: "success600" });
179
- };
180
- const getReviewStageMessage = ({
181
- contentTypeHasReviewWorkflow,
182
- requiredStage,
183
- entryStage,
184
- formatMessage
185
- }) => {
186
- if (!contentTypeHasReviewWorkflow) {
187
- return formatMessage({
188
- id: "content-releases.pages.ReleaseDetails.entry-validation.review-stage.not-enabled",
189
- defaultMessage: "This entry is not associated to any workflow."
190
- });
191
- }
192
- if (requiredStage && requiredStage.id !== entryStage?.id) {
193
- return formatMessage(
194
- {
195
- id: "content-releases.pages.ReleaseDetails.entry-validation.review-stage.not-ready",
196
- defaultMessage: "This entry is not at the required stage for publishing. ({stageName})"
197
- },
198
- {
199
- stageName: requiredStage?.name ?? ""
200
- }
201
- );
202
- }
203
- if (requiredStage && requiredStage.id === entryStage?.id) {
204
- return formatMessage(
205
- {
206
- id: "content-releases.pages.ReleaseDetails.entry-validation.review-stage.ready",
207
- defaultMessage: "This entry is at the required stage for publishing. ({stageName})"
208
- },
209
- {
210
- stageName: requiredStage?.name ?? ""
211
- }
212
- );
213
- }
214
- return formatMessage({
215
- id: "content-releases.pages.ReleaseDetails.entry-validation.review-stage.stage-not-required",
216
- defaultMessage: "No required stage for publication"
217
- });
218
- };
219
- const ReviewStageValidation = ({
220
- contentTypeHasReviewWorkflow,
221
- requiredStage,
222
- entryStage
223
- }) => {
224
- const { formatMessage } = useIntl();
225
- const Icon = getReviewStageIcon({
226
- contentTypeHasReviewWorkflow,
227
- requiredStage,
228
- entryStage
229
- });
230
- return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, width: "100%", padding: 5, children: [
231
- /* @__PURE__ */ jsxs(Flex, { gap: 2, width: "100%", children: [
232
- /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children: formatMessage({
233
- id: "content-releases.pages.ReleaseDetails.entry-validation.review-stage",
234
- defaultMessage: "Review stage"
235
- }) }),
236
- Icon
237
- ] }),
238
- /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: getReviewStageMessage({
239
- contentTypeHasReviewWorkflow,
240
- requiredStage,
241
- entryStage,
242
- formatMessage
243
- }) })
244
- ] });
245
- };
246
- const EntryValidationPopover = ({
247
- schema,
248
- entry,
249
- status,
250
- action
251
- }) => {
252
- const { validate, isLoading } = unstable_useDocument(
253
- {
254
- collectionType: schema?.kind ?? "",
255
- model: schema?.uid ?? ""
256
- },
257
- {
258
- // 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
259
- skip: true
260
- }
261
- );
262
- const errors = isLoading ? null : validate(entry);
263
- const hasErrors = errors ? Object.keys(errors).length > 0 : false;
264
- const contentTypeHasReviewWorkflow = schema?.hasReviewWorkflow ?? false;
265
- const requiredStage = schema?.stageRequiredToPublish;
266
- const entryStage = entry.strapi_stage;
267
- if (isLoading) {
268
- return null;
269
- }
270
- return /* @__PURE__ */ jsxs(Popover.Root, { children: [
271
- /* @__PURE__ */ jsx(
272
- EntryStatusTrigger,
273
- {
274
- action,
275
- status,
276
- hasErrors,
277
- requiredStage,
278
- entryStage
279
- }
280
- ),
281
- /* @__PURE__ */ jsx(Popover.Content, { children: /* @__PURE__ */ jsxs(StyledPopoverFlex, { direction: "column", children: [
282
- /* @__PURE__ */ jsx(
283
- FieldsValidation,
284
- {
285
- hasErrors,
286
- errors,
287
- contentTypeUid: schema?.uid,
288
- kind: schema?.kind,
289
- documentId: entry.documentId,
290
- locale: entry.locale
291
- }
292
- ),
293
- /* @__PURE__ */ jsx(
294
- ReviewStageValidation,
295
- {
296
- contentTypeHasReviewWorkflow,
297
- requiredStage,
298
- entryStage
299
- }
300
- )
301
- ] }) })
302
- ] });
303
- };
304
- const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
305
- const RelativeTime$1 = React.forwardRef(
306
- ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
307
- const { formatRelativeTime, formatDate, formatTime } = useIntl();
308
- const interval = intervalToDuration({
309
- start: timestamp,
310
- end: Date.now()
311
- // see https://github.com/date-fns/date-fns/issues/2891 – No idea why it's all partial it returns it every time.
312
- });
313
- const unit = intervals.find((intervalUnit) => {
314
- return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
315
- });
316
- const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
317
- const customInterval = customIntervals.find(
318
- (custom) => interval[custom.unit] < custom.threshold
319
- );
320
- const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, { numeric: "auto" });
321
- return /* @__PURE__ */ jsx(
322
- "time",
323
- {
324
- ref: forwardedRef,
325
- dateTime: timestamp.toISOString(),
326
- role: "time",
327
- title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
328
- ...restProps,
329
- children: displayText
330
- }
331
- );
332
- }
333
- );
334
- const ReleaseModal = ({
335
- handleClose,
336
- open,
337
- handleSubmit,
338
- initialValues,
339
- isLoading = false
340
- }) => {
341
- const { formatMessage } = useIntl();
342
- const { pathname } = useLocation();
343
- const isCreatingRelease = pathname === `/plugins/${pluginId}`;
344
- const { timezoneList, systemTimezone = { value: "UTC+00:00-Africa/Abidjan " } } = getTimezones(
345
- initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : /* @__PURE__ */ new Date()
346
- );
347
- const getScheduledTimestamp = (values) => {
348
- const { date, time, timezone } = values;
349
- if (!date || !time || !timezone) return null;
350
- const timezoneWithoutOffset = timezone.split("&")[1];
351
- return zonedTimeToUtc(`${date} ${time}`, timezoneWithoutOffset);
352
- };
353
- const getTimezoneWithOffset = () => {
354
- const currentTimezone = timezoneList.find(
355
- (timezone) => timezone.value.split("&")[1] === initialValues.timezone
356
- );
357
- return currentTimezone?.value || systemTimezone.value;
358
- };
359
- return /* @__PURE__ */ jsx(Modal.Root, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
360
- /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: formatMessage(
361
- {
362
- id: "content-releases.modal.title",
363
- defaultMessage: "{isCreatingRelease, select, true {New release} other {Edit release}}"
364
- },
365
- { isCreatingRelease }
366
- ) }) }),
367
- /* @__PURE__ */ jsx(
368
- Formik,
369
- {
370
- onSubmit: (values) => {
371
- handleSubmit({
372
- ...values,
373
- timezone: values.timezone ? values.timezone.split("&")[1] : null,
374
- scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
375
- });
376
- },
377
- initialValues: {
378
- ...initialValues,
379
- timezone: initialValues.timezone ? getTimezoneWithOffset() : systemTimezone.value
380
- },
381
- validationSchema: RELEASE_SCHEMA,
382
- validateOnChange: false,
383
- children: ({ values, errors, handleChange, setFieldValue }) => {
384
- return /* @__PURE__ */ jsxs(Form, { children: [
385
- /* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
386
- /* @__PURE__ */ jsxs(
387
- Field.Root,
388
- {
389
- name: "name",
390
- error: errors.name && formatMessage({ id: errors.name, defaultMessage: errors.name }),
391
- required: true,
392
- children: [
393
- /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
394
- id: "content-releases.modal.form.input.label.release-name",
395
- defaultMessage: "Name"
396
- }) }),
397
- /* @__PURE__ */ jsx(TextInput, { value: values.name, onChange: handleChange }),
398
- /* @__PURE__ */ jsx(Field.Error, {})
399
- ]
400
- }
401
- ),
402
- /* @__PURE__ */ jsx(Box, { width: "max-content", children: /* @__PURE__ */ jsx(
403
- Checkbox,
404
- {
405
- name: "isScheduled",
406
- checked: values.isScheduled,
407
- onCheckedChange: (checked) => {
408
- setFieldValue("isScheduled", checked);
409
- if (!checked) {
410
- setFieldValue("date", null);
411
- setFieldValue("time", "");
412
- setFieldValue("timezone", null);
413
- } else {
414
- setFieldValue("date", initialValues.date);
415
- setFieldValue("time", initialValues.time);
416
- setFieldValue(
417
- "timezone",
418
- initialValues.timezone ?? systemTimezone?.value
419
- );
420
- }
421
- },
422
- children: /* @__PURE__ */ jsx(
423
- Typography,
424
- {
425
- textColor: values.isScheduled ? "primary600" : "neutral800",
426
- fontWeight: values.isScheduled ? "semiBold" : "regular",
427
- children: formatMessage({
428
- id: "modal.form.input.label.schedule-release",
429
- defaultMessage: "Schedule release"
430
- })
431
- }
432
- )
433
- }
434
- ) }),
435
- values.isScheduled && /* @__PURE__ */ jsxs(Fragment, { children: [
436
- /* @__PURE__ */ jsxs(Flex, { gap: 4, alignItems: "start", children: [
437
- /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsxs(
438
- Field.Root,
439
- {
440
- name: "date",
441
- error: errors.date && formatMessage({ id: errors.date, defaultMessage: errors.date }),
442
- required: true,
443
- children: [
444
- /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
445
- id: "content-releases.modal.form.input.label.date",
446
- defaultMessage: "Date"
447
- }) }),
448
- /* @__PURE__ */ jsx(
449
- DatePicker,
450
- {
451
- onChange: (date) => {
452
- const isoFormatDate = date ? formatISO(date, { representation: "date" }) : null;
453
- setFieldValue("date", isoFormatDate);
454
- },
455
- clearLabel: formatMessage({
456
- id: "content-releases.modal.form.input.clearLabel",
457
- defaultMessage: "Clear"
458
- }),
459
- onClear: () => {
460
- setFieldValue("date", null);
461
- },
462
- value: values.date ? new Date(values.date) : /* @__PURE__ */ new Date(),
463
- minDate: utcToZonedTime(/* @__PURE__ */ new Date(), values.timezone.split("&")[1])
464
- }
465
- ),
466
- /* @__PURE__ */ jsx(Field.Error, {})
467
- ]
468
- }
469
- ) }),
470
- /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsxs(
471
- Field.Root,
472
- {
473
- name: "time",
474
- error: errors.time && formatMessage({ id: errors.time, defaultMessage: errors.time }),
475
- required: true,
476
- children: [
477
- /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
478
- id: "content-releases.modal.form.input.label.time",
479
- defaultMessage: "Time"
480
- }) }),
481
- /* @__PURE__ */ jsx(
482
- TimePicker,
483
- {
484
- onChange: (time) => {
485
- setFieldValue("time", time);
486
- },
487
- clearLabel: formatMessage({
488
- id: "content-releases.modal.form.input.clearLabel",
489
- defaultMessage: "Clear"
490
- }),
491
- onClear: () => {
492
- setFieldValue("time", "");
493
- },
494
- value: values.time || void 0
495
- }
496
- ),
497
- /* @__PURE__ */ jsx(Field.Error, {})
498
- ]
499
- }
500
- ) })
501
- ] }),
502
- /* @__PURE__ */ jsx(TimezoneComponent, { timezoneOptions: timezoneList })
503
- ] })
504
- ] }) }),
505
- /* @__PURE__ */ jsxs(Modal.Footer, { children: [
506
- /* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }) }),
507
- /* @__PURE__ */ jsx(Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
508
- {
509
- id: "content-releases.modal.form.button.submit",
510
- defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
511
- },
512
- { isCreatingRelease }
513
- ) })
514
- ] })
515
- ] });
516
- }
517
- }
518
- )
519
- ] }) });
520
- };
521
- const TimezoneComponent = ({ timezoneOptions }) => {
522
- const { values, errors, setFieldValue } = useFormikContext();
523
- const { formatMessage } = useIntl();
524
- const [timezoneList, setTimezoneList] = React.useState(timezoneOptions);
525
- React.useEffect(() => {
526
- if (values.date) {
527
- const { timezoneList: timezoneList2 } = getTimezones(new Date(values.date));
528
- setTimezoneList(timezoneList2);
529
- const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("&")[1] === values.timezone.split("&")[1]);
530
- if (updatedTimezone) {
531
- setFieldValue("timezone", updatedTimezone.value);
532
- }
533
- }
534
- }, [setFieldValue, values.date, values.timezone]);
535
- return /* @__PURE__ */ jsxs(
536
- Field.Root,
537
- {
538
- name: "timezone",
539
- error: errors.timezone && formatMessage({ id: errors.timezone, defaultMessage: errors.timezone }),
540
- required: true,
541
- children: [
542
- /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
543
- id: "content-releases.modal.form.input.label.timezone",
544
- defaultMessage: "Timezone"
545
- }) }),
546
- /* @__PURE__ */ jsx(
547
- Combobox,
548
- {
549
- autocomplete: { type: "list", filter: "contains" },
550
- value: values.timezone || void 0,
551
- textValue: values.timezone ? values.timezone.replace(/&/, " ") : void 0,
552
- onChange: (timezone) => {
553
- setFieldValue("timezone", timezone);
554
- },
555
- onTextValueChange: (timezone) => {
556
- setFieldValue("timezone", timezone);
557
- },
558
- onClear: () => {
559
- setFieldValue("timezone", "");
560
- },
561
- children: timezoneList.map((timezone) => /* @__PURE__ */ jsx(ComboboxOption, { value: timezone.value, children: timezone.value.replace(/&/, " ") }, timezone.value))
562
- }
563
- ),
564
- /* @__PURE__ */ jsx(Field.Error, {})
565
- ]
566
- }
567
- );
568
- };
569
- const useTypedDispatch = useDispatch;
570
- const isBaseQueryError = (error) => {
571
- return typeof error !== "undefined" && error.name !== void 0;
572
- };
573
- const LinkCard = styled(Link$1)`
574
- display: block;
575
- `;
576
- const RelativeTime = styled(RelativeTime$1)`
577
- display: inline-block;
578
- &::first-letter {
579
- text-transform: uppercase;
580
- }
581
- `;
582
- const getBadgeProps = (status) => {
583
- let color;
584
- switch (status) {
585
- case "ready":
586
- color = "success";
587
- break;
588
- case "blocked":
589
- color = "warning";
590
- break;
591
- case "failed":
592
- color = "danger";
593
- break;
594
- case "done":
595
- color = "primary";
596
- break;
597
- case "empty":
598
- default:
599
- color = "neutral";
600
- }
601
- return {
602
- textColor: `${color}600`,
603
- backgroundColor: `${color}100`,
604
- borderColor: `${color}200`
605
- };
606
- };
607
- const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
608
- const { formatMessage } = useIntl();
609
- if (isError) {
610
- return /* @__PURE__ */ jsx(Page.Error, {});
611
- }
612
- if (releases?.length === 0) {
613
- return /* @__PURE__ */ jsx(
614
- EmptyStateLayout,
615
- {
616
- content: formatMessage(
617
- {
618
- id: "content-releases.page.Releases.tab.emptyEntries",
619
- defaultMessage: "No releases"
620
- },
621
- {
622
- target: sectionTitle
623
- }
624
- ),
625
- icon: /* @__PURE__ */ jsx(EmptyDocuments, { width: "16rem" })
626
- }
627
- );
628
- }
629
- 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(
630
- Flex,
631
- {
632
- direction: "column",
633
- justifyContent: "space-between",
634
- padding: 4,
635
- hasRadius: true,
636
- background: "neutral0",
637
- shadow: "tableShadow",
638
- height: "100%",
639
- width: "100%",
640
- alignItems: "start",
641
- gap: 4,
642
- children: [
643
- /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", gap: 1, children: [
644
- /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", tag: "h3", variant: "delta", fontWeight: "bold", children: name }),
645
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: scheduledAt ? /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
646
- id: "content-releases.pages.Releases.not-scheduled",
647
- defaultMessage: "Not scheduled"
648
- }) })
649
- ] }),
650
- /* @__PURE__ */ jsx(Badge, { ...getBadgeProps(status), children: status })
651
- ]
652
- }
653
- ) }) }, id)) });
654
- };
655
- const StyledAlert = styled(Alert)`
656
- button {
657
- display: none;
658
- }
659
- p + div {
660
- margin-left: auto;
661
- }
662
- `;
663
- const INITIAL_FORM_VALUES = {
664
- name: "",
665
- date: format(/* @__PURE__ */ new Date(), "yyyy-MM-dd"),
666
- time: "",
667
- isScheduled: true,
668
- scheduledAt: null,
669
- timezone: null
670
- };
671
- const ReleasesPage = () => {
672
- const location = useLocation();
673
- const [releaseModalShown, setReleaseModalShown] = React.useState(false);
674
- const { toggleNotification } = useNotification();
675
- const { formatMessage } = useIntl();
676
- const navigate = useNavigate();
677
- const { formatAPIError } = useAPIErrorHandler();
678
- const [{ query }, setQuery] = useQueryParams();
679
- const response = useGetReleasesQuery(query);
680
- const { data, isLoading: isLoadingSettings } = useGetReleaseSettingsQuery();
681
- const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
682
- const { getFeature } = useLicenseLimits();
683
- const { maximumReleases = 3 } = getFeature("cms-content-releases");
684
- const { trackUsage } = useTracking();
685
- const {
686
- allowedActions: { canCreate }
687
- } = useRBAC(PERMISSIONS);
688
- const { isLoading: isLoadingReleases, isSuccess, isError } = response;
689
- const activeTab = response?.currentData?.meta?.activeTab || "pending";
690
- React.useEffect(() => {
691
- if (location?.state?.errors) {
692
- toggleNotification({
693
- type: "danger",
694
- title: formatMessage({
695
- id: "content-releases.pages.Releases.notification.error.title",
696
- defaultMessage: "Your request could not be processed."
697
- }),
698
- message: formatMessage({
699
- id: "content-releases.pages.Releases.notification.error.message",
700
- defaultMessage: "Please try again or open another release."
701
- })
702
- });
703
- navigate("", { replace: true, state: null });
704
- }
705
- }, [formatMessage, location?.state?.errors, navigate, toggleNotification]);
706
- const toggleAddReleaseModal = () => {
707
- setReleaseModalShown((prev) => !prev);
708
- };
709
- if (isLoadingReleases || isLoadingSettings) {
710
- return /* @__PURE__ */ jsx(Page.Loading, {});
711
- }
712
- const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
713
- const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
714
- const handleTabChange = (tabValue) => {
715
- setQuery({
716
- ...query,
717
- page: 1,
718
- pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
719
- filters: {
720
- releasedAt: {
721
- $notNull: tabValue !== "pending"
722
- }
723
- }
724
- });
725
- };
726
- const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
727
- const response2 = await createRelease({
728
- name,
729
- scheduledAt,
730
- timezone
731
- });
732
- if ("data" in response2) {
733
- toggleNotification({
734
- type: "success",
735
- message: formatMessage({
736
- id: "content-releases.modal.release-created-notification-success",
737
- defaultMessage: "Release created."
738
- })
739
- });
740
- trackUsage("didCreateRelease");
741
- navigate(response2.data.data.id.toString());
742
- } else if (isFetchError(response2.error)) {
743
- toggleNotification({
744
- type: "danger",
745
- message: formatAPIError(response2.error)
746
- });
747
- } else {
748
- toggleNotification({
749
- type: "danger",
750
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
751
- });
752
- }
753
- };
754
- return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoadingReleases || isLoadingSettings, children: [
755
- /* @__PURE__ */ jsx(
756
- Layouts.Header,
757
- {
758
- title: formatMessage({
759
- id: "content-releases.pages.Releases.title",
760
- defaultMessage: "Releases"
761
- }),
762
- subtitle: formatMessage({
763
- id: "content-releases.pages.Releases.header-subtitle",
764
- defaultMessage: "Create and manage content updates"
765
- }),
766
- primaryAction: canCreate ? /* @__PURE__ */ jsx(
767
- Button,
768
- {
769
- startIcon: /* @__PURE__ */ jsx(Plus, {}),
770
- onClick: toggleAddReleaseModal,
771
- disabled: hasReachedMaximumPendingReleases,
772
- children: formatMessage({
773
- id: "content-releases.header.actions.add-release",
774
- defaultMessage: "New release"
775
- })
776
- }
777
- ) : null
778
- }
779
- ),
780
- /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
781
- hasReachedMaximumPendingReleases && /* @__PURE__ */ jsx(
782
- StyledAlert,
783
- {
784
- marginBottom: 6,
785
- action: /* @__PURE__ */ jsx(Link$1, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
786
- id: "content-releases.pages.Releases.max-limit-reached.action",
787
- defaultMessage: "Explore plans"
788
- }) }),
789
- title: formatMessage(
790
- {
791
- id: "content-releases.pages.Releases.max-limit-reached.title",
792
- defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
793
- },
794
- { number: maximumReleases }
795
- ),
796
- onClose: () => {
797
- },
798
- closeLabel: "",
799
- children: formatMessage({
800
- id: "content-releases.pages.Releases.max-limit-reached.message",
801
- defaultMessage: "Upgrade to manage an unlimited number of releases."
802
- })
803
- }
804
- ),
805
- /* @__PURE__ */ jsxs(Tabs.Root, { variant: "simple", onValueChange: handleTabChange, value: activeTab, children: [
806
- /* @__PURE__ */ jsxs(Box, { paddingBottom: 8, children: [
807
- /* @__PURE__ */ jsxs(
808
- Tabs.List,
809
- {
810
- "aria-label": formatMessage({
811
- id: "content-releases.pages.Releases.tab-group.label",
812
- defaultMessage: "Releases list"
813
- }),
814
- children: [
815
- /* @__PURE__ */ jsx(Tabs.Trigger, { value: "pending", children: formatMessage(
816
- {
817
- id: "content-releases.pages.Releases.tab.pending",
818
- defaultMessage: "Pending ({count})"
819
- },
820
- {
821
- count: totalPendingReleases
822
- }
823
- ) }),
824
- /* @__PURE__ */ jsx(Tabs.Trigger, { value: "done", children: formatMessage({
825
- id: "content-releases.pages.Releases.tab.done",
826
- defaultMessage: "Done"
827
- }) })
828
- ]
829
- }
830
- ),
831
- /* @__PURE__ */ jsx(Divider, {})
832
- ] }),
833
- /* @__PURE__ */ jsx(Tabs.Content, { value: "pending", children: /* @__PURE__ */ jsx(
834
- ReleasesGrid,
835
- {
836
- sectionTitle: "pending",
837
- releases: response?.currentData?.data,
838
- isError
839
- }
840
- ) }),
841
- /* @__PURE__ */ jsx(Tabs.Content, { value: "done", children: /* @__PURE__ */ jsx(
842
- ReleasesGrid,
843
- {
844
- sectionTitle: "done",
845
- releases: response?.currentData?.data,
846
- isError
847
- }
848
- ) })
849
- ] }),
850
- /* @__PURE__ */ jsxs(
851
- Pagination.Root,
852
- {
853
- ...response?.currentData?.meta?.pagination,
854
- defaultPageSize: response?.currentData?.meta?.pagination?.pageSize,
855
- children: [
856
- /* @__PURE__ */ jsx(Pagination.PageSize, { options: ["8", "16", "32", "64"] }),
857
- /* @__PURE__ */ jsx(Pagination.Links, {})
858
- ]
859
- }
860
- )
861
- ] }) }),
862
- /* @__PURE__ */ jsx(
863
- ReleaseModal,
864
- {
865
- open: releaseModalShown,
866
- handleClose: toggleAddReleaseModal,
867
- handleSubmit: handleAddRelease,
868
- isLoading: isSubmittingForm,
869
- initialValues: {
870
- ...INITIAL_FORM_VALUES,
871
- timezone: data?.data.defaultTimezone ? data.data.defaultTimezone.split("&")[1] : null
872
- }
873
- }
874
- )
875
- ] });
876
- };
877
- const ReleaseInfoWrapper = styled(Flex)`
878
- align-self: stretch;
879
- border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
880
- border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
881
- border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
882
- `;
883
- const StyledMenuItem = styled(MenuItem)`
884
- svg path {
885
- fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
886
- }
887
- span {
888
- color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
889
- }
890
-
891
- &:hover {
892
- background: ${({ theme, $variant = "neutral" }) => theme.colors[`${$variant}100`]};
893
- }
894
- `;
895
- const PencilIcon = styled(Pencil)`
896
- width: ${({ theme }) => theme.spaces[4]};
897
- height: ${({ theme }) => theme.spaces[4]};
898
- path {
899
- fill: ${({ theme }) => theme.colors.neutral600};
900
- }
901
- `;
902
- const TrashIcon = styled(Trash)`
903
- width: ${({ theme }) => theme.spaces[4]};
904
- height: ${({ theme }) => theme.spaces[4]};
905
- path {
906
- fill: ${({ theme }) => theme.colors.danger600};
907
- }
908
- `;
909
- const ReleaseDetailsLayout = ({
910
- toggleEditReleaseModal,
911
- toggleWarningSubmit,
912
- children
913
- }) => {
914
- const { formatMessage, formatDate, formatTime } = useIntl();
915
- const { releaseId } = useParams();
916
- const {
917
- data,
918
- isLoading: isLoadingDetails,
919
- error
920
- } = useGetReleaseQuery(
921
- { id: releaseId },
922
- {
923
- skip: !releaseId
924
- }
925
- );
926
- const [publishRelease, { isLoading: isPublishing }] = usePublishReleaseMutation();
927
- const { toggleNotification } = useNotification();
928
- const { formatAPIError } = useAPIErrorHandler();
929
- const { allowedActions } = useRBAC(PERMISSIONS);
930
- const { canUpdate, canDelete, canPublish } = allowedActions;
931
- const dispatch = useTypedDispatch();
932
- const { trackUsage } = useTracking();
933
- const release = data?.data;
934
- const handlePublishRelease = (id) => async () => {
935
- const response = await publishRelease({ id });
936
- if ("data" in response) {
937
- toggleNotification({
938
- type: "success",
939
- message: formatMessage({
940
- id: "content-releases.pages.ReleaseDetails.publish-notification-success",
941
- defaultMessage: "Release was published successfully."
942
- })
943
- });
944
- const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
945
- trackUsage("didPublishRelease", {
946
- totalEntries: totalEntries2,
947
- totalPublishedEntries,
948
- totalUnpublishedEntries
949
- });
950
- } else if (isFetchError(response.error)) {
951
- toggleNotification({
952
- type: "danger",
953
- message: formatAPIError(response.error)
954
- });
955
- } else {
956
- toggleNotification({
957
- type: "danger",
958
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
959
- });
960
- }
961
- };
962
- const handleRefresh = () => {
963
- dispatch(
964
- releaseApi.util.invalidateTags([
965
- { type: "ReleaseAction", id: "LIST" },
966
- { type: "Release", id: releaseId }
967
- ])
968
- );
969
- };
970
- const getCreatedByUser = () => {
971
- if (!release?.createdBy) {
972
- return null;
973
- }
974
- if (release.createdBy.username) {
975
- return release.createdBy.username;
976
- }
977
- if (release.createdBy.firstname) {
978
- return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
979
- }
980
- return release.createdBy.email;
981
- };
982
- if (isLoadingDetails) {
983
- return /* @__PURE__ */ jsx(Page.Loading, {});
984
- }
985
- if (isBaseQueryError(error) && "code" in error || !release) {
986
- return /* @__PURE__ */ jsx(
987
- Navigate,
988
- {
989
- to: "..",
990
- state: {
991
- errors: [
992
- {
993
- // @ts-expect-error – TODO: fix this weird error flow
994
- code: error?.code
995
- }
996
- ]
997
- }
998
- }
999
- );
1000
- }
1001
- const totalEntries = release.actions.meta.count || 0;
1002
- const hasCreatedByUser = Boolean(getCreatedByUser());
1003
- const isScheduled = release.scheduledAt && release.timezone;
1004
- const numberOfEntriesText = formatMessage(
1005
- {
1006
- id: "content-releases.pages.Details.header-subtitle",
1007
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
1008
- },
1009
- { number: totalEntries }
1010
- );
1011
- const scheduledText = isScheduled ? formatMessage(
1012
- {
1013
- id: "content-releases.pages.ReleaseDetails.header-subtitle.scheduled",
1014
- defaultMessage: "Scheduled for {date} at {time} ({offset})"
1015
- },
1016
- {
1017
- date: formatDate(new Date(release.scheduledAt), {
1018
- weekday: "long",
1019
- day: "numeric",
1020
- month: "long",
1021
- year: "numeric",
1022
- timeZone: release.timezone
1023
- }),
1024
- time: formatTime(new Date(release.scheduledAt), {
1025
- timeZone: release.timezone,
1026
- hourCycle: "h23"
1027
- }),
1028
- offset: getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
1029
- }
1030
- ) : "";
1031
- return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoadingDetails, children: [
1032
- /* @__PURE__ */ jsx(
1033
- Layouts.Header,
1034
- {
1035
- title: release.name,
1036
- subtitle: /* @__PURE__ */ jsxs(Flex, { gap: 2, lineHeight: 6, children: [
1037
- /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", variant: "epsilon", children: numberOfEntriesText + (isScheduled ? ` - ${scheduledText}` : "") }),
1038
- /* @__PURE__ */ jsx(Badge, { ...getBadgeProps(release.status), children: release.status })
1039
- ] }),
1040
- navigationAction: /* @__PURE__ */ jsx(BackButton, { fallback: ".." }),
1041
- primaryAction: !release.releasedAt && /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
1042
- /* @__PURE__ */ jsxs(
1043
- SimpleMenuButton,
1044
- {
1045
- label: /* @__PURE__ */ jsx(More, {}),
1046
- variant: "tertiary",
1047
- endIcon: null,
1048
- paddingLeft: "7px",
1049
- paddingRight: "7px",
1050
- "aria-label": formatMessage({
1051
- id: "content-releases.header.actions.open-release-actions",
1052
- defaultMessage: "Release edit and delete menu"
1053
- }),
1054
- popoverPlacement: "bottom-end",
1055
- children: [
1056
- /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
1057
- /* @__PURE__ */ jsx(PencilIcon, {}),
1058
- /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
1059
- id: "content-releases.header.actions.edit",
1060
- defaultMessage: "Edit"
1061
- }) })
1062
- ] }) }),
1063
- /* @__PURE__ */ jsx(
1064
- StyledMenuItem,
1065
- {
1066
- disabled: !canDelete,
1067
- onSelect: toggleWarningSubmit,
1068
- $variant: "danger",
1069
- children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
1070
- /* @__PURE__ */ jsx(TrashIcon, {}),
1071
- /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
1072
- id: "content-releases.header.actions.delete",
1073
- defaultMessage: "Delete"
1074
- }) })
1075
- ] })
1076
- }
1077
- ),
1078
- /* @__PURE__ */ jsxs(
1079
- ReleaseInfoWrapper,
1080
- {
1081
- direction: "column",
1082
- justifyContent: "center",
1083
- alignItems: "flex-start",
1084
- gap: 1,
1085
- padding: 4,
1086
- children: [
1087
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
1088
- id: "content-releases.header.actions.created",
1089
- defaultMessage: "Created"
1090
- }) }),
1091
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", color: "neutral300", children: [
1092
- /* @__PURE__ */ jsx(RelativeTime$1, { timestamp: new Date(release.createdAt) }),
1093
- formatMessage(
1094
- {
1095
- id: "content-releases.header.actions.created.description",
1096
- defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
1097
- },
1098
- { createdBy: getCreatedByUser(), hasCreatedByUser }
1099
- )
1100
- ] })
1101
- ]
1102
- }
1103
- )
1104
- ]
1105
- }
1106
- ),
1107
- /* @__PURE__ */ jsx(Button, { size: "S", variant: "tertiary", onClick: handleRefresh, children: formatMessage({
1108
- id: "content-releases.header.actions.refresh",
1109
- defaultMessage: "Refresh"
1110
- }) }),
1111
- canPublish ? /* @__PURE__ */ jsx(
1112
- Button,
1113
- {
1114
- size: "S",
1115
- variant: "default",
1116
- onClick: handlePublishRelease(release.id.toString()),
1117
- loading: isPublishing,
1118
- disabled: release.actions.meta.count === 0,
1119
- children: formatMessage({
1120
- id: "content-releases.header.actions.publish",
1121
- defaultMessage: "Publish"
1122
- })
1123
- }
1124
- ) : null
1125
- ] })
1126
- }
1127
- ),
1128
- children
1129
- ] });
1130
- };
1131
- const SimpleMenuButton = styled(SimpleMenu)`
1132
- & > span {
1133
- display: flex;
1134
- }
1135
- `;
1136
- const GROUP_BY_OPTIONS = ["contentType", "locale", "action"];
1137
- const GROUP_BY_OPTIONS_NO_LOCALE = ["contentType", "action"];
1138
- const getGroupByOptionLabel = (value) => {
1139
- if (value === "locale") {
1140
- return {
1141
- id: "content-releases.pages.ReleaseDetails.groupBy.option.locales",
1142
- defaultMessage: "Locales"
1143
- };
1144
- }
1145
- if (value === "action") {
1146
- return {
1147
- id: "content-releases.pages.ReleaseDetails.groupBy.option.actions",
1148
- defaultMessage: "Actions"
1149
- };
1150
- }
1151
- return {
1152
- id: "content-releases.pages.ReleaseDetails.groupBy.option.content-type",
1153
- defaultMessage: "Content-Types"
1154
- };
1155
- };
1156
- const ReleaseDetailsBody = ({ releaseId }) => {
1157
- const { formatMessage } = useIntl();
1158
- const [{ query }, setQuery] = useQueryParams();
1159
- const { toggleNotification } = useNotification();
1160
- const { formatAPIError } = useAPIErrorHandler();
1161
- const {
1162
- data: releaseData,
1163
- isLoading: isReleaseLoading,
1164
- error: releaseError
1165
- } = useGetReleaseQuery({ id: releaseId });
1166
- const {
1167
- allowedActions: { canUpdate }
1168
- } = useRBAC(PERMISSIONS);
1169
- const runHookWaterfall = useStrapiApp("ReleaseDetailsPage", (state) => state.runHookWaterfall);
1170
- const { displayedHeaders, hasI18nEnabled } = runHookWaterfall("ContentReleases/pages/ReleaseDetails/add-locale-in-releases", {
1171
- displayedHeaders: [
1172
- {
1173
- label: {
1174
- id: "content-releases.page.ReleaseDetails.table.header.label.name",
1175
- defaultMessage: "name"
1176
- },
1177
- name: "name"
1178
- }
1179
- ],
1180
- hasI18nEnabled: false
1181
- });
1182
- const release = releaseData?.data;
1183
- const selectedGroupBy = query?.groupBy || "contentType";
1184
- const {
1185
- isLoading,
1186
- isFetching,
1187
- isError,
1188
- data,
1189
- error: releaseActionsError
1190
- } = useGetReleaseActionsQuery({
1191
- ...query,
1192
- releaseId
1193
- });
1194
- const [updateReleaseAction] = useUpdateReleaseActionMutation();
1195
- const handleChangeType = async (e, actionId, actionPath) => {
1196
- const response = await updateReleaseAction({
1197
- params: {
1198
- releaseId,
1199
- actionId
1200
- },
1201
- body: {
1202
- type: e.target.value
1203
- },
1204
- query,
1205
- // We are passing the query params to make optimistic updates
1206
- actionPath
1207
- // We are passing the action path to found the position in the cache of the action for optimistic updates
1208
- });
1209
- if ("error" in response) {
1210
- if (isFetchError(response.error)) {
1211
- toggleNotification({
1212
- type: "danger",
1213
- message: formatAPIError(response.error)
1214
- });
1215
- } else {
1216
- toggleNotification({
1217
- type: "danger",
1218
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1219
- });
1220
- }
1221
- }
1222
- };
1223
- if (isLoading || isReleaseLoading) {
1224
- return /* @__PURE__ */ jsx(Page.Loading, {});
1225
- }
1226
- const releaseActions = data?.data;
1227
- const releaseMeta = data?.meta;
1228
- const contentTypes = releaseMeta?.contentTypes || {};
1229
- releaseMeta?.components || {};
1230
- if (isBaseQueryError(releaseError) || !release) {
1231
- const errorsArray = [];
1232
- if (releaseError && "code" in releaseError) {
1233
- errorsArray.push({
1234
- code: releaseError.code
1235
- });
1236
- }
1237
- if (releaseActionsError && "code" in releaseActionsError) {
1238
- errorsArray.push({
1239
- code: releaseActionsError.code
1240
- });
1241
- }
1242
- return /* @__PURE__ */ jsx(
1243
- Navigate,
1244
- {
1245
- to: "..",
1246
- state: {
1247
- errors: errorsArray
1248
- }
1249
- }
1250
- );
1251
- }
1252
- if (isError || !releaseActions) {
1253
- return /* @__PURE__ */ jsx(Page.Error, {});
1254
- }
1255
- if (Object.keys(releaseActions).length === 0) {
1256
- return /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(
1257
- EmptyStateLayout,
1258
- {
1259
- action: /* @__PURE__ */ jsx(
1260
- LinkButton,
1261
- {
1262
- tag: Link,
1263
- to: {
1264
- pathname: "/content-manager"
1265
- },
1266
- style: { textDecoration: "none" },
1267
- variant: "secondary",
1268
- children: formatMessage({
1269
- id: "content-releases.page.Details.button.openContentManager",
1270
- defaultMessage: "Open the Content Manager"
1271
- })
1272
- }
1273
- ),
1274
- icon: /* @__PURE__ */ jsx(EmptyDocuments, { width: "16rem" }),
1275
- content: formatMessage({
1276
- id: "content-releases.pages.Details.tab.emptyEntries",
1277
- defaultMessage: "This release is empty. Open the Content Manager, select an entry and add it to the release."
1278
- })
1279
- }
1280
- ) });
1281
- }
1282
- const groupByLabel = formatMessage({
1283
- id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
1284
- defaultMessage: "Group by"
1285
- });
1286
- const headers = [
1287
- ...displayedHeaders,
1288
- {
1289
- label: {
1290
- id: "content-releases.page.ReleaseDetails.table.header.label.content-type",
1291
- defaultMessage: "content-type"
1292
- },
1293
- name: "content-type"
1294
- },
1295
- {
1296
- label: {
1297
- id: "content-releases.page.ReleaseDetails.table.header.label.action",
1298
- defaultMessage: "action"
1299
- },
1300
- name: "action"
1301
- },
1302
- ...!release.releasedAt ? [
1303
- {
1304
- label: {
1305
- id: "content-releases.page.ReleaseDetails.table.header.label.status",
1306
- defaultMessage: "status"
1307
- },
1308
- name: "status"
1309
- }
1310
- ] : []
1311
- ];
1312
- const options = hasI18nEnabled ? GROUP_BY_OPTIONS : GROUP_BY_OPTIONS_NO_LOCALE;
1313
- return /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsxs(Flex, { gap: 8, direction: "column", alignItems: "stretch", children: [
1314
- /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
1315
- SingleSelect,
1316
- {
1317
- placeholder: groupByLabel,
1318
- "aria-label": groupByLabel,
1319
- customizeContent: (value) => formatMessage(
1320
- {
1321
- id: `content-releases.pages.ReleaseDetails.groupBy.label`,
1322
- defaultMessage: `Group by {groupBy}`
1323
- },
1324
- {
1325
- groupBy: value
1326
- }
1327
- ),
1328
- value: formatMessage(getGroupByOptionLabel(selectedGroupBy)),
1329
- onChange: (value) => setQuery({ groupBy: value }),
1330
- children: options.map((option) => /* @__PURE__ */ jsx(SingleSelectOption, { value: option, children: formatMessage(getGroupByOptionLabel(option)) }, option))
1331
- }
1332
- ) }),
1333
- Object.keys(releaseActions).map((key) => /* @__PURE__ */ jsxs(Flex, { gap: 4, direction: "column", alignItems: "stretch", children: [
1334
- /* @__PURE__ */ jsx(Flex, { role: "separator", "aria-label": key, children: /* @__PURE__ */ jsx(Badge, { children: key }) }),
1335
- /* @__PURE__ */ jsx(
1336
- Table.Root,
1337
- {
1338
- rows: releaseActions[key].map((item) => ({
1339
- ...item,
1340
- id: Number(item.entry.id)
1341
- })),
1342
- headers,
1343
- isLoading: isLoading || isFetching,
1344
- children: /* @__PURE__ */ jsxs(Table.Content, { children: [
1345
- /* @__PURE__ */ jsx(Table.Head, { children: headers.map(({ label, name }) => /* @__PURE__ */ jsx(Table.HeaderCell, { label: formatMessage(label), name }, name)) }),
1346
- /* @__PURE__ */ jsx(Table.Loading, {}),
1347
- /* @__PURE__ */ jsx(Table.Body, { children: releaseActions[key].map(
1348
- ({ id, contentType, locale, type, entry, status }, actionIndex) => /* @__PURE__ */ jsxs(Tr, { children: [
1349
- /* @__PURE__ */ jsx(Td, { width: "25%", maxWidth: "200px", children: /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: `${contentType.mainFieldValue || entry.id}` }) }),
1350
- hasI18nEnabled && /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
1351
- /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: contentType.displayName || "" }) }),
1352
- /* @__PURE__ */ jsx(Td, { width: "20%", children: release.releasedAt ? /* @__PURE__ */ jsx(Typography, { children: formatMessage(
1353
- {
1354
- id: "content-releases.page.ReleaseDetails.table.action-published",
1355
- defaultMessage: "This entry was <b>{isPublish, select, true {published} other {unpublished}}</b>."
1356
- },
1357
- {
1358
- isPublish: type === "publish",
1359
- b: (children) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children })
1360
- }
1361
- ) }) : /* @__PURE__ */ jsx(
1362
- ReleaseActionOptions,
1363
- {
1364
- selected: type,
1365
- handleChange: (e) => handleChangeType(e, id, [key, actionIndex]),
1366
- name: `release-action-${id}-type`,
1367
- disabled: !canUpdate
1368
- }
1369
- ) }),
1370
- !release.releasedAt && /* @__PURE__ */ jsxs(Fragment, { children: [
1371
- /* @__PURE__ */ jsx(Td, { width: "20%", minWidth: "200px", children: /* @__PURE__ */ jsx(
1372
- EntryValidationPopover,
1373
- {
1374
- action: type,
1375
- schema: contentTypes?.[contentType.uid],
1376
- entry,
1377
- status
1378
- }
1379
- ) }),
1380
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxs(ReleaseActionMenu.Root, { children: [
1381
- /* @__PURE__ */ jsx(
1382
- ReleaseActionMenu.ReleaseActionEntryLinkItem,
1383
- {
1384
- contentTypeUid: contentType.uid,
1385
- documentId: entry.documentId,
1386
- locale: locale?.code
1387
- }
1388
- ),
1389
- /* @__PURE__ */ jsx(
1390
- ReleaseActionMenu.DeleteReleaseActionItem,
1391
- {
1392
- releaseId: release.id,
1393
- actionId: id
1394
- }
1395
- )
1396
- ] }) }) })
1397
- ] })
1398
- ] }, id)
1399
- ) })
1400
- ] })
1401
- }
1402
- )
1403
- ] }, `releases-group-${key}`)),
1404
- /* @__PURE__ */ jsxs(
1405
- Pagination.Root,
1406
- {
1407
- ...releaseMeta?.pagination,
1408
- defaultPageSize: releaseMeta?.pagination?.pageSize,
1409
- children: [
1410
- /* @__PURE__ */ jsx(Pagination.PageSize, {}),
1411
- /* @__PURE__ */ jsx(Pagination.Links, {})
1412
- ]
1413
- }
1414
- )
1415
- ] }) });
1416
- };
1417
- const ReleaseDetailsPage = () => {
1418
- const { formatMessage } = useIntl();
1419
- const { releaseId } = useParams();
1420
- const { toggleNotification } = useNotification();
1421
- const { formatAPIError } = useAPIErrorHandler();
1422
- const navigate = useNavigate();
1423
- const [releaseModalShown, setReleaseModalShown] = React.useState(false);
1424
- const [showWarningSubmit, setWarningSubmit] = React.useState(false);
1425
- const {
1426
- isLoading: isLoadingDetails,
1427
- data,
1428
- isSuccess: isSuccessDetails
1429
- } = useGetReleaseQuery(
1430
- { id: releaseId },
1431
- {
1432
- skip: !releaseId
1433
- }
1434
- );
1435
- const { data: dataTimezone, isLoading: isLoadingTimezone } = useGetReleaseSettingsQuery();
1436
- const [updateRelease, { isLoading: isSubmittingForm }] = useUpdateReleaseMutation();
1437
- const [deleteRelease] = useDeleteReleaseMutation();
1438
- const toggleEditReleaseModal = () => {
1439
- setReleaseModalShown((prev) => !prev);
1440
- };
1441
- const getTimezoneValue = () => {
1442
- if (releaseData?.timezone) {
1443
- return releaseData.timezone;
1444
- } else {
1445
- if (dataTimezone?.data.defaultTimezone) {
1446
- return dataTimezone.data.defaultTimezone;
1447
- }
1448
- return null;
1449
- }
1450
- };
1451
- const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
1452
- if (isLoadingDetails || isLoadingTimezone) {
1453
- return /* @__PURE__ */ jsx(
1454
- ReleaseDetailsLayout,
1455
- {
1456
- toggleEditReleaseModal,
1457
- toggleWarningSubmit,
1458
- children: /* @__PURE__ */ jsx(Page.Loading, {})
1459
- }
1460
- );
1461
- }
1462
- if (!releaseId) {
1463
- return /* @__PURE__ */ jsx(Navigate, { to: ".." });
1464
- }
1465
- const releaseData = isSuccessDetails && data?.data || null;
1466
- const title = releaseData?.name || "";
1467
- const timezone = getTimezoneValue();
1468
- const scheduledAt = releaseData?.scheduledAt && timezone ? utcToZonedTime(releaseData.scheduledAt, timezone) : null;
1469
- const date = scheduledAt ? format$1(scheduledAt, "yyyy-MM-dd") : void 0;
1470
- const time = scheduledAt ? format$1(scheduledAt, "HH:mm") : "";
1471
- const handleEditRelease = async (values) => {
1472
- const response = await updateRelease({
1473
- id: releaseId,
1474
- name: values.name,
1475
- scheduledAt: values.scheduledAt,
1476
- timezone: values.timezone
1477
- });
1478
- if ("data" in response) {
1479
- toggleNotification({
1480
- type: "success",
1481
- message: formatMessage({
1482
- id: "content-releases.modal.release-updated-notification-success",
1483
- defaultMessage: "Release updated."
1484
- })
1485
- });
1486
- toggleEditReleaseModal();
1487
- } else if (isFetchError(response.error)) {
1488
- toggleNotification({
1489
- type: "danger",
1490
- message: formatAPIError(response.error)
1491
- });
1492
- } else {
1493
- toggleNotification({
1494
- type: "danger",
1495
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1496
- });
1497
- }
1498
- };
1499
- const handleDeleteRelease = async () => {
1500
- const response = await deleteRelease({
1501
- id: releaseId
1502
- });
1503
- if ("data" in response) {
1504
- navigate("..");
1505
- } else if (isFetchError(response.error)) {
1506
- toggleNotification({
1507
- type: "danger",
1508
- message: formatAPIError(response.error)
1509
- });
1510
- } else {
1511
- toggleNotification({
1512
- type: "danger",
1513
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1514
- });
1515
- }
1516
- };
1517
- return /* @__PURE__ */ jsxs(
1518
- ReleaseDetailsLayout,
1519
- {
1520
- toggleEditReleaseModal,
1521
- toggleWarningSubmit,
1522
- children: [
1523
- /* @__PURE__ */ jsx(ReleaseDetailsBody, { releaseId }),
1524
- /* @__PURE__ */ jsx(
1525
- ReleaseModal,
1526
- {
1527
- open: releaseModalShown,
1528
- handleClose: toggleEditReleaseModal,
1529
- handleSubmit: handleEditRelease,
1530
- isLoading: isLoadingDetails || isSubmittingForm,
1531
- initialValues: {
1532
- name: title || "",
1533
- scheduledAt,
1534
- date,
1535
- time,
1536
- isScheduled: Boolean(scheduledAt),
1537
- timezone
1538
- }
1539
- }
1540
- ),
1541
- /* @__PURE__ */ jsx(Dialog.Root, { open: showWarningSubmit, onOpenChange: toggleWarningSubmit, children: /* @__PURE__ */ jsx(ConfirmDialog, { onConfirm: handleDeleteRelease, children: formatMessage({
1542
- id: "content-releases.dialog.confirmation-message",
1543
- defaultMessage: "Are you sure you want to delete this release?"
1544
- }) }) })
1545
- ]
1546
- }
1547
- );
1548
- };
1549
- const App = () => {
1550
- return /* @__PURE__ */ jsx(Page.Protect, { permissions: PERMISSIONS.main, children: /* @__PURE__ */ jsxs(Routes, { children: [
1551
- /* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(ReleasesPage, {}) }),
1552
- /* @__PURE__ */ jsx(Route, { path: ":releaseId", element: /* @__PURE__ */ jsx(ReleaseDetailsPage, {}) })
1553
- ] }) });
1554
- };
1555
- export {
1556
- App
1557
- };
1558
- //# sourceMappingURL=App-CiZCkScI.mjs.map