@strapi/content-releases 4.20.3 → 4.20.5

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-SDpSekBU.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,137 +268,206 @@ 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
  },
278
+ onTextValueChange: (timezone) => {
279
+ setFieldValue("timezone", timezone);
280
+ },
276
281
  onClear: () => {
277
282
  setFieldValue("timezone", "");
278
283
  },
279
284
  error: errors.timezone,
280
285
  required: true,
281
- 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))
282
287
  }
283
288
  );
284
289
  };
285
- const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
286
- align-self: stretch;
287
- border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
288
- border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
289
- border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
290
- `;
291
- const StyledMenuItem = styled__default.default(v2.Menu.Item)`
292
- svg path {
293
- fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
294
- }
295
- span {
296
- color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
297
- }
290
+ const LinkCard = styled__default.default(v2.Link)`
291
+ display: block;
298
292
  `;
299
- const PencilIcon = styled__default.default(icons.Pencil)`
300
- width: ${({ theme }) => theme.spaces[3]};
301
- height: ${({ theme }) => theme.spaces[3]};
302
- path {
303
- fill: ${({ theme }) => theme.colors.neutral600};
304
- }
293
+ const CapitalizeRelativeTime = styled__default.default(helperPlugin.RelativeTime)`
294
+ text-transform: capitalize;
305
295
  `;
306
- const TrashIcon = styled__default.default(icons.Trash)`
307
- width: ${({ theme }) => theme.spaces[3]};
308
- height: ${({ theme }) => theme.spaces[3]};
309
- path {
310
- fill: ${({ theme }) => theme.colors.danger600};
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";
311
314
  }
