@strapi/content-releases 4.19.1 → 4.20.1

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 (29) hide show
  1. package/dist/_chunks/{App-_20W9dYa.js → App-1hHIqUoZ.js} +253 -191
  2. package/dist/_chunks/App-1hHIqUoZ.js.map +1 -0
  3. package/dist/_chunks/{App-L1jSxCiL.mjs → App-U6GbyLIE.mjs} +257 -195
  4. package/dist/_chunks/App-U6GbyLIE.mjs.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-MyLPoISH.mjs → en-GqXgfmzl.mjs} +9 -3
  10. package/dist/_chunks/en-GqXgfmzl.mjs.map +1 -0
  11. package/dist/_chunks/{en-gYDqKYFd.js → en-bDhIlw-B.js} +9 -3
  12. package/dist/_chunks/en-bDhIlw-B.js.map +1 -0
  13. package/dist/_chunks/{index-c4zRX_sg.mjs → index-gkExFBa0.mjs} +89 -26
  14. package/dist/_chunks/index-gkExFBa0.mjs.map +1 -0
  15. package/dist/_chunks/{index-KJa1Rb5F.js → index-l-FvkQlQ.js} +88 -25
  16. package/dist/_chunks/index-l-FvkQlQ.js.map +1 -0
  17. package/dist/admin/index.js +1 -1
  18. package/dist/admin/index.mjs +1 -1
  19. package/dist/server/index.js +193 -34
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +193 -34
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +10 -9
  24. package/dist/_chunks/App-L1jSxCiL.mjs.map +0 -1
  25. package/dist/_chunks/App-_20W9dYa.js.map +0 -1
  26. package/dist/_chunks/en-MyLPoISH.mjs.map +0 -1
  27. package/dist/_chunks/en-gYDqKYFd.js.map +0 -1
  28. package/dist/_chunks/index-KJa1Rb5F.js.map +0 -1
  29. package/dist/_chunks/index-c4zRX_sg.mjs.map +0 -1
@@ -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-gkExFBa0.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,14 @@ 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(),
22
+ timezone: yup.string().when("scheduledAt", {
23
+ is: (scheduledAt) => !!scheduledAt,
24
+ then: yup.string().required(),
25
+ otherwise: yup.string().nullable()
26
+ })
20
27
  }).required().noUnknown();
