@strapi/content-releases 0.0.0-next.e1ede8c55a0e1e22ce20137bf238fc374bd5dd51 → 0.0.0-next.e6eaa3d0563c85f80fd88b258df70a55c057096e

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-R-kksTW7.mjs +1308 -0
  2. package/dist/_chunks/App-R-kksTW7.mjs.map +1 -0
  3. package/dist/_chunks/{App-o5_WfqR-.js → App-WZHc_d3m.js} +783 -419
  4. package/dist/_chunks/App-WZHc_d3m.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-ngTk74JV.mjs → en-RdapH-9X.mjs} +22 -7
  10. package/dist/_chunks/en-RdapH-9X.mjs.map +1 -0
  11. package/dist/_chunks/{en-haKSQIo8.js → en-faJDuv3q.js} +22 -7
  12. package/dist/_chunks/en-faJDuv3q.js.map +1 -0
  13. package/dist/_chunks/{index-EdBmRHRU.js → index-k6fw6RYi.js} +240 -56
  14. package/dist/_chunks/index-k6fw6RYi.js.map +1 -0
  15. package/dist/_chunks/{index-XAQOX_IB.mjs → index-vpSczx8v.mjs} +255 -71
  16. package/dist/_chunks/index-vpSczx8v.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-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,1308 @@
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-vpSczx8v.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(releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
701
+ };
702
+ const getCreatedByUser = () => {
703
+ if (!release?.createdBy) {
704
+ return null;
705
+ }
706
+ if (release.createdBy.username) {
707
+ return release.createdBy.username;
708
+ }
709
+ if (release.createdBy.firstname) {
710
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
711
+ }
712
+ return release.createdBy.email;
713
+ };
714
+ if (isLoadingDetails) {
715
+ return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
716
+ }
717
+ if (isError || !release) {
718
+ return /* @__PURE__ */ jsx(
719
+ Redirect,
720
+ {
721
+ to: {
722
+ pathname: "/plugins/content-releases",
723
+ state: {
724
+ errors: [
725
+ {
726
+ code: error?.code
727
+ }
728
+ ]
729
+ }
730
+ }
731
+ }
732
+ );
733
+ }
734
+ const totalEntries = release.actions.meta.count || 0;
735
+ const hasCreatedByUser = Boolean(getCreatedByUser());
736
+ const isScheduled = release.scheduledAt && release.timezone;
737
+ const numberOfEntriesText = formatMessage(
738
+ {
739
+ id: "content-releases.pages.Details.header-subtitle",
740
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
741
+ },
742
+ { number: totalEntries }
743
+ );
744
+ const scheduledText = isScheduled ? formatMessage(
745
+ {
746
+ id: "content-releases.pages.ReleaseDetails.header-subtitle.scheduled",
747
+ defaultMessage: "Scheduled for {date} at {time} ({offset})"
748
+ },
749
+ {
750
+ date: formatDate(new Date(release.scheduledAt), {
751
+ weekday: "long",
752
+ day: "numeric",
753
+ month: "long",
754
+ year: "numeric",
755
+ timeZone: release.timezone
756
+ }),
757
+ time: formatTime(new Date(release.scheduledAt), {
758
+ timeZone: release.timezone,
759
+ hourCycle: "h23"
760
+ }),
761
+ offset: getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
762
+ }
763
+ ) : "";
764
+ return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoadingDetails, children: [
765
+ /* @__PURE__ */ jsx(
766
+ HeaderLayout,
767
+ {
768
+ title: release.name,
769
+ subtitle: /* @__PURE__ */ jsxs(Flex, { gap: 2, lineHeight: 6, children: [
770
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", variant: "epsilon", children: numberOfEntriesText + (isScheduled ? ` - ${scheduledText}` : "") }),
771
+ /* @__PURE__ */ jsx(Badge, { ...getBadgeProps(release.status), children: release.status })
772
+ ] }),
773
+ navigationAction: /* @__PURE__ */ jsx(Link$1, { startIcon: /* @__PURE__ */ jsx(ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
774
+ id: "global.back",
775
+ defaultMessage: "Back"
776
+ }) }),
777
+ primaryAction: !release.releasedAt && /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
778
+ /* @__PURE__ */ jsxs(Menu.Root, { children: [
779
+ /* @__PURE__ */ jsx(
780
+ Menu.Trigger,
781
+ {
782
+ as: IconButton,
783
+ paddingLeft: 2,
784
+ paddingRight: 2,
785
+ "aria-label": formatMessage({
786
+ id: "content-releases.header.actions.open-release-actions",
787
+ defaultMessage: "Release edit and delete menu"
788
+ }),
789
+ icon: /* @__PURE__ */ jsx(More, {}),
790
+ variant: "tertiary"
791
+ }
792
+ ),
793
+ /* @__PURE__ */ jsxs(Menu.Content, { top: 1, popoverPlacement: "bottom-end", children: [
794
+ /* @__PURE__ */ jsxs(
795
+ Flex,
796
+ {
797
+ alignItems: "center",
798
+ justifyContent: "center",
799
+ direction: "column",
800
+ padding: 1,
801
+ width: "100%",
802
+ children: [
803
+ /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
804
+ /* @__PURE__ */ jsx(PencilIcon, {}),
805
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
806
+ id: "content-releases.header.actions.edit",
807
+ defaultMessage: "Edit"
808
+ }) })
809
+ ] }) }),
810
+ /* @__PURE__ */ jsx(
811
+ StyledMenuItem,
812
+ {
813
+ disabled: !canDelete,
814
+ onSelect: toggleWarningSubmit,
815
+ variant: "danger",
816
+ children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
817
+ /* @__PURE__ */ jsx(TrashIcon, {}),
818
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
819
+ id: "content-releases.header.actions.delete",
820
+ defaultMessage: "Delete"
821
+ }) })
822
+ ] })
823
+ }
824
+ )
825
+ ]
826
+ }
827
+ ),
828
+ /* @__PURE__ */ jsxs(
829
+ ReleaseInfoWrapper,
830
+ {
831
+ direction: "column",
832
+ justifyContent: "center",
833
+ alignItems: "flex-start",
834
+ gap: 1,
835
+ padding: 5,
836
+ children: [
837
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
838
+ id: "content-releases.header.actions.created",
839
+ defaultMessage: "Created"
840
+ }) }),
841
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", color: "neutral300", children: [
842
+ /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(release.createdAt) }),
843
+ formatMessage(
844
+ {
845
+ id: "content-releases.header.actions.created.description",
846
+ defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
847
+ },
848
+ { createdBy: getCreatedByUser(), hasCreatedByUser }
849
+ )
850
+ ] })
851
+ ]
852
+ }
853
+ )
854
+ ] })
855
+ ] }),
856
+ /* @__PURE__ */ jsx(Button, { size: "S", variant: "tertiary", onClick: handleRefresh, children: formatMessage({
857
+ id: "content-releases.header.actions.refresh",
858
+ defaultMessage: "Refresh"
859
+ }) }),
860
+ /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.publish, children: /* @__PURE__ */ jsx(
861
+ Button,
862
+ {
863
+ size: "S",
864
+ variant: "default",
865
+ onClick: handlePublishRelease,
866
+ loading: isPublishing,
867
+ disabled: release.actions.meta.count === 0,
868
+ children: formatMessage({
869
+ id: "content-releases.header.actions.publish",
870
+ defaultMessage: "Publish"
871
+ })
872
+ }
873
+ ) })
874
+ ] })
875
+ }
876
+ ),
877
+ children
878
+ ] });
879
+ };
880
+ const GROUP_BY_OPTIONS = ["contentType", "locale", "action"];
881
+ const getGroupByOptionLabel = (value) => {
882
+ if (value === "locale") {
883
+ return {
884
+ id: "content-releases.pages.ReleaseDetails.groupBy.option.locales",
885
+ defaultMessage: "Locales"
886
+ };
887
+ }
888
+ if (value === "action") {
889
+ return {
890
+ id: "content-releases.pages.ReleaseDetails.groupBy.option.actions",
891
+ defaultMessage: "Actions"
892
+ };
893
+ }
894
+ return {
895
+ id: "content-releases.pages.ReleaseDetails.groupBy.option.content-type",
896
+ defaultMessage: "Content-Types"
897
+ };
898
+ };
899
+ const ReleaseDetailsBody = () => {
900
+ const { formatMessage } = useIntl();
901
+ const { releaseId } = useParams();
902
+ const [{ query }, setQuery] = useQueryParams();
903
+ const toggleNotification = useNotification();
904
+ const { formatAPIError } = useAPIErrorHandler();
905
+ const {
906
+ data: releaseData,
907
+ isLoading: isReleaseLoading,
908
+ isError: isReleaseError,
909
+ error: releaseError
910
+ } = useGetReleaseQuery({ id: releaseId });
911
+ const {
912
+ allowedActions: { canUpdate }
913
+ } = useRBAC(PERMISSIONS);
914
+ const release = releaseData?.data;
915
+ const selectedGroupBy = query?.groupBy || "contentType";
916
+ const {
917
+ isLoading,
918
+ isFetching,
919
+ isError,
920
+ data,
921
+ error: releaseActionsError
922
+ } = useGetReleaseActionsQuery({
923
+ ...query,
924
+ releaseId
925
+ });
926
+ const [updateReleaseAction] = useUpdateReleaseActionMutation();
927
+ const handleChangeType = async (e, actionId, actionPath) => {
928
+ const response = await updateReleaseAction({
929
+ params: {
930
+ releaseId,
931
+ actionId
932
+ },
933
+ body: {
934
+ type: e.target.value
935
+ },
936
+ query,
937
+ // We are passing the query params to make optimistic updates
938
+ actionPath
939
+ // We are passing the action path to found the position in the cache of the action for optimistic updates
940
+ });
941
+ if ("error" in response) {
942
+ if (isAxiosError(response.error)) {
943
+ toggleNotification({
944
+ type: "warning",
945
+ message: formatAPIError(response.error)
946
+ });
947
+ } else {
948
+ toggleNotification({
949
+ type: "warning",
950
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
951
+ });
952
+ }
953
+ }
954
+ };
955
+ if (isLoading || isReleaseLoading) {
956
+ return /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
957
+ }
958
+ const releaseActions = data?.data;
959
+ const releaseMeta = data?.meta;
960
+ const contentTypes = releaseMeta?.contentTypes || {};
961
+ const components = releaseMeta?.components || {};
962
+ if (isReleaseError || !release) {
963
+ const errorsArray = [];
964
+ if (releaseError) {
965
+ errorsArray.push({
966
+ code: releaseError.code
967
+ });
968
+ }
969
+ if (releaseActionsError) {
970
+ errorsArray.push({
971
+ code: releaseActionsError.code
972
+ });
973
+ }
974
+ return /* @__PURE__ */ jsx(
975
+ Redirect,
976
+ {
977
+ to: {
978
+ pathname: "/plugins/content-releases",
979
+ state: {
980
+ errors: errorsArray
981
+ }
982
+ }
983
+ }
984
+ );
985
+ }
986
+ if (isError || !releaseActions) {
987
+ return /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(AnErrorOccurred, {}) });
988
+ }
989
+ if (Object.keys(releaseActions).length === 0) {
990
+ return /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(
991
+ NoContent,
992
+ {
993
+ content: {
994
+ id: "content-releases.pages.Details.tab.emptyEntries",
995
+ defaultMessage: "This release is empty. Open the Content Manager, select an entry and add it to the release."
996
+ },
997
+ action: /* @__PURE__ */ jsx(
998
+ LinkButton,
999
+ {
1000
+ as: Link$2,
1001
+ to: {
1002
+ pathname: "/content-manager"
1003
+ },
1004
+ style: { textDecoration: "none" },
1005
+ variant: "secondary",
1006
+ children: formatMessage({
1007
+ id: "content-releases.page.Details.button.openContentManager",
1008
+ defaultMessage: "Open the Content Manager"
1009
+ })
1010
+ }
1011
+ )
1012
+ }
1013
+ ) });
1014
+ }
1015
+ return /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsxs(Flex, { gap: 8, direction: "column", alignItems: "stretch", children: [
1016
+ /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
1017
+ SingleSelect,
1018
+ {
1019
+ "aria-label": formatMessage({
1020
+ id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
1021
+ defaultMessage: "Group by"
1022
+ }),
1023
+ customizeContent: (value) => formatMessage(
1024
+ {
1025
+ id: `content-releases.pages.ReleaseDetails.groupBy.label`,
1026
+ defaultMessage: `Group by {groupBy}`
1027
+ },
1028
+ {
1029
+ groupBy: value
1030
+ }
1031
+ ),
1032
+ value: formatMessage(getGroupByOptionLabel(selectedGroupBy)),
1033
+ onChange: (value) => setQuery({ groupBy: value }),
1034
+ children: GROUP_BY_OPTIONS.map((option) => /* @__PURE__ */ jsx(SingleSelectOption, { value: option, children: formatMessage(getGroupByOptionLabel(option)) }, option))
1035
+ }
1036
+ ) }),
1037
+ Object.keys(releaseActions).map((key) => /* @__PURE__ */ jsxs(Flex, { gap: 4, direction: "column", alignItems: "stretch", children: [
1038
+ /* @__PURE__ */ jsx(Flex, { role: "separator", "aria-label": key, children: /* @__PURE__ */ jsx(Badge, { children: key }) }),
1039
+ /* @__PURE__ */ jsx(
1040
+ Table.Root,
1041
+ {
1042
+ rows: releaseActions[key].map((item) => ({
1043
+ ...item,
1044
+ id: Number(item.entry.id)
1045
+ })),
1046
+ colCount: releaseActions[key].length,
1047
+ isLoading,
1048
+ isFetching,
1049
+ children: /* @__PURE__ */ jsxs(Table.Content, { children: [
1050
+ /* @__PURE__ */ jsxs(Table.Head, { children: [
1051
+ /* @__PURE__ */ jsx(
1052
+ Table.HeaderCell,
1053
+ {
1054
+ fieldSchemaType: "string",
1055
+ label: formatMessage({
1056
+ id: "content-releases.page.ReleaseDetails.table.header.label.name",
1057
+ defaultMessage: "name"
1058
+ }),
1059
+ name: "name"
1060
+ }
1061
+ ),
1062
+ /* @__PURE__ */ jsx(
1063
+ Table.HeaderCell,
1064
+ {
1065
+ fieldSchemaType: "string",
1066
+ label: formatMessage({
1067
+ id: "content-releases.page.ReleaseDetails.table.header.label.locale",
1068
+ defaultMessage: "locale"
1069
+ }),
1070
+ name: "locale"
1071
+ }
1072
+ ),
1073
+ /* @__PURE__ */ jsx(
1074
+ Table.HeaderCell,
1075
+ {
1076
+ fieldSchemaType: "string",
1077
+ label: formatMessage({
1078
+ id: "content-releases.page.ReleaseDetails.table.header.label.content-type",
1079
+ defaultMessage: "content-type"
1080
+ }),
1081
+ name: "content-type"
1082
+ }
1083
+ ),
1084
+ /* @__PURE__ */ jsx(
1085
+ Table.HeaderCell,
1086
+ {
1087
+ fieldSchemaType: "string",
1088
+ label: formatMessage({
1089
+ id: "content-releases.page.ReleaseDetails.table.header.label.action",
1090
+ defaultMessage: "action"
1091
+ }),
1092
+ name: "action"
1093
+ }
1094
+ ),
1095
+ !release.releasedAt && /* @__PURE__ */ jsx(
1096
+ Table.HeaderCell,
1097
+ {
1098
+ fieldSchemaType: "string",
1099
+ label: formatMessage({
1100
+ id: "content-releases.page.ReleaseDetails.table.header.label.status",
1101
+ defaultMessage: "status"
1102
+ }),
1103
+ name: "status"
1104
+ }
1105
+ )
1106
+ ] }),
1107
+ /* @__PURE__ */ jsx(Table.LoadingBody, {}),
1108
+ /* @__PURE__ */ jsx(Table.Body, { children: releaseActions[key].map(
1109
+ ({ id, contentType, locale, type, entry }, actionIndex) => /* @__PURE__ */ jsxs(Tr, { children: [
1110
+ /* @__PURE__ */ jsx(Td, { width: "25%", maxWidth: "200px", children: /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: `${contentType.mainFieldValue || entry.id}` }) }),
1111
+ /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
1112
+ /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: contentType.displayName || "" }) }),
1113
+ /* @__PURE__ */ jsx(Td, { width: "20%", children: release.releasedAt ? /* @__PURE__ */ jsx(Typography, { children: formatMessage(
1114
+ {
1115
+ id: "content-releases.page.ReleaseDetails.table.action-published",
1116
+ defaultMessage: "This entry was <b>{isPublish, select, true {published} other {unpublished}}</b>."
1117
+ },
1118
+ {
1119
+ isPublish: type === "publish",
1120
+ b: (children) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children })
1121
+ }
1122
+ ) }) : /* @__PURE__ */ jsx(
1123
+ ReleaseActionOptions,
1124
+ {
1125
+ selected: type,
1126
+ handleChange: (e) => handleChangeType(e, id, [key, actionIndex]),
1127
+ name: `release-action-${id}-type`,
1128
+ disabled: !canUpdate
1129
+ }
1130
+ ) }),
1131
+ !release.releasedAt && /* @__PURE__ */ jsxs(Fragment, { children: [
1132
+ /* @__PURE__ */ jsx(Td, { width: "20%", minWidth: "200px", children: /* @__PURE__ */ jsx(
1133
+ EntryValidationText,
1134
+ {
1135
+ action: type,
1136
+ schema: contentTypes?.[contentType.uid],
1137
+ components,
1138
+ entry
1139
+ }
1140
+ ) }),
1141
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxs(ReleaseActionMenu.Root, { children: [
1142
+ /* @__PURE__ */ jsx(
1143
+ ReleaseActionMenu.ReleaseActionEntryLinkItem,
1144
+ {
1145
+ contentTypeUid: contentType.uid,
1146
+ entryId: entry.id,
1147
+ locale: locale?.code
1148
+ }
1149
+ ),
1150
+ /* @__PURE__ */ jsx(
1151
+ ReleaseActionMenu.DeleteReleaseActionItem,
1152
+ {
1153
+ releaseId: release.id,
1154
+ actionId: id
1155
+ }
1156
+ )
1157
+ ] }) }) })
1158
+ ] })
1159
+ ] }, id)
1160
+ ) })
1161
+ ] })
1162
+ }
1163
+ )
1164
+ ] }, `releases-group-${key}`)),
1165
+ /* @__PURE__ */ jsxs(Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1166
+ /* @__PURE__ */ jsx(PageSizeURLQuery, { defaultValue: releaseMeta?.pagination?.pageSize.toString() }),
1167
+ /* @__PURE__ */ jsx(
1168
+ PaginationURLQuery,
1169
+ {
1170
+ pagination: {
1171
+ pageCount: releaseMeta?.pagination?.pageCount || 0
1172
+ }
1173
+ }
1174
+ )
1175
+ ] })
1176
+ ] }) });
1177
+ };
1178
+ const ReleaseDetailsPage = () => {
1179
+ const { formatMessage } = useIntl();
1180
+ const { releaseId } = useParams();
1181
+ const toggleNotification = useNotification();
1182
+ const { formatAPIError } = useAPIErrorHandler();
1183
+ const { replace } = useHistory();
1184
+ const [releaseModalShown, setReleaseModalShown] = React.useState(false);
1185
+ const [showWarningSubmit, setWarningSubmit] = React.useState(false);
1186
+ const {
1187
+ isLoading: isLoadingDetails,
1188
+ data,
1189
+ isSuccess: isSuccessDetails
1190
+ } = useGetReleaseQuery({ id: releaseId });
1191
+ const [updateRelease, { isLoading: isSubmittingForm }] = useUpdateReleaseMutation();
1192
+ const [deleteRelease, { isLoading: isDeletingRelease }] = useDeleteReleaseMutation();
1193
+ const toggleEditReleaseModal = () => {
1194
+ setReleaseModalShown((prev) => !prev);
1195
+ };
1196
+ const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
1197
+ if (isLoadingDetails) {
1198
+ return /* @__PURE__ */ jsx(
1199
+ ReleaseDetailsLayout,
1200
+ {
1201
+ toggleEditReleaseModal,
1202
+ toggleWarningSubmit,
1203
+ children: /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) })
1204
+ }
1205
+ );
1206
+ }
1207
+ const releaseData = isSuccessDetails && data?.data || null;
1208
+ const title = releaseData?.name || "";
1209
+ const timezone = releaseData?.timezone ?? null;
1210
+ const scheduledAt = releaseData?.scheduledAt && timezone ? utcToZonedTime(releaseData.scheduledAt, timezone) : null;
1211
+ const date = scheduledAt ? new Date(format(scheduledAt, "yyyy-MM-dd")) : null;
1212
+ const time = scheduledAt ? format(scheduledAt, "HH:mm") : "";
1213
+ const handleEditRelease = async (values) => {
1214
+ const response = await updateRelease({
1215
+ id: releaseId,
1216
+ name: values.name,
1217
+ scheduledAt: values.scheduledAt,
1218
+ timezone: values.timezone
1219
+ });
1220
+ if ("data" in response) {
1221
+ toggleNotification({
1222
+ type: "success",
1223
+ message: formatMessage({
1224
+ id: "content-releases.modal.release-updated-notification-success",
1225
+ defaultMessage: "Release updated."
1226
+ })
1227
+ });
1228
+ } else if (isAxiosError(response.error)) {
1229
+ toggleNotification({
1230
+ type: "warning",
1231
+ message: formatAPIError(response.error)
1232
+ });
1233
+ } else {
1234
+ toggleNotification({
1235
+ type: "warning",
1236
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1237
+ });
1238
+ }
1239
+ toggleEditReleaseModal();
1240
+ };
1241
+ const handleDeleteRelease = async () => {
1242
+ const response = await deleteRelease({
1243
+ id: releaseId
1244
+ });
1245
+ if ("data" in response) {
1246
+ replace("/plugins/content-releases");
1247
+ } else if (isAxiosError(response.error)) {
1248
+ toggleNotification({
1249
+ type: "warning",
1250
+ message: formatAPIError(response.error)
1251
+ });
1252
+ } else {
1253
+ toggleNotification({
1254
+ type: "warning",
1255
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1256
+ });
1257
+ }
1258
+ };
1259
+ return /* @__PURE__ */ jsxs(
1260
+ ReleaseDetailsLayout,
1261
+ {
1262
+ toggleEditReleaseModal,
1263
+ toggleWarningSubmit,
1264
+ children: [
1265
+ /* @__PURE__ */ jsx(ReleaseDetailsBody, {}),
1266
+ releaseModalShown && /* @__PURE__ */ jsx(
1267
+ ReleaseModal,
1268
+ {
1269
+ handleClose: toggleEditReleaseModal,
1270
+ handleSubmit: handleEditRelease,
1271
+ isLoading: isLoadingDetails || isSubmittingForm,
1272
+ initialValues: {
1273
+ name: title || "",
1274
+ scheduledAt,
1275
+ date,
1276
+ time,
1277
+ isScheduled: Boolean(scheduledAt),
1278
+ timezone
1279
+ }
1280
+ }
1281
+ ),
1282
+ /* @__PURE__ */ jsx(
1283
+ ConfirmDialog,
1284
+ {
1285
+ bodyText: {
1286
+ id: "content-releases.dialog.confirmation-message",
1287
+ defaultMessage: "Are you sure you want to delete this release?"
1288
+ },
1289
+ isOpen: showWarningSubmit,
1290
+ isConfirmButtonLoading: isDeletingRelease,
1291
+ onToggleDialog: toggleWarningSubmit,
1292
+ onConfirm: handleDeleteRelease
1293
+ }
1294
+ )
1295
+ ]
1296
+ }
1297
+ );
1298
+ };
1299
+ const App = () => {
1300
+ return /* @__PURE__ */ jsx(CheckPagePermissions, { permissions: PERMISSIONS.main, children: /* @__PURE__ */ jsxs(Switch, { children: [
1301
+ /* @__PURE__ */ jsx(Route, { exact: true, path: `/plugins/${pluginId}`, component: ReleasesPage }),
1302
+ /* @__PURE__ */ jsx(Route, { exact: true, path: `/plugins/${pluginId}/:releaseId`, component: ReleaseDetailsPage })
1303
+ ] }) });
1304
+ };
1305
+ export {
1306
+ App
1307
+ };
1308
+ //# sourceMappingURL=App-R-kksTW7.mjs.map