@strapi/content-releases 0.0.0-next.09b9d36b22a205d90c9303f2e37134938cf76c90 → 0.0.0-next.0a8a7f1e0ee2fb410eeffb9eb1943d28f1f24705

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