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

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