@strapi/content-releases 0.0.0-next.6d59515520a3850456f256fb0e4c54b75054ddf4 → 0.0.0-next.aa7c7ec6724534e157d8a23fe85ee8318dabbf37

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.
@@ -1,11 +1,11 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { useNotification, useAPIErrorHandler, LoadingIndicatorPage, ConfirmDialog, useRBAC, RelativeTime, CheckPermissions, useQueryParams, AnErrorOccurred, NoContent, Table, PageSizeURLQuery, PaginationURLQuery, CheckPagePermissions } from "@strapi/helper-plugin";
2
+ import { useNotification, useAPIErrorHandler, LoadingIndicatorPage, ConfirmDialog, useRBAC, useTracking, RelativeTime, CheckPermissions, useQueryParams, AnErrorOccurred, NoContent, Table, PageSizeURLQuery, PaginationURLQuery, CheckPagePermissions } from "@strapi/helper-plugin";
3
3
  import { useLocation, useParams, useHistory, Redirect, Link as Link$1, Switch, Route } from "react-router-dom";
4
- import { p as pluginId, u as useGetReleaseQuery, a as useUpdateReleaseMutation, b as useDeleteReleaseMutation, c as usePublishReleaseMutation, P as PERMISSIONS, d as useTypedDispatch, e as useGetReleaseActionsQuery, f as useUpdateReleaseActionMutation, R as ReleaseActionOptions, g as ReleaseActionMenu, i as isAxiosError, r as releaseApi, h as useGetReleasesQuery, j as useCreateReleaseMutation } from "./index-c4zRX_sg.mjs";
4
+ import { p as pluginId, u as useGetReleaseQuery, a as useUpdateReleaseMutation, b as useDeleteReleaseMutation, c as usePublishReleaseMutation, P as PERMISSIONS, d as useTypedDispatch, e as useGetReleaseActionsQuery, f as useUpdateReleaseActionMutation, R as ReleaseActionOptions, g as ReleaseActionMenu, i as isAxiosError, r as releaseApi, h as useGetReleasesQuery, j as useCreateReleaseMutation } from "./index-8aK7GzI5.mjs";
5
5
  import * as React from "react";
6
- import { unstable_useDocument } from "@strapi/admin/strapi-admin";
7
- import { ModalLayout, ModalHeader, Typography, ModalBody, TextInput, ModalFooter, Button, Flex, ContentLayout, Main, HeaderLayout, Link, IconButton, Popover, SingleSelect, SingleSelectOption, Badge, Tr, Td, Icon, Tooltip, TabGroup, Box, Tabs, Tab, Divider, TabPanels, TabPanel, EmptyStateLayout, Grid, GridItem } from "@strapi/design-system";
8
- import { LinkButton, Link as Link$2 } from "@strapi/design-system/v2";
6
+ import { unstable_useDocument, useLicenseLimits } from "@strapi/admin/strapi-admin";
7
+ import { ModalLayout, ModalHeader, Typography, ModalBody, TextInput, ModalFooter, Button, Flex, ContentLayout, Main, HeaderLayout, Link, IconButton, SingleSelect, SingleSelectOption, Badge, Tr, Td, Icon, Tooltip, Alert, TabGroup, Box, Tabs, Tab, Divider, TabPanels, TabPanel, EmptyStateLayout, Grid, GridItem } from "@strapi/design-system";
8
+ import { Menu, LinkButton, Link as Link$2 } from "@strapi/design-system/v2";
9
9
  import { Pencil, Trash, ArrowLeft, More, CrossCircle, CheckCircle, Plus, EmptyDocuments } from "@strapi/icons";
10
10
  import { useIntl } from "react-intl";
11
11
  import styled from "styled-components";
@@ -16,7 +16,9 @@ import "axios";
16
16
  import "@reduxjs/toolkit/query/react";
17
17
  import "react-redux";
18
18
  const RELEASE_SCHEMA = yup.object().shape({
19
- name: yup.string().trim().required()
19
+ name: yup.string().trim().required(),
20
+ // scheduledAt is a date, but we always receive strings from the client
21
+ scheduledAt: yup.string().nullable()
20
22
  }).required().noUnknown();
