@strapi/content-releases 4.20.3 → 4.20.4

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,12 +1,12 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { useNotification, useAPIErrorHandler, LoadingIndicatorPage, ConfirmDialog, useRBAC, useTracking, RelativeTime, CheckPermissions, useQueryParams, AnErrorOccurred, NoContent, Table, PageSizeURLQuery, PaginationURLQuery, CheckPagePermissions } from "@strapi/helper-plugin";
3
- import { useLocation, useParams, useHistory, Redirect, Link as Link$1, Switch, Route } from "react-router-dom";
4
- import { g as getTimezoneOffset, 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, h as ReleaseActionMenu, i as isAxiosError, r as releaseApi, j as useGetReleasesQuery, k as useCreateReleaseMutation } from "./index-rEfNT9PC.mjs";
2
+ import { RelativeTime, useNotification, useAPIErrorHandler, useQueryParams, useTracking, LoadingIndicatorPage, CheckPermissions, PageSizeURLQuery, PaginationURLQuery, AnErrorOccurred, ConfirmDialog, useRBAC, NoContent, Table, CheckPagePermissions } from "@strapi/helper-plugin";
3
+ import { useLocation, useHistory, useParams, Redirect, Link as Link$2, Switch, Route } from "react-router-dom";
4
+ import { g as getTimezoneOffset, p as pluginId, u as useGetReleasesQuery, a as useCreateReleaseMutation, P as PERMISSIONS, i as isAxiosError, b as useGetReleaseQuery, c as useUpdateReleaseMutation, d as useDeleteReleaseMutation, e as usePublishReleaseMutation, f as useTypedDispatch, h as useGetReleaseActionsQuery, j as useUpdateReleaseActionMutation, R as ReleaseActionOptions, k as ReleaseActionMenu, r as releaseApi } from "./index-_eBuegHN.mjs";
5
5
  import * as React from "react";
6
- import { unstable_useDocument, useLicenseLimits } from "@strapi/admin/strapi-admin";
7
- import { ModalLayout, ModalHeader, Typography, ModalBody, Flex, TextInput, Box, Checkbox, DatePicker, TimePicker, ModalFooter, Button, Combobox, ComboboxOption, ContentLayout, Main, HeaderLayout, Link, IconButton, SingleSelect, SingleSelectOption, Badge, Tr, Td, Icon, Tooltip, Alert, TabGroup, 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
- import { Pencil, Trash, ArrowLeft, More, CrossCircle, CheckCircle, Plus, EmptyDocuments } from "@strapi/icons";
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
10
  import format from "date-fns/format";
11
11
  import { zonedTimeToUtc, utcToZonedTime } from "date-fns-tz";
12
12
  import { useIntl } from "react-intl";
@@ -248,6 +248,9 @@ const TimezoneComponent = ({ timezoneOptions }) => {
248
248
  onChange: (timezone) => {
249
249
  setFieldValue("timezone", timezone);
250
250
  },
251
+ onTextValueChange: (timezone) => {
252
+ setFieldValue("timezone", timezone);
253
+ },
251
254
  onClear: () => {
252
255
  setFieldValue("timezone", "");
253
256
  },
@@ -257,122 +260,187 @@ const TimezoneComponent = ({ timezoneOptions }) => {
257
260
  }
258
261
  );
259
262
  };
260
- const ReleaseInfoWrapper = styled(Flex)`
261
- align-self: stretch;
262
- border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
263
- border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
264
- border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
265
- `;
266
- const StyledMenuItem = styled(Menu.Item)`
267
- svg path {
268
- fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
269
- }
270
- span {
271
- color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
272
- }
263
+ const LinkCard = styled(Link)`
264
+ display: block;
273
265
  `;
274
- const PencilIcon = styled(Pencil)`
275
- width: ${({ theme }) => theme.spaces[3]};
276
- height: ${({ theme }) => theme.spaces[3]};
277
- path {
278
- fill: ${({ theme }) => theme.colors.neutral600};
279
- }
266
+ const CapitalizeRelativeTime = styled(RelativeTime)`
267
+ text-transform: capitalize;
280
268
  `;
281
- const TrashIcon = styled(Trash)`
282
- width: ${({ theme }) => theme.spaces[3]};
283
- height: ${({ theme }) => theme.spaces[3]};
284
- path {
285
- fill: ${({ theme }) => theme.colors.danger600};
269
+ const getBadgeProps = (status) => {
270
+ let color;
271
+ switch (status) {
272
+ case "ready":
273
+ color = "success";
274
+ break;
275
+ case "blocked":
276
+ color = "warning";
277
+ break;
278
+ case "failed":
279
+ color = "danger";
280
+ break;
281
+ case "done":
282
+ color = "primary";
283
+ break;
284
+ case "empty":
285
+ default:
286
+ color = "neutral";
286
287
  }
287
- `;
288
- const TypographyMaxWidth = styled(Typography)`
289
- max-width: 300px;
290
- `;
291
- const EntryValidationText = ({ action, schema, components, entry }) => {
288
+ return {
289
+ textColor: `${color}600`,
290
+ backgroundColor: `${color}100`,
291
+ borderColor: `${color}200`
292
+ };
293
+ };
294
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
292
295
  const { formatMessage } = useIntl();
293
- const { validate } = unstable_useDocument();
294
- const { errors } = validate(entry, {
295
- contentType: schema,
296
- components,
297
- isCreatingEntry: false
298
- });
299
- if (Object.keys(errors).length > 0) {
300
- const validationErrorsMessages = Object.entries(errors).map(
301
- ([key, value]) => formatMessage(
302
- { id: `${value.id}.withField`, defaultMessage: value.defaultMessage },
303
- { field: key }
304
- )
305
- ).join(" ");
306
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
307
- /* @__PURE__ */ jsx(Icon, { color: "danger600", as: CrossCircle }),
308
- /* @__PURE__ */ jsx(Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
309
- ] });
296
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
297
+ if (isError) {
298
+ return /* @__PURE__ */ jsx(AnErrorOccurred, {});
310
299
  }
