@strapi/content-releases 0.0.0-experimental.check-license → 0.0.0-experimental.d8a676a242377cee820b59b21a05d47290d9ac73

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