21
23
  const ReleaseModal = ({
22
24
  handleClose,
@@ -90,10 +92,7 @@ const ReleaseInfoWrapper = styled(Flex)`
90
92
  border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
91
93
  border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
92
94
  `;
93
- const StyledFlex = styled(Flex)`
94
- align-self: stretch;
95
- cursor: ${({ disabled }) => disabled ? "not-allowed" : "pointer"};
96
-
95
+ const StyledMenuItem = styled(Menu.Item)`
97
96
  svg path {
98
97
  fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
99
98
  }
@@ -102,15 +101,15 @@ const StyledFlex = styled(Flex)`
102
101
  }
103
102
  `;
104
103
  const PencilIcon = styled(Pencil)`
105
- width: ${({ theme }) => theme.spaces[4]};
106
- height: ${({ theme }) => theme.spaces[4]};
104
+ width: ${({ theme }) => theme.spaces[3]};
105
+ height: ${({ theme }) => theme.spaces[3]};
107
106
  path {
108
107
  fill: ${({ theme }) => theme.colors.neutral600};
109
108
  }
110
109
  `;
111
110
  const TrashIcon = styled(Trash)`
112
- width: ${({ theme }) => theme.spaces[4]};
113
- height: ${({ theme }) => theme.spaces[4]};
111
+ width: ${({ theme }) => theme.spaces[3]};
112
+ height: ${({ theme }) => theme.spaces[3]};
114
113
  path {
115
114
  fill: ${({ theme }) => theme.colors.danger600};
116
115
  }
@@ -118,24 +117,6 @@ const TrashIcon = styled(Trash)`
118
117
  const TypographyMaxWidth = styled(Typography)`
119
118
  max-width: 300px;
120
119
  `;
121
- const PopoverButton = ({ onClick, disabled, children }) => {
122
- return /* @__PURE__ */ jsx(
123
- StyledFlex,
124
- {
125
- paddingTop: 2,
126
- paddingBottom: 2,
127
- paddingLeft: 4,
128
- paddingRight: 4,
129
- alignItems: "center",
130
- gap: 2,
131
- as: "button",
132
- hasRadius: true,
133
- onClick,
134
- disabled,
135
- children
136
- }
137
- );
138
- };
139
120
  const EntryValidationText = ({ action, schema, components, entry }) => {
140
121
  const { formatMessage } = useIntl();
141
122
  const { validate } = unstable_useDocument();
@@ -186,8 +167,6 @@ const ReleaseDetailsLayout = ({
186
167
  }) => {
187
168
  const { formatMessage } = useIntl();
188
169
  const { releaseId } = useParams();
189
- const [isPopoverVisible, setIsPopoverVisible] = React.useState(false);
190
- const moreButtonRef = React.useRef(null);
191
170
  const {
192
171
  data,
193
172
  isLoading: isLoadingDetails,
@@ -201,14 +180,8 @@ const ReleaseDetailsLayout = ({
201
180
  allowedActions: { canUpdate, canDelete }
202
181
  } = useRBAC(PERMISSIONS);
203
182
  const dispatch = useTypedDispatch();
183
+ const { trackUsage } = useTracking();
204
184
  const release = data?.data;
205
- const handleTogglePopover = () => {
206
- setIsPopoverVisible((prev) => !prev);
207
- };
208
- const openReleaseModal = () => {
209
- toggleEditReleaseModal();
210
- handleTogglePopover();
211
- };
212
185
  const handlePublishRelease = async () => {
213
186
  const response = await publishRelease({ id: releaseId });
214
187
  if ("data" in response) {
@@ -219,6 +192,12 @@ const ReleaseDetailsLayout = ({
219
192
  defaultMessage: "Release was published successfully."
220
193
  })
221
194
  });
195
+ const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
196
+ trackUsage("didPublishRelease", {
197
+ totalEntries: totalEntries2,
198
+ totalPublishedEntries,
199
+ totalUnpublishedEntries
200
+ });
222
201
  } else if (isAxiosError(response.error)) {
223
202
  toggleNotification({
224
203
  type: "warning",
@@ -231,13 +210,21 @@ const ReleaseDetailsLayout = ({
231
210
  });
232
211
  }
233
212
  };
234
- const openWarningConfirmDialog = () => {
235
- toggleWarningSubmit();
236
- handleTogglePopover();
237
- };
238
213
  const handleRefresh = () => {
239
214
  dispatch(releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
240
215
  };
216
+ const getCreatedByUser = () => {
217
+ if (!release?.createdBy) {
218
+ return null;
219
+ }
220
+ if (release.createdBy.username) {
221
+ return release.createdBy.username;
222
+ }
223
+ if (release.createdBy.firstname) {
224
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
225
+ }
226
+ return release.createdBy.email;
227
+ };
241
228
  if (isLoadingDetails) {
242
229
  return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
243
230
  }
@@ -259,7 +246,7 @@ const ReleaseDetailsLayout = ({
259
246
  );
260
247
  }
261
248
  const totalEntries = release.actions.meta.count || 0;
262
- const createdBy = release.createdBy.lastname ? `${release.createdBy.firstname} ${release.createdBy.lastname}` : `${release.createdBy.firstname}`;
249
+ const hasCreatedByUser = Boolean(getCreatedByUser());
263
250
  return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoadingDetails, children: [
264
251
  /* @__PURE__ */ jsx(
265
252
  HeaderLayout,
@@ -277,72 +264,98 @@ const ReleaseDetailsLayout = ({
277
264
  defaultMessage: "Back"
278
265
  }) }),
279
266
  primaryAction: !release.releasedAt && /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
280
- /* @__PURE__ */ jsx(
281
- IconButton,
282
- {
283
- label: formatMessage({
284
- id: "content-releases.header.actions.open-release-actions",
285
- defaultMessage: "Release actions"
286
- }),
287
- ref: moreButtonRef,
288
- onClick: handleTogglePopover,
289
- children: /* @__PURE__ */ jsx(More, {})
290
- }
291
- ),
292
- isPopoverVisible && /* @__PURE__ */ jsxs(
293
- Popover,
294
- {
295
- source: moreButtonRef,
296
- placement: "bottom-end",
297
- onDismiss: handleTogglePopover,
298
- spacing: 4,
299
- minWidth: "242px",
300
- children: [
301
- /* @__PURE__ */ jsxs(Flex, { alignItems: "center", justifyContent: "center", direction: "column", padding: 1, children: [
302
- /* @__PURE__ */ jsxs(PopoverButton, { disabled: !canUpdate, onClick: openReleaseModal, children: [
303
- /* @__PURE__ */ jsx(PencilIcon, {}),
304
- /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
305
- id: "content-releases.header.actions.edit",
306
- defaultMessage: "Edit"
307
- }) })
308
- ] }),
309
- /* @__PURE__ */ jsxs(PopoverButton, { disabled: !canDelete, onClick: openWarningConfirmDialog, children: [
310
- /* @__PURE__ */ jsx(TrashIcon, {}),
311
- /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
312
- id: "content-releases.header.actions.delete",
313
- defaultMessage: "Delete"
314
- }) })
315
- ] })
316
- ] }),
317
- /* @__PURE__ */ jsxs(
318
- ReleaseInfoWrapper,
319
- {
320
- direction: "column",
321
- justifyContent: "center",
322
- alignItems: "flex-start",
323
- gap: 1,
324
- padding: 5,
325
- children: [
326
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
327
- id: "content-releases.header.actions.created",
328
- defaultMessage: "Created"
329
- }) }),
330
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", color: "neutral300", children: [
331
- /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(release.createdAt) }),
332
- formatMessage(
333
- {
334
- id: "content-releases.header.actions.created.description",
335
- defaultMessage: " by {createdBy}"
336
- },
337
- { createdBy }
338
- )
339
- ] })
340
- ]
341
- }
342
- )
343
- ]
344
- }
345
- ),
267
+ /* @__PURE__ */ jsxs(Menu.Root, { children: [
268
+ /* @__PURE__ */ jsx(
269
+ Menu.Trigger,
270
+ {
271
+ as: IconButton,
272
+ paddingLeft: 2,
273
+ paddingRight: 2,
274
+ "aria-label": formatMessage({
275
+ id: "content-releases.header.actions.open-release-actions",
276
+ defaultMessage: "Release edit and delete menu"
277
+ }),
278
+ icon: /* @__PURE__ */ jsx(More, {}),
279
+ variant: "tertiary"
280
+ }
281
+ ),
282
+ /* @__PURE__ */ jsxs(Menu.Content, { top: 1, popoverPlacement: "bottom-end", children: [
283
+ /* @__PURE__ */ jsxs(
284
+ Flex,
285
+ {
286
+ alignItems: "center",
287
+ justifyContent: "center",
288
+ direction: "column",
289
+ padding: 1,
290
+ width: "100%",
291
+ children: [
292
+ /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(
293
+ Flex,
294
+ {
295
+ paddingTop: 2,
296
+ paddingBottom: 2,
297
+ alignItems: "center",
298
+ gap: 2,
299
+ hasRadius: true,
300
+ width: "100%",
301
+ children: [
302
+ /* @__PURE__ */ jsx(PencilIcon, {}),
303
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
304
+ id: "content-releases.header.actions.edit",
305
+ defaultMessage: "Edit"
306
+ }) })
307
+ ]
308
+ }
309
+ ) }),
310
+ /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxs(
311
+ Flex,
312
+ {
313
+ paddingTop: 2,
314
+ paddingBottom: 2,
315
+ alignItems: "center",
316
+ gap: 2,
317
+ hasRadius: true,
318
+ width: "100%",
319
+ children: [
320
+ /* @__PURE__ */ jsx(TrashIcon, {}),
321
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
322
+ id: "content-releases.header.actions.delete",
323
+ defaultMessage: "Delete"
324
+ }) })
325
+ ]
326
+ }
327
+ ) })
328
+ ]
329
+ }
330
+ ),
331
+ /* @__PURE__ */ jsxs(
332
+ ReleaseInfoWrapper,
333
+ {
334
+ direction: "column",
335
+ justifyContent: "center",
336
+ alignItems: "flex-start",
337
+ gap: 1,
338
+ padding: 5,
339
+ children: [
340
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
341
+ id: "content-releases.header.actions.created",
342
+ defaultMessage: "Created"
343
+ }) }),
344
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", color: "neutral300", children: [
345
+ /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(release.createdAt) }),
346
+ formatMessage(
347
+ {
348
+ id: "content-releases.header.actions.created.description",
349
+ defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
350
+ },
351
+ { createdBy: getCreatedByUser(), hasCreatedByUser }
352
+ )
353
+ ] })
354
+ ]
355
+ }
356
+ )
357
+ ] })
358
+ ] }),
346
359
  /* @__PURE__ */ jsx(Button, { size: "S", variant: "tertiary", onClick: handleRefresh, children: formatMessage({
347
360
  id: "content-releases.header.actions.refresh",
348
361
  defaultMessage: "Refresh"
@@ -398,6 +411,9 @@ const ReleaseDetailsBody = () => {
398
411
  isError: isReleaseError,
399
412
  error: releaseError
400
413
  } = useGetReleaseQuery({ id: releaseId });
414
+ const {
415
+ allowedActions: { canUpdate }
416
+ } = useRBAC(PERMISSIONS);
401
417
  const release = releaseData?.data;
402
418
  const selectedGroupBy = query?.groupBy || "contentType";
403
419
  const {
@@ -411,7 +427,7 @@ const ReleaseDetailsBody = () => {
411
427
  releaseId
412
428
  });
413
429
  const [updateReleaseAction] = useUpdateReleaseActionMutation();
414
- const handleChangeType = async (e, actionId) => {
430
+ const handleChangeType = async (e, actionId, actionPath) => {
415
431
  const response = await updateReleaseAction({
416
432
  params: {
417
433
  releaseId,
@@ -419,7 +435,11 @@ const ReleaseDetailsBody = () => {
419
435
  },
420
436
  body: {
421
437
  type: e.target.value
422
- }
438
+ },
439
+ query,
440
+ // We are passing the query params to make optimistic updates
441
+ actionPath
442
+ // We are passing the action path to found the position in the cache of the action for optimistic updates
423
443
  });
424
444
  if ("error" in response) {
425
445
  if (isAxiosError(response.error)) {
@@ -500,7 +520,7 @@ const ReleaseDetailsBody = () => {
500
520
  SingleSelect,
501
521
  {
502
522
  "aria-label": formatMessage({
503
- id: "content-releases.pages.ReleaseDetails.groupBy.label",
523
+ id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
504
524
  defaultMessage: "Group by"
505
525
  }),
506
526
  customizeContent: (value) => formatMessage(
@@ -518,7 +538,7 @@ const ReleaseDetailsBody = () => {
518
538
  }
519
539
  ) }),
520
540
  Object.keys(releaseActions).map((key) => /* @__PURE__ */ jsxs(Flex, { gap: 4, direction: "column", alignItems: "stretch", children: [
521
- /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(Badge, { children: key }) }),
541
+ /* @__PURE__ */ jsx(Flex, { role: "separator", "aria-label": key, children: /* @__PURE__ */ jsx(Badge, { children: key }) }),
522
542
  /* @__PURE__ */ jsx(
523
543
  Table.Root,
524
544
  {
@@ -588,56 +608,59 @@ const ReleaseDetailsBody = () => {
588
608
  )
589
609
  ] }),
590
610
  /* @__PURE__ */ jsx(Table.LoadingBody, {}),
591
- /* @__PURE__ */ jsx(Table.Body, { children: releaseActions[key].map(({ id, contentType, locale, type, entry }) => /* @__PURE__ */ jsxs(Tr, { children: [
592
- /* @__PURE__ */ jsx(Td, { width: "25%", maxWidth: "200px", children: /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: `${contentType.mainFieldValue || entry.id}` }) }),
593
- /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
594
- /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: contentType.displayName || "" }) }),
595
- /* @__PURE__ */ jsx(Td, { width: "20%", children: release.releasedAt ? /* @__PURE__ */ jsx(Typography, { children: formatMessage(
596
- {
597
- id: "content-releases.page.ReleaseDetails.table.action-published",
598
- defaultMessage: "This entry was <b>{isPublish, select, true {published} other {unpublished}}</b>."
599
- },
600
- {
601
- isPublish: type === "publish",
602
- b: (children) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children })
603
- }
604
- ) }) : /* @__PURE__ */ jsx(
605
- ReleaseActionOptions,
606
- {
607
- selected: type,
608
- handleChange: (e) => handleChangeType(e, id),
609
- name: `release-action-${id}-type`
610
- }
611
- ) }),
612
- !release.releasedAt && /* @__PURE__ */ jsxs(Fragment, { children: [
613
- /* @__PURE__ */ jsx(Td, { width: "20%", minWidth: "200px", children: /* @__PURE__ */ jsx(
614
- EntryValidationText,
611
+ /* @__PURE__ */ jsx(Table.Body, { children: releaseActions[key].map(
612
+ ({ id, contentType, locale, type, entry }, actionIndex) => /* @__PURE__ */ jsxs(Tr, { children: [
613
+ /* @__PURE__ */ jsx(Td, { width: "25%", maxWidth: "200px", children: /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: `${contentType.mainFieldValue || entry.id}` }) }),
614
+ /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
615
+ /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: contentType.displayName || "" }) }),
616
+ /* @__PURE__ */ jsx(Td, { width: "20%", children: release.releasedAt ? /* @__PURE__ */ jsx(Typography, { children: formatMessage(
617
+ {
618
+ id: "content-releases.page.ReleaseDetails.table.action-published",
619
+ defaultMessage: "This entry was <b>{isPublish, select, true {published} other {unpublished}}</b>."
620
+ },
615
621
  {
616
- action: type,
617
- schema: contentTypes?.[contentType.uid],
618
- components,
619
- entry
622
+ isPublish: type === "publish",
623
+ b: (children) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children })
624
+ }
625
+ ) }) : /* @__PURE__ */ jsx(
626
+ ReleaseActionOptions,
627
+ {
628
+ selected: type,
629
+ handleChange: (e) => handleChangeType(e, id, [key, actionIndex]),
630
+ name: `release-action-${id}-type`,
631
+ disabled: !canUpdate
620
632
  }
621
633
  ) }),
622
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxs(ReleaseActionMenu.Root, { children: [
623
- /* @__PURE__ */ jsx(
624
- ReleaseActionMenu.ReleaseActionEntryLinkItem,
625
- {
626
- contentTypeUid: contentType.uid,
627
- entryId: entry.id,
628
- locale: locale?.code
629
- }
630
- ),
631
- /* @__PURE__ */ jsx(
632
- ReleaseActionMenu.DeleteReleaseActionItem,
634
+ !release.releasedAt && /* @__PURE__ */ jsxs(Fragment, { children: [
635
+ /* @__PURE__ */ jsx(Td, { width: "20%", minWidth: "200px", children: /* @__PURE__ */ jsx(
636
+ EntryValidationText,
633
637
  {
634
- releaseId: release.id,
635
- actionId: id
638
+ action: type,
639
+ schema: contentTypes?.[contentType.uid],
640
+ components,
641
+ entry
636
642
  }
637
- )
638
- ] }) }) })
639
- ] })
640
- ] }, id)) })
643
+ ) }),
644
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxs(ReleaseActionMenu.Root, { children: [
645
+ /* @__PURE__ */ jsx(
646
+ ReleaseActionMenu.ReleaseActionEntryLinkItem,
647
+ {
648
+ contentTypeUid: contentType.uid,
649
+ entryId: entry.id,
650
+ locale: locale?.code
651
+ }
652
+ ),
653
+ /* @__PURE__ */ jsx(
654
+ ReleaseActionMenu.DeleteReleaseActionItem,
655
+ {
656
+ releaseId: release.id,
657
+ actionId: id
658
+ }
659
+ )
660
+ ] }) }) })
661
+ ] })
662
+ ] }, id)
663
+ ) })
641
664
  ] })