311
- if (action == "publish") {
312
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
313
- /* @__PURE__ */ jsx(Icon, { color: "success600", as: CheckCircle }),
314
- entry.publishedAt ? /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
315
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
316
- defaultMessage: "Already published"
317
- }) }) : /* @__PURE__ */ jsx(Typography, { children: formatMessage({
318
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
319
- defaultMessage: "Ready to publish"
320
- }) })
321
- ] });
300
+ if (releases?.length === 0) {
301
+ return /* @__PURE__ */ jsx(
302
+ EmptyStateLayout,
303
+ {
304
+ content: formatMessage(
305
+ {
306
+ id: "content-releases.page.Releases.tab.emptyEntries",
307
+ defaultMessage: "No releases"
308
+ },
309
+ {
310
+ target: sectionTitle
311
+ }
312
+ ),
313
+ icon: /* @__PURE__ */ jsx(EmptyDocuments, { width: "10rem" })
314
+ }
315
+ );
322
316
  }
323
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
324
- /* @__PURE__ */ jsx(Icon, { color: "success600", as: CheckCircle }),
325
- !entry.publishedAt ? /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
326
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
327
- defaultMessage: "Already unpublished"
328
- }) }) : /* @__PURE__ */ jsx(Typography, { children: formatMessage({
329
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
330
- defaultMessage: "Ready to unpublish"
331
- }) })
332
- ] });
317
+ return /* @__PURE__ */ jsx(Grid, { gap: 4, children: releases.map(({ id, name, actions, scheduledAt, status }) => /* @__PURE__ */ jsx(GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxs(
318
+ Flex,
319
+ {
320
+ direction: "column",
321
+ justifyContent: "space-between",
322
+ padding: 4,
323
+ hasRadius: true,
324
+ background: "neutral0",
325
+ shadow: "tableShadow",
326
+ height: "100%",
327
+ width: "100%",
328
+ alignItems: "start",
329
+ gap: 4,
330
+ children: [
331
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", gap: 1, children: [
332
+ /* @__PURE__ */ jsx(Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
333
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: IsSchedulingEnabled ? scheduledAt ? /* @__PURE__ */ jsx(CapitalizeRelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
334
+ id: "content-releases.pages.Releases.not-scheduled",
335
+ defaultMessage: "Not scheduled"
336
+ }) : formatMessage(
337
+ {
338
+ id: "content-releases.page.Releases.release-item.entries",
339
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
340
+ },
341
+ { number: actions.meta.count }
342
+ ) })
343
+ ] }),
344
+ /* @__PURE__ */ jsx(Badge, { ...getBadgeProps(status), children: status })
345
+ ]
346
+ }
347
+ ) }) }, id)) });
333
348
  };