312
- `;
313
- const TypographyMaxWidth = styled__default.default(designSystem.Typography)`
314
- max-width: 300px;
315
- `;
316
- const EntryValidationText = ({ action, schema, components, entry }) => {
315
+ return {
316
+ textColor: `${color}600`,
317
+ backgroundColor: `${color}100`,
318
+ borderColor: `${color}200`
319
+ };
320
+ };
321
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
317
322
  const { formatMessage } = reactIntl.useIntl();
318
- const { validate } = strapiAdmin.unstable_useDocument();
319
- const { errors } = validate(entry, {
320
- contentType: schema,
321
- components,
322
- isCreatingEntry: false
323
- });
324
- if (Object.keys(errors).length > 0) {
325
- const validationErrorsMessages = Object.entries(errors).map(
326
- ([key, value]) => formatMessage(
327
- { id: `${value.id}.withField`, defaultMessage: value.defaultMessage },
328
- { field: key }
329
- )
330
- ).join(" ");
331
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
332
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "danger600", as: icons.CrossCircle }),
333
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsxRuntime.jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
334
- ] });
323
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
324
+ if (isError) {
325
+ return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
335
326
  }
336
- if (action == "publish") {
337
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
338
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
339
- entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
340
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
341
- defaultMessage: "Already published"
342
- }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
343
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
344
- defaultMessage: "Ready to publish"
345
- }) })
346
- ] });
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
+ );
347
343
  }
348
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
349
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
350
- !entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
351
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
352
- defaultMessage: "Already unpublished"
353
- }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
354
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
355
- defaultMessage: "Ready to unpublish"
356
- }) })
357
- ] });
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)) });
358
375
  };
359
- const ReleaseDetailsLayout = ({
360
- toggleEditReleaseModal,
361
- toggleWarningSubmit,
362
- children
363
- }) => {
364
- const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
365
- const { releaseId } = reactRouterDom.useParams();
366
- const {
367
- data,
368
- isLoading: isLoadingDetails,
369
- isError,
370
- error
371
- } = index.useGetReleaseQuery({ id: releaseId });
372
- const [publishRelease, { isLoading: isPublishing }] = index.usePublishReleaseMutation();
376
+ const StyledAlert = styled__default.default(designSystem.Alert)`
377
+ button {
378
+ display: none;
379
+ }
380
+ p + div {
381
+ margin-left: auto;
382
+ }
383
+ `;
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
392
+ };
393
+ const ReleasesPage = () => {
394
+ const tabRef = React__namespace.useRef(null);
395
+ const location = reactRouterDom.useLocation();
396
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
373
397
  const toggleNotification = helperPlugin.useNotification();
398
+ const { formatMessage } = reactIntl.useIntl();
399
+ const { push, replace } = reactRouterDom.useHistory();
374
400
  const { formatAPIError } = helperPlugin.useAPIErrorHandler();
375
- const {
376
- allowedActions: { canUpdate, canDelete }
377
- } = helperPlugin.useRBAC(index.PERMISSIONS);
378
- 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");
379
406
  const { trackUsage } = helperPlugin.useTracking();
380
- const release = data?.data;
381
- const handlePublishRelease = async () => {
382
- const response = await publishRelease({ id: releaseId });
383
- 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) {
384
412
  toggleNotification({
385
- 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
+ }),
386
418
  message: formatMessage({
387
- id: "content-releases.pages.ReleaseDetails.publish-notification-success",
388
- defaultMessage: "Release was published successfully."
419
+ id: "content-releases.pages.Releases.notification.error.message",
420
+ defaultMessage: "Please try again or open another release."
389
421
  })
390
422
  });
391
- const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
392
- trackUsage("didPublishRelease", {
393
- totalEntries: totalEntries2,
394
- totalPublishedEntries,
395
- 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
+ })
396
464
  });
397
- } 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)) {
398
468
  toggleNotification({
399
469
  type: "warning",
400
- message: formatAPIError(response.error)
470
+ message: formatAPIError(response2.error)
401
471
  });
402
472
  } else {
403
473
  toggleNotification({
@@ -406,32 +476,291 @@ const ReleaseDetailsLayout = ({
406
476
  });
407
477
  }
408
478
  };
409
- const handleRefresh = () => {
410
- dispatch(index.releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
411
- };
412
- const getCreatedByUser = () => {
413
- if (!release?.createdBy) {
414
- return null;
415
- }
416
- if (release.createdBy.username) {
417
- return release.createdBy.username;
418
- }
419
- if (release.createdBy.firstname) {
420
- return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
421
- }
422
- return release.createdBy.email;
423
- };
424
- if (isLoadingDetails) {
425
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
426
- }
427
- if (isError || !release) {
428
- return /* @__PURE__ */ jsxRuntime.jsx(
429
- reactRouterDom.Redirect,
479
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
480
+ /* @__PURE__ */ jsxRuntime.jsx(
481
+ designSystem.HeaderLayout,
430
482
  {
431
- to: {
432
- pathname: "/plugins/content-releases",
433
- state: {
434
- errors: [
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",
762
+ state: {
763
+ errors: [
435
764
  {
436
765
  code: error?.code
437
766
  }
@@ -477,7 +806,10 @@ const ReleaseDetailsLayout = ({
477
806
  designSystem.HeaderLayout,
478
807
  {
479
808
  title: release.name,
480
- 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
+ ] }),
481
813
  navigationAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
482
814
  id: "global.back",
483
815
  defaultMessage: "Back"
@@ -508,42 +840,28 @@ const ReleaseDetailsLayout = ({
508
840
  padding: 1,
509
841
  width: "100%",
510
842
  children: [
511
- /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(
512
- designSystem.Flex,
513
- {
514
- paddingTop: 2,
515
- paddingBottom: 2,
516
- alignItems: "center",
517
- gap: 2,
518
- hasRadius: true,
519
- width: "100%",
520
- children: [
521
- /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
522
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
523
- id: "content-releases.header.actions.edit",
524
- defaultMessage: "Edit"
525
- }) })
526
- ]
527
- }
528
- ) }),
529
- /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxRuntime.jsxs(
530
- designSystem.Flex,
843
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
844
+ /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
845
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
846
+ id: "content-releases.header.actions.edit",
847
+ defaultMessage: "Edit"
848
+ }) })
849
+ ] }) }),
850
+ /* @__PURE__ */ jsxRuntime.jsx(
851
+ StyledMenuItem,
531
852
  {
532
- paddingTop: 2,
533
- paddingBottom: 2,
534
- alignItems: "center",
535
- gap: 2,
536
- hasRadius: true,
537
- width: "100%",
538
- children: [
853
+ disabled: !canDelete,
854
+ onSelect: toggleWarningSubmit,
855
+ variant: "danger",
856
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
539
857
  /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
540
858
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
541
859
  id: "content-releases.header.actions.delete",
542
860
  defaultMessage: "Delete"
543
861
  }) })
544
- ]
862
+ ] })
545
863
  }
546
- ) })
864
+ )
547
865
  ]
548
866
  }
549
867
  ),
@@ -900,274 +1218,57 @@ const ReleaseDetailsBody = () => {
900
1218
  const ReleaseDetailsPage = () => {
901
1219
  const { formatMessage } = reactIntl.useIntl();
902
1220
  const { releaseId } = reactRouterDom.useParams();
903
- const toggleNotification = helperPlugin.useNotification();
904
- const { formatAPIError } = helperPlugin.useAPIErrorHandler();
905
- const { push } = reactRouterDom.useHistory();
906
- const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
907
- const [showWarningSubmit, setWarningSubmit] = React__namespace.useState(false);
908
- const {
909
- isLoading: isLoadingDetails,
910
- data,
911
- isSuccess: isSuccessDetails
912
- } = index.useGetReleaseQuery({ id: releaseId });
913
- const [updateRelease, { isLoading: isSubmittingForm }] = index.useUpdateReleaseMutation();
914
- const [deleteRelease, { isLoading: isDeletingRelease }] = index.useDeleteReleaseMutation();
915
- const toggleEditReleaseModal = () => {
916
- setReleaseModalShown((prev) => !prev);
917
- };
918
- const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
919
- if (isLoadingDetails) {
920
- return /* @__PURE__ */ jsxRuntime.jsx(
921
- ReleaseDetailsLayout,
922
- {
923
- toggleEditReleaseModal,
924
- toggleWarningSubmit,
925
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) })
926
- }
927
- );
928
- }
929
- const releaseData = isSuccessDetails && data?.data || null;
930
- const title = releaseData?.name || "";
931
- const timezone = releaseData?.timezone ?? null;
932
- const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
933
- const date = scheduledAt ? new Date(format__default.default(scheduledAt, "yyyy-MM-dd")) : null;
934
- const time = scheduledAt ? format__default.default(scheduledAt, "HH:mm") : "";
935
- const handleEditRelease = async (values) => {
936
- const response = await updateRelease({
937
- id: releaseId,
938
- name: values.name,
939
- scheduledAt: values.scheduledAt,
940
- timezone: values.timezone
941
- });
942
- if ("data" in response) {
943
- toggleNotification({
944
- type: "success",
945
- message: formatMessage({
946
- id: "content-releases.modal.release-updated-notification-success",
947
- defaultMessage: "Release updated."
948
- })
949
- });
950
- } else if (index.isAxiosError(response.error)) {
951
- toggleNotification({
952
- type: "warning",
953
- message: formatAPIError(response.error)
954
- });
955
- } else {
956
- toggleNotification({
957
- type: "warning",
958
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
959
- });
960
- }
961
- toggleEditReleaseModal();
962
- };
963
- const handleDeleteRelease = async () => {
964
- const response = await deleteRelease({
965
- id: releaseId
966
- });
967
- if ("data" in response) {
968
- push("/plugins/content-releases");
969
- } else if (index.isAxiosError(response.error)) {
970
- toggleNotification({
971
- type: "warning",
972
- message: formatAPIError(response.error)
973
- });
974
- } else {
975
- toggleNotification({
976
- type: "warning",
977
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
978
- });
979
- }
980
- };
981
- return /* @__PURE__ */ jsxRuntime.jsxs(
982
- ReleaseDetailsLayout,
983
- {
984
- toggleEditReleaseModal,
985
- toggleWarningSubmit,
986
- children: [
987
- /* @__PURE__ */ jsxRuntime.jsx(ReleaseDetailsBody, {}),
988
- releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
989
- ReleaseModal,
990
- {
991
- handleClose: toggleEditReleaseModal,
992
- handleSubmit: handleEditRelease,
993
- isLoading: isLoadingDetails || isSubmittingForm,
994
- initialValues: {
995
- name: title || "",
996
- scheduledAt,
997
- date,
998
- time,
999
- isScheduled: Boolean(scheduledAt),
1000
- timezone
1001
- }
1002
- }
1003
- ),
1004
- /* @__PURE__ */ jsxRuntime.jsx(
1005
- helperPlugin.ConfirmDialog,
1006
- {
1007
- bodyText: {
1008
- id: "content-releases.dialog.confirmation-message",
1009
- defaultMessage: "Are you sure you want to delete this release?"
1010
- },
1011
- isOpen: showWarningSubmit,
1012
- isConfirmButtonLoading: isDeletingRelease,
1013
- onToggleDialog: toggleWarningSubmit,
1014
- onConfirm: handleDeleteRelease
1015
- }
1016
- )
1017
- ]
1018
- }
1019
- );
1020
- };
1021
- const LinkCard = styled__default.default(v2.Link)`
1022
- display: block;
1023
- `;
1024
- const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
1025
- const { formatMessage } = reactIntl.useIntl();
1026
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
1027
- if (isError) {
1028
- return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
1029
- }
1030
- if (releases?.length === 0) {
1031
- return /* @__PURE__ */ jsxRuntime.jsx(
1032
- designSystem.EmptyStateLayout,
1033
- {
1034
- content: formatMessage(
1035
- {
1036
- id: "content-releases.page.Releases.tab.emptyEntries",
1037
- defaultMessage: "No releases"
1038
- },
1039
- {
1040
- target: sectionTitle
1041
- }
1042
- ),
1043
- icon: /* @__PURE__ */ jsxRuntime.jsx(icons.EmptyDocuments, { width: "10rem" })
1044
- }
1045
- );
1046
- }
1047
- 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(
1048
- designSystem.Flex,
1049
- {
1050
- direction: "column",
1051
- justifyContent: "space-between",
1052
- padding: 4,
1053
- hasRadius: true,
1054
- background: "neutral0",
1055
- shadow: "tableShadow",
1056
- height: "100%",
1057
- width: "100%",
1058
- alignItems: "start",
1059
- gap: 2,
1060
- children: [
1061
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
1062
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: IsSchedulingEnabled ? scheduledAt ? /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
1063
- id: "content-releases.pages.Releases.not-scheduled",
1064
- defaultMessage: "Not scheduled"
1065
- }) : formatMessage(
1066
- {
1067
- id: "content-releases.page.Releases.release-item.entries",
1068
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
1069
- },
1070
- { number: actions.meta.count }
1071
- ) })
1072
- ]
1073
- }
1074
- ) }) }, id)) });
1075
- };
1076
- const StyledAlert = styled__default.default(designSystem.Alert)`
1077
- button {
1078
- display: none;
1079
- }
1080
- p + div {
1081
- margin-left: auto;
1082
- }
1083
- `;
1084
- const INITIAL_FORM_VALUES = {
1085
- name: "",
1086
- date: null,
1087
- time: "",
1088
- // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
1089
- isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
1090
- scheduledAt: null,
1091
- timezone: null
1092
- };
1093
- const ReleasesPage = () => {
1094
- const tabRef = React__namespace.useRef(null);
1095
- const location = reactRouterDom.useLocation();
1096
- const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
1097
- const toggleNotification = helperPlugin.useNotification();
1098
- const { formatMessage } = reactIntl.useIntl();
1099
- const { push, replace } = reactRouterDom.useHistory();
1221
+ const toggleNotification = helperPlugin.useNotification();
1100
1222
  const { formatAPIError } = helperPlugin.useAPIErrorHandler();
1101
- const [{ query }, setQuery] = helperPlugin.useQueryParams();
1102
- const response = index.useGetReleasesQuery(query);
1103
- const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
1104
- const { getFeature } = strapiAdmin.useLicenseLimits();
1105
- const { maximumReleases = 3 } = getFeature("cms-content-releases");
1106
- const { trackUsage } = helperPlugin.useTracking();
1107
- const { isLoading, isSuccess, isError } = response;
1108
- const activeTab = response?.currentData?.meta?.activeTab || "pending";
1109
- const activeTabIndex = ["pending", "done"].indexOf(activeTab);
1110
- React__namespace.useEffect(() => {
1111
- if (location?.state?.errors) {
1112
- toggleNotification({
1113
- type: "warning",
1114
- title: formatMessage({
1115
- id: "content-releases.pages.Releases.notification.error.title",
1116
- defaultMessage: "Your request could not be processed."
1117
- }),
1118
- message: formatMessage({
1119
- id: "content-releases.pages.Releases.notification.error.message",
1120
- defaultMessage: "Please try again or open another release."
1121
- })
1122
- });
1123
- replace({ state: null });
1124
- }
1125
- }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
1126
- React__namespace.useEffect(() => {
1127
- if (tabRef.current) {
1128
- tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
1129
- }
1130
- }, [activeTabIndex]);
1131
- const toggleAddReleaseModal = () => {
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 = () => {
1132
1234
  setReleaseModalShown((prev) => !prev);
1133
1235
  };
1134
- if (isLoading) {
1135
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
1136
- }
1137
- const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
1138
- const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
1139
- const handleTabChange = (index2) => {
1140
- setQuery({
1141
- ...query,
1142
- page: 1,
1143
- pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
1144
- filters: {
1145
- releasedAt: {
1146
- $notNull: index2 === 0 ? false : true
1147
- }
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, {}) })
1148
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
1149
1259
  });
1150
- };
1151
- const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
1152
- const response2 = await createRelease({
1153
- name,
1154
- scheduledAt,
1155
- timezone
1156
- });
1157
- if ("data" in response2) {
1260
+ if ("data" in response) {
1158
1261
  toggleNotification({
1159
1262
  type: "success",
1160
1263
  message: formatMessage({
1161
- id: "content-releases.modal.release-created-notification-success",
1162
- defaultMessage: "Release created."
1264
+ id: "content-releases.modal.release-updated-notification-success",
1265
+ defaultMessage: "Release updated."
1163
1266
  })
1164
1267
  });
1165
- trackUsage("didCreateRelease");
1166
- push(`/plugins/content-releases/${response2.data.data.id}`);
1167
- } else if (index.isAxiosError(response2.error)) {
1268
+ } else if (index.isAxiosError(response.error)) {
1168
1269
  toggleNotification({
1169
1270
  type: "warning",
1170
- message: formatAPIError(response2.error)
1271
+ message: formatAPIError(response.error)
1171
1272
  });
1172
1273
  } else {
1173
1274
  toggleNotification({
@@ -1175,135 +1276,65 @@ const ReleasesPage = () => {
1175
1276
  message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1176
1277
  });
1177
1278
  }
1279
+ toggleEditReleaseModal();
1178
1280
  };
1179
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
1180
- /* @__PURE__ */ jsxRuntime.jsx(
1181
- designSystem.HeaderLayout,
1182
- {
1183
- title: formatMessage({
1184
- id: "content-releases.pages.Releases.title",
1185
- defaultMessage: "Releases"
1186
- }),
1187
- subtitle: formatMessage(
1188
- {
1189
- id: "content-releases.pages.Releases.header-subtitle",
1190
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
1191
- },
1192
- { number: totalReleases }
1193
- ),
1194
- primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
1195
- designSystem.Button,
1196
- {
1197
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1198
- onClick: toggleAddReleaseModal,
1199
- disabled: hasReachedMaximumPendingReleases,
1200
- children: formatMessage({
1201
- id: "content-releases.header.actions.add-release",
1202
- defaultMessage: "New release"
1203
- })
1204
- }
1205
- ) })
1206
- }
1207
- ),
1208
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1209
- activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
1210
- StyledAlert,
1211
- {
1212
- marginBottom: 6,
1213
- action: /* @__PURE__ */ jsxRuntime.jsx(v2.Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
1214
- id: "content-releases.pages.Releases.max-limit-reached.action",
1215
- defaultMessage: "Explore plans"
1216
- }) }),
1217
- title: formatMessage(
1218
- {
1219
- id: "content-releases.pages.Releases.max-limit-reached.title",
1220
- defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
1221
- },
1222
- { number: maximumReleases }
1223
- ),
1224
- onClose: () => {
1225
- },
1226
- closeLabel: "",
1227
- children: formatMessage({
1228
- id: "content-releases.pages.Releases.max-limit-reached.message",
1229
- defaultMessage: "Upgrade to manage an unlimited number of releases."
1230
- })
1231
- }
1232
- ),
1233
- /* @__PURE__ */ jsxRuntime.jsxs(
1234
- designSystem.TabGroup,
1235
- {
1236
- label: formatMessage({
1237
- id: "content-releases.pages.Releases.tab-group.label",
1238
- defaultMessage: "Releases list"
1239
- }),
1240
- variant: "simple",
1241
- initialSelectedTabIndex: activeTabIndex,
1242
- onTabChange: handleTabChange,
1243
- ref: tabRef,
1244
- children: [
1245
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingBottom: 8, children: [
1246
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs, { children: [
1247
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1248
- id: "content-releases.pages.Releases.tab.pending",
1249
- defaultMessage: "Pending"
1250
- }) }),
1251
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1252
- id: "content-releases.pages.Releases.tab.done",
1253
- defaultMessage: "Done"
1254
- }) })
1255
- ] }),
1256
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {})
1257
- ] }),
1258
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.TabPanels, { children: [
1259
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
1260
- ReleasesGrid,
1261
- {
1262
- sectionTitle: "pending",
1263
- releases: response?.currentData?.data,
1264
- isError
1265
- }
1266
- ) }),
1267
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
1268
- ReleasesGrid,
1269
- {
1270
- sectionTitle: "done",
1271
- releases: response?.currentData?.data,
1272
- isError
1273
- }
1274
- ) })
1275
- ] })
1276
- ]
1277
- }
1278
- ),
1279
- totalReleases > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1280
- /* @__PURE__ */ jsxRuntime.jsx(
1281
- 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,
1282
1308
  {
1283
- options: ["8", "16", "32", "64"],
1284
- 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
+ }
1285
1320
  }
1286
1321
  ),
1287
1322
  /* @__PURE__ */ jsxRuntime.jsx(
1288
- helperPlugin.PaginationURLQuery,
1323
+ helperPlugin.ConfirmDialog,
1289
1324
  {
1290
- pagination: {
1291
- pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
1292
- }
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
1293
1333
  }
1294
1334
  )
1295
- ] })
1296
- ] }) }),
1297
- releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
1298
- ReleaseModal,
1299
- {
1300
- handleClose: toggleAddReleaseModal,
1301
- handleSubmit: handleAddRelease,
1302
- isLoading: isSubmittingForm,
1303
- initialValues: INITIAL_FORM_VALUES
1304
- }
1305
- )
1306
- ] });
1335
+ ]
1336
+ }
1337
+ );
1307
1338
  };
1308
1339
  const App = () => {
1309
1340
  return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPagePermissions, { permissions: index.PERMISSIONS.main, children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Switch, { children: [
@@ -1312,4 +1343,4 @@ const App = () => {
1312
1343
  ] }) });
1313
1344
  };
1314
1345
  exports.App = App;
1315
- //# sourceMappingURL=App-C_iroyIu.js.map
1346
+ //# sourceMappingURL=App-p8aKBitd.js.map