642
665
  }
643
666
  )
@@ -762,37 +785,6 @@ const ReleaseDetailsPage = () => {
762
785
  }
763
786
  );
764
787
  };
765
- const ReleasesLayout = ({
766
- isLoading,
767
- totalReleases,
768
- onClickAddRelease,
769
- children
770
- }) => {
771
- const { formatMessage } = useIntl();
772
- return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoading, children: [
773
- /* @__PURE__ */ jsx(
774
- HeaderLayout,
775
- {
776
- title: formatMessage({
777
- id: "content-releases.pages.Releases.title",
778
- defaultMessage: "Releases"
779
- }),
780
- subtitle: !isLoading && formatMessage(
781
- {
782
- id: "content-releases.pages.Releases.header-subtitle",
783
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
784
- },
785
- { number: totalReleases }
786
- ),
787
- primaryAction: /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.create, children: /* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Plus, {}), onClick: onClickAddRelease, children: formatMessage({
788
- id: "content-releases.header.actions.add-release",
789
- defaultMessage: "New release"
790
- }) }) })
791
- }
792
- ),
793
- children
794
- ] });
795
- };
796
788
  const LinkCard = styled(Link$2)`
797
789
  display: block;
798
790
  `;
@@ -844,6 +836,14 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
844
836
  }
845
837
  ) }) }, id)) });
846
838
  };
839
+ const StyledAlert = styled(Alert)`
840
+ button {
841
+ display: none;
842
+ }
843
+ p + div {
844
+ margin-left: auto;
845
+ }
846
+ `;
847
847
  const INITIAL_FORM_VALUES = {
848
848
  name: ""
849
849
  };
@@ -858,6 +858,9 @@ const ReleasesPage = () => {
858
858
  const [{ query }, setQuery] = useQueryParams();
859
859
  const response = useGetReleasesQuery(query);
860
860
  const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
861
+ const { getFeature } = useLicenseLimits();
862
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
863
+ const { trackUsage } = useTracking();
861
864
  const { isLoading, isSuccess, isError } = response;
862
865
  const activeTab = response?.currentData?.meta?.activeTab || "pending";
863
866
  const activeTabIndex = ["pending", "done"].indexOf(activeTab);
@@ -886,9 +889,10 @@ const ReleasesPage = () => {
886
889
  setReleaseModalShown((prev) => !prev);
887
890
  };
888
891
  if (isLoading) {
889
- return /* @__PURE__ */ jsx(ReleasesLayout, { onClickAddRelease: toggleAddReleaseModal, isLoading: true, children: /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) }) });
892
+ return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
890
893
  }