334
- const ReleaseDetailsLayout = ({
335
- toggleEditReleaseModal,
336
- toggleWarningSubmit,
337
- children
338
- }) => {
339
- const { formatMessage, formatDate, formatTime } = useIntl();
340
- const { releaseId } = useParams();
341
- const {
342
- data,
343
- isLoading: isLoadingDetails,
344
- isError,
345
- error
346
- } = useGetReleaseQuery({ id: releaseId });
347
- const [publishRelease, { isLoading: isPublishing }] = usePublishReleaseMutation();
349
+ const StyledAlert = styled(Alert)`
350
+ button {
351
+ display: none;
352
+ }
353
+ p + div {
354
+ margin-left: auto;
355
+ }
356
+ `;
357
+ const INITIAL_FORM_VALUES = {
358
+ name: "",
359
+ date: null,
360
+ time: "",
361
+ // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
362
+ isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
363
+ scheduledAt: null,
364
+ timezone: null
365
+ };
366
+ const ReleasesPage = () => {
367
+ const tabRef = React.useRef(null);
368
+ const location = useLocation();
369
+ const [releaseModalShown, setReleaseModalShown] = React.useState(false);
348
370
  const toggleNotification = useNotification();
371
+ const { formatMessage } = useIntl();
372
+ const { push, replace } = useHistory();
349
373
  const { formatAPIError } = useAPIErrorHandler();
350
- const {
351
- allowedActions: { canUpdate, canDelete }
352
- } = useRBAC(PERMISSIONS);
353
- const dispatch = useTypedDispatch();
374
+ const [{ query }, setQuery] = useQueryParams();
375
+ const response = useGetReleasesQuery(query);
376
+ const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
377
+ const { getFeature } = useLicenseLimits();
378
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
354
379
  const { trackUsage } = useTracking();
355
- const release = data?.data;
356
- const handlePublishRelease = async () => {
357
- const response = await publishRelease({ id: releaseId });
358
- if ("data" in response) {
380
+ const { isLoading, isSuccess, isError } = response;
381
+ const activeTab = response?.currentData?.meta?.activeTab || "pending";
382
+ const activeTabIndex = ["pending", "done"].indexOf(activeTab);
383
+ React.useEffect(() => {
384
+ if (location?.state?.errors) {
359
385
  toggleNotification({
360
- type: "success",
386
+ type: "warning",
387
+ title: formatMessage({
388
+ id: "content-releases.pages.Releases.notification.error.title",
389
+ defaultMessage: "Your request could not be processed."
390
+ }),
361
391
  message: formatMessage({
362
- id: "content-releases.pages.ReleaseDetails.publish-notification-success",
363
- defaultMessage: "Release was published successfully."
392
+ id: "content-releases.pages.Releases.notification.error.message",
393
+ defaultMessage: "Please try again or open another release."
364
394
  })
365
395
  });
366
- const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
367
- trackUsage("didPublishRelease", {
368
- totalEntries: totalEntries2,
369
- totalPublishedEntries,
370
- totalUnpublishedEntries
396
+ replace({ state: null });
397
+ }
398
+ }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
399
+ React.useEffect(() => {
400
+ if (tabRef.current) {
401
+ tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
402
+ }
403
+ }, [activeTabIndex]);
404
+ const toggleAddReleaseModal = () => {
405
+ setReleaseModalShown((prev) => !prev);
406
+ };
407
+ if (isLoading) {
408
+ return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
409
+ }
410
+ const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
411
+ const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
412
+ const handleTabChange = (index) => {
413
+ setQuery({
414
+ ...query,
415
+ page: 1,
416
+ pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
417
+ filters: {
418
+ releasedAt: {
419
+ $notNull: index === 0 ? false : true
420
+ }
421
+ }
422
+ });
423
+ };
424
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
425
+ const response2 = await createRelease({
426
+ name,
427
+ scheduledAt,
428
+ timezone
429
+ });
430
+ if ("data" in response2) {
431
+ toggleNotification({
432
+ type: "success",
433
+ message: formatMessage({
434
+ id: "content-releases.modal.release-created-notification-success",
435
+ defaultMessage: "Release created."
436
+ })
371
437
  });
372
- } else if (isAxiosError(response.error)) {
438
+ trackUsage("didCreateRelease");
439
+ push(`/plugins/content-releases/${response2.data.data.id}`);
440
+ } else if (isAxiosError(response2.error)) {
373
441
  toggleNotification({
374
442
  type: "warning",
375
- message: formatAPIError(response.error)
443
+ message: formatAPIError(response2.error)
376
444
  });
377
445
  } else {
378
446
  toggleNotification({
@@ -381,34 +449,293 @@ const ReleaseDetailsLayout = ({
381
449
  });
382
450
  }
383
451
  };
384
- const handleRefresh = () => {
385
- dispatch(releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
386
- };
387
- const getCreatedByUser = () => {
388
- if (!release?.createdBy) {
389
- return null;
390
- }
391
- if (release.createdBy.username) {
392
- return release.createdBy.username;
393
- }
394
- if (release.createdBy.firstname) {
395
- return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
396
- }
397
- return release.createdBy.email;
398
- };
399
- if (isLoadingDetails) {
400
- return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
401
- }
402
- if (isError || !release) {
403
- return /* @__PURE__ */ jsx(
404
- Redirect,
452
+ return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoading, children: [
453
+ /* @__PURE__ */ jsx(
454
+ HeaderLayout,
405
455
  {
406
- to: {
407
- pathname: "/plugins/content-releases",
408
- state: {
409
- errors: [
410
- {
411
- code: error?.code
456
+ title: formatMessage({
457
+ id: "content-releases.pages.Releases.title",
458
+ defaultMessage: "Releases"
459
+ }),
460
+ subtitle: formatMessage({
461
+ id: "content-releases.pages.Releases.header-subtitle",
462
+ defaultMessage: "Create and manage content updates"
463
+ }),
464
+ primaryAction: /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.create, children: /* @__PURE__ */ jsx(
465
+ Button,
466
+ {
467
+ startIcon: /* @__PURE__ */ jsx(Plus, {}),
468
+ onClick: toggleAddReleaseModal,
469
+ disabled: hasReachedMaximumPendingReleases,
470
+ children: formatMessage({
471
+ id: "content-releases.header.actions.add-release",
472
+ defaultMessage: "New release"
473
+ })
474
+ }
475
+ ) })
476
+ }
477
+ ),
478
+ /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
479
+ hasReachedMaximumPendingReleases && /* @__PURE__ */ jsx(
480
+ StyledAlert,
481
+ {
482
+ marginBottom: 6,
483
+ action: /* @__PURE__ */ jsx(Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
484
+ id: "content-releases.pages.Releases.max-limit-reached.action",
485
+ defaultMessage: "Explore plans"
486
+ }) }),
487
+ title: formatMessage(
488
+ {
489
+ id: "content-releases.pages.Releases.max-limit-reached.title",
490
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
491
+ },
492
+ { number: maximumReleases }
493
+ ),
494
+ onClose: () => {
495
+ },
496
+ closeLabel: "",
497
+ children: formatMessage({
498
+ id: "content-releases.pages.Releases.max-limit-reached.message",
499
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
500
+ })
501
+ }
502
+ ),
503
+ /* @__PURE__ */ jsxs(
504
+ TabGroup,
505
+ {
506
+ label: formatMessage({
507
+ id: "content-releases.pages.Releases.tab-group.label",
508
+ defaultMessage: "Releases list"
509
+ }),
510
+ variant: "simple",
511
+ initialSelectedTabIndex: activeTabIndex,
512
+ onTabChange: handleTabChange,
513
+ ref: tabRef,
514
+ children: [
515
+ /* @__PURE__ */ jsxs(Box, { paddingBottom: 8, children: [
516
+ /* @__PURE__ */ jsxs(Tabs, { children: [
517
+ /* @__PURE__ */ jsx(Tab, { children: formatMessage(
518
+ {
519
+ id: "content-releases.pages.Releases.tab.pending",
520
+ defaultMessage: "Pending ({count})"
521
+ },
522
+ {
523
+ count: totalPendingReleases
524
+ }
525
+ ) }),
526
+ /* @__PURE__ */ jsx(Tab, { children: formatMessage({
527
+ id: "content-releases.pages.Releases.tab.done",
528
+ defaultMessage: "Done"
529
+ }) })
530
+ ] }),
531
+ /* @__PURE__ */ jsx(Divider, {})
532
+ ] }),
533
+ /* @__PURE__ */ jsxs(TabPanels, { children: [
534
+ /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
535
+ ReleasesGrid,
536
+ {
537
+ sectionTitle: "pending",
538
+ releases: response?.currentData?.data,
539
+ isError
540
+ }
541
+ ) }),
542
+ /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
543
+ ReleasesGrid,
544
+ {
545
+ sectionTitle: "done",
546
+ releases: response?.currentData?.data,
547
+ isError
548
+ }
549
+ ) })
550
+ ] })
551
+ ]
552
+ }
553
+ ),
554
+ response.currentData?.meta?.pagination?.total ? /* @__PURE__ */ jsxs(Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
555
+ /* @__PURE__ */ jsx(
556
+ PageSizeURLQuery,
557
+ {
558
+ options: ["8", "16", "32", "64"],
559
+ defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
560
+ }
561
+ ),
562
+ /* @__PURE__ */ jsx(
563
+ PaginationURLQuery,
564
+ {
565
+ pagination: {
566
+ pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
567
+ }
568
+ }
569
+ )
570
+ ] }) : null
571
+ ] }) }),
572
+ releaseModalShown && /* @__PURE__ */ jsx(
573
+ ReleaseModal,
574
+ {
575
+ handleClose: toggleAddReleaseModal,
576
+ handleSubmit: handleAddRelease,
577
+ isLoading: isSubmittingForm,
578
+ initialValues: INITIAL_FORM_VALUES
579
+ }
580
+ )
581
+ ] });
582
+ };
583
+ const ReleaseInfoWrapper = styled(Flex)`
584
+ align-self: stretch;
585
+ border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
586
+ border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
587
+ border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
588
+ `;
589
+ const StyledMenuItem = styled(Menu.Item)`
590
+ svg path {
591
+ fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
592
+ }
593
+ span {
594
+ color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
595
+ }
596
+
597
+ &:hover {
598
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
599
+ }
600
+ `;
601
+ const PencilIcon = styled(Pencil)`
602
+ width: ${({ theme }) => theme.spaces[3]};
603
+ height: ${({ theme }) => theme.spaces[3]};
604
+ path {
605
+ fill: ${({ theme }) => theme.colors.neutral600};
606
+ }
607
+ `;
608
+ const TrashIcon = styled(Trash)`
609
+ width: ${({ theme }) => theme.spaces[3]};
610
+ height: ${({ theme }) => theme.spaces[3]};
611
+ path {
612
+ fill: ${({ theme }) => theme.colors.danger600};
613
+ }
614
+ `;
615
+ const TypographyMaxWidth = styled(Typography)`
616
+ max-width: 300px;
617
+ `;
618
+ const EntryValidationText = ({ action, schema, components, entry }) => {
619
+ const { formatMessage } = useIntl();
620
+ const { validate } = unstable_useDocument();
621
+ const { errors } = validate(entry, {
622
+ contentType: schema,
623
+ components,
624
+ isCreatingEntry: false
625
+ });
626
+ if (Object.keys(errors).length > 0) {
627
+ const validationErrorsMessages = Object.entries(errors).map(
628
+ ([key, value]) => formatMessage(
629
+ { id: `${value.id}.withField`, defaultMessage: value.defaultMessage },
630
+ { field: key }
631
+ )
632
+ ).join(" ");
633
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
634
+ /* @__PURE__ */ jsx(Icon, { color: "danger600", as: CrossCircle }),
635
+ /* @__PURE__ */ jsx(Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
636
+ ] });
637
+ }
638
+ if (action == "publish") {
639
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
640
+ /* @__PURE__ */ jsx(Icon, { color: "success600", as: CheckCircle }),
641
+ entry.publishedAt ? /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
642
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
643
+ defaultMessage: "Already published"
644
+ }) }) : /* @__PURE__ */ jsx(Typography, { children: formatMessage({
645
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
646
+ defaultMessage: "Ready to publish"
647
+ }) })
648
+ ] });
649
+ }
650
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
651
+ /* @__PURE__ */ jsx(Icon, { color: "success600", as: CheckCircle }),
652
+ !entry.publishedAt ? /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
653
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
654
+ defaultMessage: "Already unpublished"
655
+ }) }) : /* @__PURE__ */ jsx(Typography, { children: formatMessage({
656
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
657
+ defaultMessage: "Ready to unpublish"
658
+ }) })
659
+ ] });
660
+ };
661
+ const ReleaseDetailsLayout = ({
662
+ toggleEditReleaseModal,
663
+ toggleWarningSubmit,
664
+ children
665
+ }) => {
666
+ const { formatMessage, formatDate, formatTime } = useIntl();
667
+ const { releaseId } = useParams();
668
+ const {
669
+ data,
670
+ isLoading: isLoadingDetails,
671
+ isError,
672
+ error
673
+ } = useGetReleaseQuery({ id: releaseId });
674
+ const [publishRelease, { isLoading: isPublishing }] = usePublishReleaseMutation();
675
+ const toggleNotification = useNotification();
676
+ const { formatAPIError } = useAPIErrorHandler();
677
+ const {
678
+ allowedActions: { canUpdate, canDelete }
679
+ } = useRBAC(PERMISSIONS);
680
+ const dispatch = useTypedDispatch();
681
+ const { trackUsage } = useTracking();
682
+ const release = data?.data;
683
+ const handlePublishRelease = async () => {
684
+ const response = await publishRelease({ id: releaseId });
685
+ if ("data" in response) {
686
+ toggleNotification({
687
+ type: "success",
688
+ message: formatMessage({
689
+ id: "content-releases.pages.ReleaseDetails.publish-notification-success",
690
+ defaultMessage: "Release was published successfully."
691
+ })
692
+ });
693
+ const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
694
+ trackUsage("didPublishRelease", {
695
+ totalEntries: totalEntries2,
696
+ totalPublishedEntries,
697
+ totalUnpublishedEntries
698
+ });
699
+ } else if (isAxiosError(response.error)) {
700
+ toggleNotification({
701
+ type: "warning",
702
+ message: formatAPIError(response.error)
703
+ });
704
+ } else {
705
+ toggleNotification({
706
+ type: "warning",
707
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
708
+ });
709
+ }
710
+ };
711
+ const handleRefresh = () => {
712
+ dispatch(releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
713
+ };
714
+ const getCreatedByUser = () => {
715
+ if (!release?.createdBy) {
716
+ return null;
717
+ }
718
+ if (release.createdBy.username) {
719
+ return release.createdBy.username;
720
+ }
721
+ if (release.createdBy.firstname) {
722
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
723
+ }
724
+ return release.createdBy.email;
725
+ };
726
+ if (isLoadingDetails) {
727
+ return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
728
+ }
729
+ if (isError || !release) {
730
+ return /* @__PURE__ */ jsx(
731
+ Redirect,
732
+ {
733
+ to: {
734
+ pathname: "/plugins/content-releases",
735
+ state: {
736
+ errors: [
737
+ {
738
+ code: error?.code
412
739
  }
413
740
  ]
414
741
  }
@@ -452,8 +779,11 @@ const ReleaseDetailsLayout = ({
452
779
  HeaderLayout,
453
780
  {
454
781
  title: release.name,
455
- subtitle: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? ` - ${scheduledText}` : ""),
456
- navigationAction: /* @__PURE__ */ jsx(Link, { startIcon: /* @__PURE__ */ jsx(ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
782
+ subtitle: /* @__PURE__ */ jsxs(Flex, { gap: 2, lineHeight: 6, children: [
783
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", variant: "epsilon", children: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? ` - ${scheduledText}` : "") }),
784
+ /* @__PURE__ */ jsx(Badge, { ...getBadgeProps(release.status), children: release.status })
785
+ ] }),
786
+ navigationAction: /* @__PURE__ */ jsx(Link$1, { startIcon: /* @__PURE__ */ jsx(ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
457
787
  id: "global.back",
458
788
  defaultMessage: "Back"
459
789
  }) }),
@@ -483,42 +813,28 @@ const ReleaseDetailsLayout = ({
483
813
  padding: 1,
484
814
  width: "100%",
485
815
  children: [
486
- /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(
487
- Flex,
488
- {
489
- paddingTop: 2,
490
- paddingBottom: 2,
491
- alignItems: "center",
492
- gap: 2,
493
- hasRadius: true,
494
- width: "100%",
495
- children: [
496
- /* @__PURE__ */ jsx(PencilIcon, {}),
497
- /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
498
- id: "content-releases.header.actions.edit",
499
- defaultMessage: "Edit"
500
- }) })
501
- ]
502
- }
503
- ) }),
504
- /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxs(
505
- Flex,
816
+ /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
817
+ /* @__PURE__ */ jsx(PencilIcon, {}),
818
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
819
+ id: "content-releases.header.actions.edit",
820
+ defaultMessage: "Edit"
821
+ }) })
822
+ ] }) }),
823
+ /* @__PURE__ */ jsx(
824
+ StyledMenuItem,
506
825
  {
507
- paddingTop: 2,
508
- paddingBottom: 2,
509
- alignItems: "center",
510
- gap: 2,
511
- hasRadius: true,
512
- width: "100%",
513
- children: [
826
+ disabled: !canDelete,
827
+ onSelect: toggleWarningSubmit,
828
+ variant: "danger",
829
+ children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
514
830
  /* @__PURE__ */ jsx(TrashIcon, {}),
515
831
  /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
516
832
  id: "content-releases.header.actions.delete",
517
833
  defaultMessage: "Delete"
518
834
  }) })
