@strapi/content-releases 0.0.0-experimental.e1ede8c55a0e1e22ce20137bf238fc374bd5dd51 → 0.0.0-experimental.e47108ccbbc4ad1bfaf4526fa6b70d6ace1ca7a9

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