891
894
  const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
895
+ const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
892
896
  const handleTabChange = (index) => {
893
897
  setQuery({
894
898
  ...query,
@@ -913,6 +917,7 @@ const ReleasesPage = () => {
913
917
  defaultMessage: "Release created."
914
918
  })
915
919
  });
920
+ trackUsage("didCreateRelease");
916
921
  push(`/plugins/content-releases/${response2.data.data.id}`);
917
922
  } else if (isAxiosError(response2.error)) {
918
923
  toggleNotification({
@@ -926,8 +931,60 @@ const ReleasesPage = () => {
926
931
  });
927
932
  }
928
933
  };
929
- return /* @__PURE__ */ jsxs(ReleasesLayout, { onClickAddRelease: toggleAddReleaseModal, totalReleases, children: [
934
+ return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoading, children: [
935
+ /* @__PURE__ */ jsx(
936
+ HeaderLayout,
937
+ {
938
+ title: formatMessage({
939
+ id: "content-releases.pages.Releases.title",
940
+ defaultMessage: "Releases"
941
+ }),
942
+ subtitle: formatMessage(
943
+ {
944
+ id: "content-releases.pages.Releases.header-subtitle",
945
+ defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
946
+ },
947
+ { number: totalReleases }
948
+ ),
949
+ primaryAction: /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.create, children: /* @__PURE__ */ jsx(
950
+ Button,
951
+ {
952
+ startIcon: /* @__PURE__ */ jsx(Plus, {}),
953
+ onClick: toggleAddReleaseModal,
954
+ disabled: hasReachedMaximumPendingReleases,
955
+ children: formatMessage({
956
+ id: "content-releases.header.actions.add-release",
957
+ defaultMessage: "New release"
958
+ })
959
+ }
960
+ ) })
961
+ }
962
+ ),
930
963
  /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
964
+ activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsx(
965
+ StyledAlert,
966
+ {
967
+ marginBottom: 6,
968
+ action: /* @__PURE__ */ jsx(Link$2, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
969
+ id: "content-releases.pages.Releases.max-limit-reached.action",
970
+ defaultMessage: "Explore plans"
971
+ }) }),
972
+ title: formatMessage(
973
+ {
974
+ id: "content-releases.pages.Releases.max-limit-reached.title",
975
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
976
+ },
977
+ { number: maximumReleases }
978
+ ),
979
+ onClose: () => {
980
+ },
981
+ closeLabel: "",
982
+ children: formatMessage({
983
+ id: "content-releases.pages.Releases.max-limit-reached.message",
984
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
985
+ })
986
+ }
987
+ ),
931
988
  /* @__PURE__ */ jsxs(
932
989
  TabGroup,
933
990
  {
@@ -1012,4 +1069,4 @@ const App = () => {
1012
1069
  export {
1013
1070
  App
1014
1071
  };
1015
- //# sourceMappingURL=App-L1jSxCiL.mjs.map
1072
+ //# sourceMappingURL=App-8FCxPK8-.mjs.map