21
28
  const ReleaseModal = ({
22
29
  handleClose,
@@ -90,10 +97,7 @@ const ReleaseInfoWrapper = styled(Flex)`
90
97
  border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
91
98
  border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
92
99
  `;
93
- const StyledFlex = styled(Flex)`
94
- align-self: stretch;
95
- cursor: ${({ disabled }) => disabled ? "not-allowed" : "pointer"};
96
-
100
+ const StyledMenuItem = styled(Menu.Item)`
97
101
  svg path {
98
102
  fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
99
103
  }
@@ -102,15 +106,15 @@ const StyledFlex = styled(Flex)`
102
106
  }
103
107
  `;
104
108
  const PencilIcon = styled(Pencil)`
105
- width: ${({ theme }) => theme.spaces[4]};
106
- height: ${({ theme }) => theme.spaces[4]};
109
+ width: ${({ theme }) => theme.spaces[3]};
110
+ height: ${({ theme }) => theme.spaces[3]};
107
111
  path {
108
112
  fill: ${({ theme }) => theme.colors.neutral600};
109
113
  }
110
114
  `;
111
115
  const TrashIcon = styled(Trash)`
112
- width: ${({ theme }) => theme.spaces[4]};
113
- height: ${({ theme }) => theme.spaces[4]};
116
+ width: ${({ theme }) => theme.spaces[3]};
117
+ height: ${({ theme }) => theme.spaces[3]};
114
118
  path {
115
119
  fill: ${({ theme }) => theme.colors.danger600};
116
120
  }
@@ -118,24 +122,6 @@ const TrashIcon = styled(Trash)`
118
122
  const TypographyMaxWidth = styled(Typography)`
119
123
  max-width: 300px;
120
124
  `;
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
125
  const EntryValidationText = ({ action, schema, components, entry }) => {
140
126
  const { formatMessage } = useIntl();
141
127
  const { validate } = unstable_useDocument();
@@ -186,8 +172,6 @@ const ReleaseDetailsLayout = ({
186
172
  }) => {
187
173
  const { formatMessage } = useIntl();
188
174
  const { releaseId } = useParams();
189
- const [isPopoverVisible, setIsPopoverVisible] = React.useState(false);
190
- const moreButtonRef = React.useRef(null);
191
175
  const {
192
176
  data,
193
177
  isLoading: isLoadingDetails,
@@ -201,14 +185,8 @@ const ReleaseDetailsLayout = ({
201
185
  allowedActions: { canUpdate, canDelete }
202
186
  } = useRBAC(PERMISSIONS);
203
187
  const dispatch = useTypedDispatch();
188
+ const { trackUsage } = useTracking();
204
189
  const release = data?.data;
205
- const handleTogglePopover = () => {
206
- setIsPopoverVisible((prev) => !prev);
207
- };
208
- const openReleaseModal = () => {
209
- toggleEditReleaseModal();
210
- handleTogglePopover();
211
- };
212
190
  const handlePublishRelease = async () => {
213
191
  const response = await publishRelease({ id: releaseId });
214
192
  if ("data" in response) {
@@ -219,6 +197,12 @@ const ReleaseDetailsLayout = ({
219
197
  defaultMessage: "Release was published successfully."
220
198
  })
221
199
  });
200
+ const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
201
+ trackUsage("didPublishRelease", {
202
+ totalEntries: totalEntries2,
203
+ totalPublishedEntries,
204
+ totalUnpublishedEntries
205
+ });
222
206
  } else if (isAxiosError(response.error)) {
223
207
  toggleNotification({
224
208
  type: "warning",
@@ -231,13 +215,21 @@ const ReleaseDetailsLayout = ({
231
215
  });
232
216
  }
233
217
  };
234
- const openWarningConfirmDialog = () => {
235
- toggleWarningSubmit();
236
- handleTogglePopover();
237
- };
238
218
  const handleRefresh = () => {
239
219
  dispatch(releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
240
220
  };
221
+ const getCreatedByUser = () => {
222
+ if (!release?.createdBy) {
223
+ return null;
224
+ }
225
+ if (release.createdBy.username) {
226
+ return release.createdBy.username;
227
+ }
228
+ if (release.createdBy.firstname) {
229
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
230
+ }
231
+ return release.createdBy.email;
232
+ };
241
233
  if (isLoadingDetails) {
242
234
  return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
243
235
  }
@@ -259,7 +251,7 @@ const ReleaseDetailsLayout = ({
259
251
  );
260
252
  }
261
253
  const totalEntries = release.actions.meta.count || 0;
262
- const createdBy = release.createdBy.lastname ? `${release.createdBy.firstname} ${release.createdBy.lastname}` : `${release.createdBy.firstname}`;
254
+ const hasCreatedByUser = Boolean(getCreatedByUser());
263
255
  return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoadingDetails, children: [
264
256
  /* @__PURE__ */ jsx(
265
257
  HeaderLayout,
@@ -277,72 +269,98 @@ const ReleaseDetailsLayout = ({
277
269
  defaultMessage: "Back"
278
270
  }) }),
279
271
  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
- ),
272
+ /* @__PURE__ */ jsxs(Menu.Root, { children: [
273
+ /* @__PURE__ */ jsx(
274
+ Menu.Trigger,
275
+ {
276
+ as: IconButton,
277
+ paddingLeft: 2,
278
+ paddingRight: 2,
279
+ "aria-label": formatMessage({
280
+ id: "content-releases.header.actions.open-release-actions",
281
+ defaultMessage: "Release edit and delete menu"
282
+ }),
283
+ icon: /* @__PURE__ */ jsx(More, {}),
284
+ variant: "tertiary"
285
+ }
286
+ ),
287
+ /* @__PURE__ */ jsxs(Menu.Content, { top: 1, popoverPlacement: "bottom-end", children: [
288
+ /* @__PURE__ */ jsxs(
289
+ Flex,
290
+ {
291
+ alignItems: "center",
292
+ justifyContent: "center",
293
+ direction: "column",
294
+ padding: 1,
295
+ width: "100%",
296
+ children: [
297
+ /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(
298
+ Flex,
299
+ {
300
+ paddingTop: 2,
301
+ paddingBottom: 2,
302
+ alignItems: "center",
303
+ gap: 2,
304
+ hasRadius: true,
305
+ width: "100%",
306
+ children: [
307
+ /* @__PURE__ */ jsx(PencilIcon, {}),
308
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
309
+ id: "content-releases.header.actions.edit",
310
+ defaultMessage: "Edit"
311
+ }) })
312
+ ]
313
+ }
314
+ ) }),
315
+ /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxs(
316
+ Flex,
317
+ {
318
+ paddingTop: 2,
319
+ paddingBottom: 2,
320
+ alignItems: "center",
321
+ gap: 2,
322
+ hasRadius: true,
323
+ width: "100%",
324
+ children: [
325
+ /* @__PURE__ */ jsx(TrashIcon, {}),
326
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
327
+ id: "content-releases.header.actions.delete",
328
+ defaultMessage: "Delete"
329
+ }) })
330
+ ]
331
+ }
332
+ ) })
333
+ ]
334
+ }
335
+ ),
336
+ /* @__PURE__ */ jsxs(
337
+ ReleaseInfoWrapper,
338
+ {
339
+ direction: "column",
340
+ justifyContent: "center",
341
+ alignItems: "flex-start",
342
+ gap: 1,
343
+ padding: 5,
344
+ children: [
345
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
346
+ id: "content-releases.header.actions.created",
347
+ defaultMessage: "Created"
348
+ }) }),
349
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", color: "neutral300", children: [
350
+ /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(release.createdAt) }),
351
+ formatMessage(
352
+ {
353
+ id: "content-releases.header.actions.created.description",
354
+ defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
355
+ },
356
+ { createdBy: getCreatedByUser(), hasCreatedByUser }
357
+ )
358
+ ] })
359
+ ]
360
+ }
361
+ )
362
+ ] })
363
+ ] }),
346
364
  /* @__PURE__ */ jsx(Button, { size: "S", variant: "tertiary", onClick: handleRefresh, children: formatMessage({
347
365
  id: "content-releases.header.actions.refresh",
348
366
  defaultMessage: "Refresh"
@@ -398,6 +416,9 @@ const ReleaseDetailsBody = () => {
398
416
  isError: isReleaseError,
399
417
  error: releaseError
400
418
  } = useGetReleaseQuery({ id: releaseId });
419
+ const {
420
+ allowedActions: { canUpdate }
421
+ } = useRBAC(PERMISSIONS);
401
422
  const release = releaseData?.data;
402
423
  const selectedGroupBy = query?.groupBy || "contentType";
403
424
  const {
@@ -411,7 +432,7 @@ const ReleaseDetailsBody = () => {
411
432
  releaseId
412
433
  });
413
434
  const [updateReleaseAction] = useUpdateReleaseActionMutation();
414
- const handleChangeType = async (e, actionId) => {
435
+ const handleChangeType = async (e, actionId, actionPath) => {
415
436
  const response = await updateReleaseAction({
416
437
  params: {
417
438
  releaseId,
@@ -419,7 +440,11 @@ const ReleaseDetailsBody = () => {
419
440
  },
420
441
  body: {
421
442
  type: e.target.value
422
- }
443
+ },
444
+ query,
445
+ // We are passing the query params to make optimistic updates
446
+ actionPath
447
+ // We are passing the action path to found the position in the cache of the action for optimistic updates
423
448
  });
424
449
  if ("error" in response) {
425
450
  if (isAxiosError(response.error)) {
@@ -500,7 +525,7 @@ const ReleaseDetailsBody = () => {
500
525
  SingleSelect,
501
526
  {
502
527
  "aria-label": formatMessage({
503
- id: "content-releases.pages.ReleaseDetails.groupBy.label",
528
+ id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
504
529
  defaultMessage: "Group by"
505
530
  }),
506
531
  customizeContent: (value) => formatMessage(
@@ -518,7 +543,7 @@ const ReleaseDetailsBody = () => {
518
543
  }
519
544
  ) }),
520
545
  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 }) }),
546
+ /* @__PURE__ */ jsx(Flex, { role: "separator", "aria-label": key, children: /* @__PURE__ */ jsx(Badge, { children: key }) }),
522
547
  /* @__PURE__ */ jsx(
523
548
  Table.Root,
524
549
  {
@@ -588,56 +613,59 @@ const ReleaseDetailsBody = () => {
588
613
  )
589
614
  ] }),
590
615
  /* @__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,
616
+ /* @__PURE__ */ jsx(Table.Body, { children: releaseActions[key].map(
617
+ ({ id, contentType, locale, type, entry }, actionIndex) => /* @__PURE__ */ jsxs(Tr, { children: [
618
+ /* @__PURE__ */ jsx(Td, { width: "25%", maxWidth: "200px", children: /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: `${contentType.mainFieldValue || entry.id}` }) }),
619
+ /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
620
+ /* @__PURE__ */ jsx(Td, { width: "10%", children: /* @__PURE__ */ jsx(Typography, { children: contentType.displayName || "" }) }),
621
+ /* @__PURE__ */ jsx(Td, { width: "20%", children: release.releasedAt ? /* @__PURE__ */ jsx(Typography, { children: formatMessage(
622
+ {
623
+ id: "content-releases.page.ReleaseDetails.table.action-published",
624
+ defaultMessage: "This entry was <b>{isPublish, select, true {published} other {unpublished}}</b>."
625
+ },
615
626
  {
616
- action: type,
617
- schema: contentTypes?.[contentType.uid],
618
- components,
619
- entry
627
+ isPublish: type === "publish",
628
+ b: (children) => /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", children })
629
+ }
630
+ ) }) : /* @__PURE__ */ jsx(
631
+ ReleaseActionOptions,
632
+ {
633
+ selected: type,
634
+ handleChange: (e) => handleChangeType(e, id, [key, actionIndex]),
635
+ name: `release-action-${id}-type`,
636
+ disabled: !canUpdate
620
637
  }
621
638
  ) }),
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,
639
+ !release.releasedAt && /* @__PURE__ */ jsxs(Fragment, { children: [
640
+ /* @__PURE__ */ jsx(Td, { width: "20%", minWidth: "200px", children: /* @__PURE__ */ jsx(
641
+ EntryValidationText,
633
642
  {
634
- releaseId: release.id,
635
- actionId: id
643
+ action: type,
644
+ schema: contentTypes?.[contentType.uid],
645
+ components,
646
+ entry
636
647
  }
637
- )
638
- ] }) }) })
639
- ] })
640
- ] }, id)) })
648
+ ) }),
649
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxs(ReleaseActionMenu.Root, { children: [
650
+ /* @__PURE__ */ jsx(
651
+ ReleaseActionMenu.ReleaseActionEntryLinkItem,
652
+ {
653
+ contentTypeUid: contentType.uid,
654
+ entryId: entry.id,
655
+ locale: locale?.code
656
+ }
657
+ ),
658
+ /* @__PURE__ */ jsx(
659
+ ReleaseActionMenu.DeleteReleaseActionItem,
660
+ {
661
+ releaseId: release.id,
662
+ actionId: id
663
+ }
664
+ )
665
+ ] }) }) })
666
+ ] })
667
+ ] }, id)
668
+ ) })
641
669
  ] })
