@strapi/content-releases 0.0.0-next.73143c28059b343ba62d98c29672ab114562fbbc → 0.0.0-next.78ea7925e0dad75936ae2e937a041a0666e3d65a

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