519
- ]
835
+ ] })
520
836
  }
521
- ) })
837
+ )
522
838
  ]
523
839
  }
524
840
  ),
@@ -694,7 +1010,7 @@ const ReleaseDetailsBody = () => {
694
1010
  action: /* @__PURE__ */ jsx(
695
1011
  LinkButton,
696
1012
  {
697
- as: Link$1,
1013
+ as: Link$2,
698
1014
  to: {
699
1015
  pathname: "/content-manager"
700
1016
  },
@@ -877,272 +1193,55 @@ const ReleaseDetailsPage = () => {
877
1193
  const { releaseId } = useParams();
878
1194
  const toggleNotification = useNotification();
879
1195
  const { formatAPIError } = useAPIErrorHandler();
880
- const { push } = useHistory();
881
- const [releaseModalShown, setReleaseModalShown] = React.useState(false);
882
- const [showWarningSubmit, setWarningSubmit] = React.useState(false);
883
- const {
884
- isLoading: isLoadingDetails,
885
- data,
886
- isSuccess: isSuccessDetails
887
- } = useGetReleaseQuery({ id: releaseId });
888
- const [updateRelease, { isLoading: isSubmittingForm }] = useUpdateReleaseMutation();
889
- const [deleteRelease, { isLoading: isDeletingRelease }] = useDeleteReleaseMutation();
890
- const toggleEditReleaseModal = () => {
891
- setReleaseModalShown((prev) => !prev);
892
- };
893
- const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
894
- if (isLoadingDetails) {
895
- return /* @__PURE__ */ jsx(
896
- ReleaseDetailsLayout,
897
- {
898
- toggleEditReleaseModal,
899
- toggleWarningSubmit,
900
- children: /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) })
901
- }
902
- );
903
- }
904
- const releaseData = isSuccessDetails && data?.data || null;
905
- const title = releaseData?.name || "";
906
- const timezone = releaseData?.timezone ?? null;
907
- const scheduledAt = releaseData?.scheduledAt && timezone ? utcToZonedTime(releaseData.scheduledAt, timezone) : null;
908
- const date = scheduledAt ? new Date(format(scheduledAt, "yyyy-MM-dd")) : null;
909
- const time = scheduledAt ? format(scheduledAt, "HH:mm") : "";
910
- const handleEditRelease = async (values) => {
911
- const response = await updateRelease({
912
- id: releaseId,
913
- name: values.name,
914
- scheduledAt: values.scheduledAt,
915
- timezone: values.timezone
916
- });
917
- if ("data" in response) {
918
- toggleNotification({
919
- type: "success",
920
- message: formatMessage({
921
- id: "content-releases.modal.release-updated-notification-success",
922
- defaultMessage: "Release updated."
923
- })
924
- });
925
- } else if (isAxiosError(response.error)) {
926
- toggleNotification({
927
- type: "warning",
928
- message: formatAPIError(response.error)
929
- });
930
- } else {
931
- toggleNotification({
932
- type: "warning",
933
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
934
- });
935
- }
936
- toggleEditReleaseModal();
937
- };
938
- const handleDeleteRelease = async () => {
939
- const response = await deleteRelease({
940
- id: releaseId
941
- });
942
- if ("data" in response) {
943
- push("/plugins/content-releases");
944
- } else if (isAxiosError(response.error)) {
945
- toggleNotification({
946
- type: "warning",
947
- message: formatAPIError(response.error)
948
- });
949
- } else {
950
- toggleNotification({
951
- type: "warning",
952
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
953
- });
954
- }
955
- };
956
- return /* @__PURE__ */ jsxs(
957
- ReleaseDetailsLayout,
958
- {
959
- toggleEditReleaseModal,
960
- toggleWarningSubmit,
961
- children: [
962
- /* @__PURE__ */ jsx(ReleaseDetailsBody, {}),
963
- releaseModalShown && /* @__PURE__ */ jsx(
964
- ReleaseModal,
965
- {
966
- handleClose: toggleEditReleaseModal,
967
- handleSubmit: handleEditRelease,
968
- isLoading: isLoadingDetails || isSubmittingForm,
969
- initialValues: {
970
- name: title || "",
971
- scheduledAt,
972
- date,
973
- time,
974
- isScheduled: Boolean(scheduledAt),
975
- timezone
976
- }
977
- }
978
- ),
979
- /* @__PURE__ */ jsx(
980
- ConfirmDialog,
981
- {
982
- bodyText: {
983
- id: "content-releases.dialog.confirmation-message",
984
- defaultMessage: "Are you sure you want to delete this release?"
985
- },
986
- isOpen: showWarningSubmit,
987
- isConfirmButtonLoading: isDeletingRelease,
988
- onToggleDialog: toggleWarningSubmit,
989
- onConfirm: handleDeleteRelease
990
- }
991
- )
992
- ]
993
- }
994
- );
995
- };
996
- const LinkCard = styled(Link$2)`
997
- display: block;
998
- `;
999
- const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
1000
- const { formatMessage } = useIntl();
1001
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
1002
- if (isError) {
1003
- return /* @__PURE__ */ jsx(AnErrorOccurred, {});
1004
- }
1005
- if (releases?.length === 0) {
1006
- return /* @__PURE__ */ jsx(
1007
- EmptyStateLayout,
1008
- {
1009
- content: formatMessage(
1010
- {
1011
- id: "content-releases.page.Releases.tab.emptyEntries",
1012
- defaultMessage: "No releases"
1013
- },
1014
- {
1015
- target: sectionTitle
1016
- }
1017
- ),
1018
- icon: /* @__PURE__ */ jsx(EmptyDocuments, { width: "10rem" })
1019
- }
1020
- );
1021
- }
1022
- return /* @__PURE__ */ jsx(Grid, { gap: 4, children: releases.map(({ id, name, actions, scheduledAt }) => /* @__PURE__ */ jsx(GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxs(
1023
- Flex,
1024
- {
1025
- direction: "column",
1026
- justifyContent: "space-between",
1027
- padding: 4,
1028
- hasRadius: true,
1029
- background: "neutral0",
1030
- shadow: "tableShadow",
1031
- height: "100%",
1032
- width: "100%",
1033
- alignItems: "start",
1034
- gap: 2,
1035
- children: [
1036
- /* @__PURE__ */ jsx(Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
1037
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: IsSchedulingEnabled ? scheduledAt ? /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
1038
- id: "content-releases.pages.Releases.not-scheduled",
1039
- defaultMessage: "Not scheduled"
1040
- }) : formatMessage(
1041
- {
1042
- id: "content-releases.page.Releases.release-item.entries",
1043
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
1044
- },
1045
- { number: actions.meta.count }
1046
- ) })
1047
- ]
1048
- }
1049
- ) }) }, id)) });
1050
- };
1051
- const StyledAlert = styled(Alert)`
1052
- button {
1053
- display: none;
1054
- }
1055
- p + div {
1056
- margin-left: auto;
1057
- }
1058
- `;
1059
- const INITIAL_FORM_VALUES = {
1060
- name: "",
1061
- date: null,
1062
- time: "",
1063
- // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
1064
- isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
1065
- scheduledAt: null,
1066
- timezone: null
1067
- };
1068
- const ReleasesPage = () => {
1069
- const tabRef = React.useRef(null);
1070
- const location = useLocation();
1071
- const [releaseModalShown, setReleaseModalShown] = React.useState(false);
1072
- const toggleNotification = useNotification();
1073
- const { formatMessage } = useIntl();
1074
- const { push, replace } = useHistory();
1075
- const { formatAPIError } = useAPIErrorHandler();
1076
- const [{ query }, setQuery] = useQueryParams();
1077
- const response = useGetReleasesQuery(query);
1078
- const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
1079
- const { getFeature } = useLicenseLimits();
1080
- const { maximumReleases = 3 } = getFeature("cms-content-releases");
1081
- const { trackUsage } = useTracking();
1082
- const { isLoading, isSuccess, isError } = response;
1083
- const activeTab = response?.currentData?.meta?.activeTab || "pending";
1084
- const activeTabIndex = ["pending", "done"].indexOf(activeTab);
1085
- React.useEffect(() => {
1086
- if (location?.state?.errors) {
1087
- toggleNotification({
1088
- type: "warning",
1089
- title: formatMessage({
1090
- id: "content-releases.pages.Releases.notification.error.title",
1091
- defaultMessage: "Your request could not be processed."
1092
- }),
1093
- message: formatMessage({
1094
- id: "content-releases.pages.Releases.notification.error.message",
1095
- defaultMessage: "Please try again or open another release."
1096
- })
1097
- });
1098
- replace({ state: null });
1099
- }
1100
- }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
1101
- React.useEffect(() => {
1102
- if (tabRef.current) {
1103
- tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
1104
- }
1105
- }, [activeTabIndex]);
1106
- const toggleAddReleaseModal = () => {
1196
+ const { push } = useHistory();
1197
+ const [releaseModalShown, setReleaseModalShown] = React.useState(false);
1198
+ const [showWarningSubmit, setWarningSubmit] = React.useState(false);
1199
+ const {
1200
+ isLoading: isLoadingDetails,
1201
+ data,
1202
+ isSuccess: isSuccessDetails
1203
+ } = useGetReleaseQuery({ id: releaseId });
1204
+ const [updateRelease, { isLoading: isSubmittingForm }] = useUpdateReleaseMutation();
1205
+ const [deleteRelease, { isLoading: isDeletingRelease }] = useDeleteReleaseMutation();
1206
+ const toggleEditReleaseModal = () => {
1107
1207
  setReleaseModalShown((prev) => !prev);
1108
1208
  };
1109
- if (isLoading) {
1110
- return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
1111
- }
1112
- const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
1113
- const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
1114
- const handleTabChange = (index) => {
1115
- setQuery({
1116
- ...query,
1117
- page: 1,
1118
- pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
1119
- filters: {
1120
- releasedAt: {
1121
- $notNull: index === 0 ? false : true
1122
- }
1209
+ const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
1210
+ if (isLoadingDetails) {
1211
+ return /* @__PURE__ */ jsx(
1212
+ ReleaseDetailsLayout,
1213
+ {
1214
+ toggleEditReleaseModal,
1215
+ toggleWarningSubmit,
1216
+ children: /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) })
1123
1217
  }
1218
+ );
1219
+ }
1220
+ const releaseData = isSuccessDetails && data?.data || null;
1221
+ const title = releaseData?.name || "";
1222
+ const timezone = releaseData?.timezone ?? null;
1223
+ const scheduledAt = releaseData?.scheduledAt && timezone ? utcToZonedTime(releaseData.scheduledAt, timezone) : null;
1224
+ const date = scheduledAt ? new Date(format(scheduledAt, "yyyy-MM-dd")) : null;
1225
+ const time = scheduledAt ? format(scheduledAt, "HH:mm") : "";
1226
+ const handleEditRelease = async (values) => {
1227
+ const response = await updateRelease({
1228
+ id: releaseId,
1229
+ name: values.name,
1230
+ scheduledAt: values.scheduledAt,
1231
+ timezone: values.timezone
1124
1232
  });
1125
- };
1126
- const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
1127
- const response2 = await createRelease({
1128
- name,
1129
- scheduledAt,
1130
- timezone
1131
- });
1132
- if ("data" in response2) {
1233
+ if ("data" in response) {
1133
1234
  toggleNotification({
1134
1235
  type: "success",
1135
1236
  message: formatMessage({
1136
- id: "content-releases.modal.release-created-notification-success",
1137
- defaultMessage: "Release created."
1237
+ id: "content-releases.modal.release-updated-notification-success",
1238
+ defaultMessage: "Release updated."
1138
1239
  })
1139
1240
  });
1140
- trackUsage("didCreateRelease");
1141
- push(`/plugins/content-releases/${response2.data.data.id}`);
1142
- } else if (isAxiosError(response2.error)) {
1241
+ } else if (isAxiosError(response.error)) {
1143
1242
  toggleNotification({
1144
1243
  type: "warning",
1145
- message: formatAPIError(response2.error)
1244
+ message: formatAPIError(response.error)
1146
1245
  });
1147
1246
  } else {
1148
1247
  toggleNotification({
@@ -1150,135 +1249,65 @@ const ReleasesPage = () => {
1150
1249
  message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1151
1250
  });
1152
1251
  }
1252
+ toggleEditReleaseModal();
1153
1253
  };
1154
- return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoading, children: [
1155
- /* @__PURE__ */ jsx(
1156
- HeaderLayout,
1157
- {
1158
- title: formatMessage({
1159
- id: "content-releases.pages.Releases.title",
1160
- defaultMessage: "Releases"
1161
- }),
1162
- subtitle: formatMessage(
1163
- {
1164
- id: "content-releases.pages.Releases.header-subtitle",
1165
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
1166
- },
1167
- { number: totalReleases }
1168
- ),
1169
- primaryAction: /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.create, children: /* @__PURE__ */ jsx(
1170
- Button,
1171
- {
1172
- startIcon: /* @__PURE__ */ jsx(Plus, {}),
1173
- onClick: toggleAddReleaseModal,
1174
- disabled: hasReachedMaximumPendingReleases,
1175
- children: formatMessage({
1176
- id: "content-releases.header.actions.add-release",
1177
- defaultMessage: "New release"
1178
- })
1179
- }
1180
- ) })
1181
- }
1182
- ),
1183
- /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
1184
- activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsx(
1185
- StyledAlert,
1186
- {
1187
- marginBottom: 6,
1188
- action: /* @__PURE__ */ jsx(Link$2, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
1189
- id: "content-releases.pages.Releases.max-limit-reached.action",
1190
- defaultMessage: "Explore plans"
1191
- }) }),
1192
- title: formatMessage(
1193
- {
1194
- id: "content-releases.pages.Releases.max-limit-reached.title",
1195
- defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
1196
- },
1197
- { number: maximumReleases }
1198
- ),
1199
- onClose: () => {
1200
- },
1201
- closeLabel: "",
1202
- children: formatMessage({
1203
- id: "content-releases.pages.Releases.max-limit-reached.message",
1204
- defaultMessage: "Upgrade to manage an unlimited number of releases."
1205
- })
1206
- }
1207
- ),
1208
- /* @__PURE__ */ jsxs(
1209
- TabGroup,
1210
- {
1211
- label: formatMessage({
1212
- id: "content-releases.pages.Releases.tab-group.label",
1213
- defaultMessage: "Releases list"
1214
- }),
1215
- variant: "simple",
1216
- initialSelectedTabIndex: activeTabIndex,
1217
- onTabChange: handleTabChange,
1218
- ref: tabRef,
1219
- children: [
1220
- /* @__PURE__ */ jsxs(Box, { paddingBottom: 8, children: [
1221
- /* @__PURE__ */ jsxs(Tabs, { children: [
1222
- /* @__PURE__ */ jsx(Tab, { children: formatMessage({
1223
- id: "content-releases.pages.Releases.tab.pending",
1224
- defaultMessage: "Pending"
1225
- }) }),
1226
- /* @__PURE__ */ jsx(Tab, { children: formatMessage({
1227
- id: "content-releases.pages.Releases.tab.done",
1228
- defaultMessage: "Done"
1229
- }) })
1230
- ] }),
1231
- /* @__PURE__ */ jsx(Divider, {})
1232
- ] }),
1233
- /* @__PURE__ */ jsxs(TabPanels, { children: [
1234
- /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
1235
- ReleasesGrid,
1236
- {
1237
- sectionTitle: "pending",
1238
- releases: response?.currentData?.data,
1239
- isError
1240
- }
1241
- ) }),
1242
- /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
1243
- ReleasesGrid,
1244
- {
1245
- sectionTitle: "done",
1246
- releases: response?.currentData?.data,
1247
- isError
1248
- }
1249
- ) })
1250
- ] })
1251
- ]
1252
- }
1253
- ),
1254
- totalReleases > 0 && /* @__PURE__ */ jsxs(Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1255
- /* @__PURE__ */ jsx(
1256
- PageSizeURLQuery,
1254
+ const handleDeleteRelease = async () => {
1255
+ const response = await deleteRelease({
1256
+ id: releaseId
1257
+ });
1258
+ if ("data" in response) {
1259
+ push("/plugins/content-releases");
1260
+ } else if (isAxiosError(response.error)) {
1261
+ toggleNotification({
1262
+ type: "warning",
1263
+ message: formatAPIError(response.error)
1264
+ });
1265
+ } else {
1266
+ toggleNotification({
1267
+ type: "warning",
1268
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1269
+ });
1270
+ }
1271
+ };
1272
+ return /* @__PURE__ */ jsxs(
1273
+ ReleaseDetailsLayout,
1274
+ {
1275
+ toggleEditReleaseModal,
1276
+ toggleWarningSubmit,
1277
+ children: [
1278
+ /* @__PURE__ */ jsx(ReleaseDetailsBody, {}),
1279
+ releaseModalShown && /* @__PURE__ */ jsx(
1280
+ ReleaseModal,
1257
1281
  {
1258
- options: ["8", "16", "32", "64"],
1259
- defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
1282
+ handleClose: toggleEditReleaseModal,
1283
+ handleSubmit: handleEditRelease,
1284
+ isLoading: isLoadingDetails || isSubmittingForm,
1285
+ initialValues: {
1286
+ name: title || "",
1287
+ scheduledAt,
1288
+ date,
1289
+ time,
1290
+ isScheduled: Boolean(scheduledAt),
1291
+ timezone
1292
+ }
1260
1293
  }
1261
1294
  ),
1262
1295
  /* @__PURE__ */ jsx(
1263
- PaginationURLQuery,
1296
+ ConfirmDialog,
1264
1297
  {
1265
- pagination: {
1266
- pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
1267
- }
1298
+ bodyText: {
1299
+ id: "content-releases.dialog.confirmation-message",
1300
+ defaultMessage: "Are you sure you want to delete this release?"
1301
+ },
1302
+ isOpen: showWarningSubmit,
1303
+ isConfirmButtonLoading: isDeletingRelease,
1304
+ onToggleDialog: toggleWarningSubmit,
1305
+ onConfirm: handleDeleteRelease
1268
1306
  }
1269
1307
  )
1270
- ] })
1271
- ] }) }),
1272
- releaseModalShown && /* @__PURE__ */ jsx(
1273
- ReleaseModal,
1274
- {
1275
- handleClose: toggleAddReleaseModal,
1276
- handleSubmit: handleAddRelease,
1277
- isLoading: isSubmittingForm,
1278
- initialValues: INITIAL_FORM_VALUES
1279
- }
1280
- )
1281
- ] });
1308
+ ]
1309
+ }
1310
+ );
1282
1311
  };
1283
1312
  const App = () => {
1284
1313
  return /* @__PURE__ */ jsx(CheckPagePermissions, { permissions: PERMISSIONS.main, children: /* @__PURE__ */ jsxs(Switch, { children: [
@@ -1289,4 +1318,4 @@ const App = () => {
1289
1318
  export {
1290
1319
  App
1291
1320
  };
1292
- //# sourceMappingURL=App-hWBsb1nt.mjs.map
1321
+ //# sourceMappingURL=App-6ugQxqYE.mjs.map