642
670
  }
643
671
  )
@@ -762,37 +790,6 @@ const ReleaseDetailsPage = () => {
762
790
  }
763
791
  );
764
792
  };
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
793
  const LinkCard = styled(Link$2)`
797
794
  display: block;
798
795
  `;
@@ -844,6 +841,14 @@ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
844
841
  }
845
842
  ) }) }, id)) });
846
843
  };
844
+ const StyledAlert = styled(Alert)`
845
+ button {
846
+ display: none;
847
+ }
848
+ p + div {
849
+ margin-left: auto;
850
+ }
851
+ `;
847
852
  const INITIAL_FORM_VALUES = {
848
853
  name: ""
849
854
  };
@@ -858,6 +863,9 @@ const ReleasesPage = () => {
858
863
  const [{ query }, setQuery] = useQueryParams();
859
864
  const response = useGetReleasesQuery(query);
860
865
  const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
866
+ const { getFeature } = useLicenseLimits();
867
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
868
+ const { trackUsage } = useTracking();
861
869
  const { isLoading, isSuccess, isError } = response;
862
870
  const activeTab = response?.currentData?.meta?.activeTab || "pending";
863
871
  const activeTabIndex = ["pending", "done"].indexOf(activeTab);
@@ -886,9 +894,10 @@ const ReleasesPage = () => {
886
894
  setReleaseModalShown((prev) => !prev);
887
895
  };
888
896
  if (isLoading) {
889
- return /* @__PURE__ */ jsx(ReleasesLayout, { onClickAddRelease: toggleAddReleaseModal, isLoading: true, children: /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) }) });
897
+ return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
890
898
  }
891
899
  const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
900
+ const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
892
901
  const handleTabChange = (index) => {
893
902
  setQuery({
894
903
  ...query,
@@ -913,6 +922,7 @@ const ReleasesPage = () => {
913
922
  defaultMessage: "Release created."
914
923
  })
915
924
  });
925
+ trackUsage("didCreateRelease");
916
926
  push(`/plugins/content-releases/${response2.data.data.id}`);
917
927
  } else if (isAxiosError(response2.error)) {
918
928
  toggleNotification({
@@ -926,8 +936,60 @@ const ReleasesPage = () => {
926
936
  });
927
937
  }
928
938
  };
929
- return /* @__PURE__ */ jsxs(ReleasesLayout, { onClickAddRelease: toggleAddReleaseModal, totalReleases, children: [
939
+ return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoading, children: [
940
+ /* @__PURE__ */ jsx(
941
+ HeaderLayout,
942
+ {
943
+ title: formatMessage({
944
+ id: "content-releases.pages.Releases.title",
945
+ defaultMessage: "Releases"
946
+ }),
947
+ subtitle: formatMessage(
948
+ {
949
+ id: "content-releases.pages.Releases.header-subtitle",
950
+ defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
951
+ },
952
+ { number: totalReleases }
953
+ ),
954
+ primaryAction: /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.create, children: /* @__PURE__ */ jsx(
955
+ Button,
956
+ {
957
+ startIcon: /* @__PURE__ */ jsx(Plus, {}),
958
+ onClick: toggleAddReleaseModal,
959
+ disabled: hasReachedMaximumPendingReleases,
960
+ children: formatMessage({
961
+ id: "content-releases.header.actions.add-release",
962
+ defaultMessage: "New release"
963
+ })
964
+ }
965
+ ) })
966
+ }
967
+ ),
930
968
  /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
969
+ activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsx(
970
+ StyledAlert,
971
+ {
972
+ marginBottom: 6,
973
+ action: /* @__PURE__ */ jsx(Link$2, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
974
+ id: "content-releases.pages.Releases.max-limit-reached.action",
975
+ defaultMessage: "Explore plans"
976
+ }) }),
977
+ title: formatMessage(
978
+ {
979
+ id: "content-releases.pages.Releases.max-limit-reached.title",
980
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
981
+ },
982
+ { number: maximumReleases }
983
+ ),
984
+ onClose: () => {
985
+ },
986
+ closeLabel: "",
987
+ children: formatMessage({
988
+ id: "content-releases.pages.Releases.max-limit-reached.message",
989
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
990
+ })
991
+ }
992
+ ),
931
993
  /* @__PURE__ */ jsxs(
932
994
  TabGroup,
933
995
  {
@@ -1012,4 +1074,4 @@ const App = () => {
1012
1074
  export {
1013
1075
  App
1014
1076
  };
1015
- //# sourceMappingURL=App-L1jSxCiL.mjs.map
1077
+ //# sourceMappingURL=App-U6GbyLIE.mjs.map