@strapi/content-releases 0.0.0-next.e1ede8c55a0e1e22ce20137bf238fc374bd5dd51 → 0.0.0-next.f4ff842a3cb7b83db540bee67554b704e042b042

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/_chunks/App-dLXY5ei3.js +1353 -0
  2. package/dist/_chunks/App-dLXY5ei3.js.map +1 -0
  3. package/dist/_chunks/App-jrh58sXY.mjs +1330 -0
  4. package/dist/_chunks/App-jrh58sXY.mjs.map +1 -0
  5. package/dist/_chunks/PurchaseContentReleases-3tRbmbY3.mjs +51 -0
  6. package/dist/_chunks/PurchaseContentReleases-3tRbmbY3.mjs.map +1 -0
  7. package/dist/_chunks/PurchaseContentReleases-bpIYXOfu.js +51 -0
  8. package/dist/_chunks/PurchaseContentReleases-bpIYXOfu.js.map +1 -0
  9. package/dist/_chunks/{en-haKSQIo8.js → en-HrREghh3.js} +31 -7
  10. package/dist/_chunks/en-HrREghh3.js.map +1 -0
  11. package/dist/_chunks/{en-ngTk74JV.mjs → en-ltT1TlKQ.mjs} +31 -7
  12. package/dist/_chunks/en-ltT1TlKQ.mjs.map +1 -0
  13. package/dist/_chunks/{index-EdBmRHRU.js → index-CVO0Rqdm.js} +551 -64
  14. package/dist/_chunks/index-CVO0Rqdm.js.map +1 -0
  15. package/dist/_chunks/{index-XAQOX_IB.mjs → index-PiOGBETy.mjs} +570 -83
  16. package/dist/_chunks/index-PiOGBETy.mjs.map +1 -0
  17. package/dist/admin/index.js +2 -1
  18. package/dist/admin/index.js.map +1 -1
  19. package/dist/admin/index.mjs +3 -2
  20. package/dist/admin/index.mjs.map +1 -1
  21. package/dist/server/index.js +1192 -299
  22. package/dist/server/index.js.map +1 -1
  23. package/dist/server/index.mjs +1191 -300
  24. package/dist/server/index.mjs.map +1 -1
  25. package/package.json +16 -12
  26. package/dist/_chunks/App-g2P5kbSm.mjs +0 -945
  27. package/dist/_chunks/App-g2P5kbSm.mjs.map +0 -1
  28. package/dist/_chunks/App-o5_WfqR-.js +0 -967
  29. package/dist/_chunks/App-o5_WfqR-.js.map +0 -1
  30. package/dist/_chunks/en-haKSQIo8.js.map +0 -1
  31. package/dist/_chunks/en-ngTk74JV.mjs.map +0 -1
  32. package/dist/_chunks/index-EdBmRHRU.js.map +0 -1
  33. package/dist/_chunks/index-XAQOX_IB.mjs.map +0 -1
@@ -13,6 +13,7 @@ const reactRouterDom = require("react-router-dom");
13
13
  const yup = require("yup");
14
14
  const react = require("@reduxjs/toolkit/query/react");
15
15
  const styled = require("styled-components");
16
+ const reactRedux = require("react-redux");
16
17
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
17
18
  function _interopNamespace(e) {
18
19
  if (e && e.__esModule)
@@ -158,7 +159,7 @@ const isAxiosError = (err) => {
158
159
  const releaseApi = react.createApi({
159
160
  reducerPath: pluginId,
160
161
  baseQuery: axiosBaseQuery,
161
- tagTypes: ["Release", "ReleaseAction"],
162
+ tagTypes: ["Release", "ReleaseAction", "EntriesInRelease"],
162
163
  endpoints: (build) => {
163
164
  return {
164
165
  getReleasesForEntry: build.query({
@@ -273,6 +274,20 @@ const releaseApi = react.createApi({
273
274
  { type: "ReleaseAction", id: "LIST" }
274
275
  ]
275
276
  }),
277
+ createManyReleaseActions: build.mutation({
278
+ query({ body, params }) {
279
+ return {
280
+ url: `/content-releases/${params.releaseId}/actions/bulk`,
281
+ method: "POST",
282
+ data: body
283
+ };
284
+ },
285
+ invalidatesTags: [
286
+ { type: "Release", id: "LIST" },
287
+ { type: "ReleaseAction", id: "LIST" },
288
+ { type: "EntriesInRelease" }
289
+ ]
290
+ }),
276
291
  updateReleaseAction: build.mutation({
277
292
  query({ body, params }) {
278
293
  return {
@@ -281,7 +296,27 @@ const releaseApi = react.createApi({
281
296
  data: body
282
297
  };
283
298
  },
284
- invalidatesTags: () => [{ type: "ReleaseAction", id: "LIST" }]
299
+ invalidatesTags: () => [{ type: "ReleaseAction", id: "LIST" }],
300
+ async onQueryStarted({ body, params, query: query2, actionPath }, { dispatch, queryFulfilled }) {
301
+ const paramsWithoutActionId = {
302
+ releaseId: params.releaseId,
303
+ ...query2
304
+ };
305
+ const patchResult = dispatch(
306
+ releaseApi.util.updateQueryData("getReleaseActions", paramsWithoutActionId, (draft) => {
307
+ const [key, index] = actionPath;
308
+ const action = draft.data[key][index];
309
+ if (action) {
310
+ action.type = body.type;
311
+ }
312
+ })
313
+ );
314
+ try {
315
+ await queryFulfilled;
316
+ } catch {
317
+ patchResult.undo();
318
+ }
319
+ }
285
320
  }),
286
321
  deleteReleaseAction: build.mutation({
287
322
  query({ params }) {
@@ -290,9 +325,11 @@ const releaseApi = react.createApi({
290
325
  method: "DELETE"
291
326
  };
292
327
  },
293
- invalidatesTags: [
328
+ invalidatesTags: (result, error, arg) => [
294
329
  { type: "Release", id: "LIST" },
295
- { type: "ReleaseAction", id: "LIST" }
330
+ { type: "Release", id: arg.params.releaseId },
331
+ { type: "ReleaseAction", id: "LIST" },
332
+ { type: "EntriesInRelease" }
296
333
  ]
297
334
  }),
298
335
  publishRelease: build.mutation({
@@ -311,7 +348,22 @@ const releaseApi = react.createApi({
311
348
  method: "DELETE"
312
349
  };
313
350
  },
314
- invalidatesTags: (result, error, arg) => [{ type: "Release", id: arg.id }]
351
+ invalidatesTags: () => [{ type: "Release", id: "LIST" }, { type: "EntriesInRelease" }]
352
+ }),
353
+ getMappedEntriesInReleases: build.query({
354
+ query(params) {
355
+ return {
356
+ url: "/content-releases/mapEntriesToReleases",
357
+ method: "GET",
358
+ config: {
359
+ params
360
+ }
361
+ };
362
+ },
363
+ transformResponse(response) {
364
+ return response.data;
365
+ },
366
+ providesTags: [{ type: "EntriesInRelease" }]
315
367
  })
316
368
  };
317
369
  }
@@ -323,43 +375,67 @@ const {
323
375
  useGetReleaseActionsQuery,
324
376
  useCreateReleaseMutation,
325
377
  useCreateReleaseActionMutation,
378
+ useCreateManyReleaseActionsMutation,
326
379
  useUpdateReleaseMutation,
327
380
  useUpdateReleaseActionMutation,
328
381
  usePublishReleaseMutation,
329
382
  useDeleteReleaseActionMutation,
330
- useDeleteReleaseMutation
383
+ useDeleteReleaseMutation,
384
+ useGetMappedEntriesInReleasesQuery
331
385
  } = releaseApi;
386
+ const getTimezoneOffset = (timezone, date) => {
387
+ try {
388
+ const offsetPart = new Intl.DateTimeFormat("en", {
389
+ timeZone: timezone,
390
+ timeZoneName: "longOffset"
391
+ }).formatToParts(date).find((part) => part.type === "timeZoneName");
392
+ const offset = offsetPart ? offsetPart.value : "";
393
+ let utcOffset = offset.replace("GMT", "UTC");
394
+ if (!utcOffset.includes("+") && !utcOffset.includes("-")) {
395
+ utcOffset = `${utcOffset}+00:00`;
396
+ }
397
+ return utcOffset;
398
+ } catch (error) {
399
+ return "";
400
+ }
401
+ };
402
+ const useTypedDispatch = reactRedux.useDispatch;
403
+ const useTypedSelector = reactRedux.useSelector;
332
404
  const StyledMenuItem = styled__default.default(v2.Menu.Item)`
333
405
  &:hover {
334
- background: ${({ theme }) => theme.colors.danger100};
406
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
407
+
408
+ svg {
409
+ path {
410
+ fill: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}600`]};
411
+ }
412
+ }
413
+
414
+ a {
415
+ color: ${({ theme }) => theme.colors.neutral800};
416
+ }
335
417
  }
336
418
 
337
419
  svg {
338
420
  path {
339
- fill: ${({ theme }) => theme.colors.danger600};
421
+ fill: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}600`]};
340
422
  }
341
423
  }
342
424
 
343
- &:hover {
344
- svg {
345
- path {
346
- fill: ${({ theme }) => theme.colors.danger600};
347
- }
348
- }
425
+ a {
426
+ color: ${({ theme }) => theme.colors.neutral800};
427
+ }
428
+
429
+ span,
430
+ a {
431
+ width: 100%;
349
432
  }
350
- `;
351
- const StyledCross = styled__default.default(icons.Cross)`
352
- padding: ${({ theme }) => theme.spaces[1]};
353
433
  `;
354
434
  const StyledIconButton = styled__default.default(designSystem.IconButton)`
355
435
  /* Setting this style inline with borderColor will not apply the style */
356
436
  border: ${({ theme }) => `1px solid ${theme.colors.neutral200}`};
357
437
  `;
358
- const ReleaseActionMenu = ({
359
- releaseId,
360
- actionId,
361
- hasTriggerBorder = false
362
- }) => {
438
+ const DeleteReleaseActionItem = ({ releaseId, actionId }) => {
363
439
  const { formatMessage } = reactIntl.useIntl();
364
440
  const toggleNotification = helperPlugin.useNotification();
365
441
  const { formatAPIError } = helperPlugin.useAPIErrorHandler();
@@ -392,6 +468,73 @@ const ReleaseActionMenu = ({
392
468
  }
393
469
  }
394
470
  };
471
+ return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: PERMISSIONS.deleteAction, children: /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { variant: "danger", onSelect: handleDeleteAction, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
472
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { as: icons.Cross, width: 3, height: 3 }),
473
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger600", variant: "omega", children: formatMessage({
474
+ id: "content-releases.content-manager-edit-view.remove-from-release",
475
+ defaultMessage: "Remove from release"
476
+ }) })
477
+ ] }) }) });
478
+ };
479
+ const ReleaseActionEntryLinkItem = ({
480
+ contentTypeUid,
481
+ entryId,
482
+ locale
483
+ }) => {
484
+ const { formatMessage } = reactIntl.useIntl();
485
+ const collectionTypePermissions = useTypedSelector(
486
+ (state) => state.rbacProvider.collectionTypesRelatedPermissions
487
+ );
488
+ const updatePermissions = contentTypeUid ? collectionTypePermissions[contentTypeUid]?.["plugin::content-manager.explorer.update"] : [];
489
+ const canUpdateEntryForLocale = Boolean(
490
+ !locale || updatePermissions?.find(
491
+ (permission) => permission.properties?.locales?.includes(locale)
492
+ )
493
+ );
494
+ return /* @__PURE__ */ jsxRuntime.jsx(
495
+ helperPlugin.CheckPermissions,
496
+ {
497
+ permissions: [
498
+ {
499
+ action: "plugin::content-manager.explorer.update",
500
+ subject: contentTypeUid
501
+ }
502
+ ],
503
+ children: canUpdateEntryForLocale && /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { children: /* @__PURE__ */ jsxRuntime.jsx(
504
+ v2.Link,
505
+ {
506
+ as: reactRouterDom.NavLink,
507
+ to: {
508
+ pathname: `/content-manager/collection-types/${contentTypeUid}/${entryId}`,
509
+ search: locale && `?plugins[i18n][locale]=${locale}`
510
+ },
511
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { as: icons.Pencil, width: 3, height: 3 }),
512
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: formatMessage({
513
+ id: "content-releases.content-manager-edit-view.edit-entry",
514
+ defaultMessage: "Edit entry"
515
+ }) })
516
+ }
517
+ ) })
518
+ }
519
+ );
520
+ };
521
+ const EditReleaseItem = ({ releaseId }) => {
522
+ const { formatMessage } = reactIntl.useIntl();
523
+ return /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { children: /* @__PURE__ */ jsxRuntime.jsx(
524
+ v2.Link,
525
+ {
526
+ href: `/admin/plugins/content-releases/${releaseId}`,
527
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { as: icons.Pencil, width: 3, height: 3 }),
528
+ isExternal: false,
529
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: formatMessage({
530
+ id: "content-releases.content-manager-edit-view.edit-release",
531
+ defaultMessage: "Edit release"
532
+ }) })
533
+ }
534
+ ) });
535
+ };
536
+ const Root = ({ children, hasTriggerBorder = false }) => {
537
+ const { formatMessage } = reactIntl.useIntl();
395
538
  return (
396
539
  // A user can access the dropdown if they have permissions to delete a release-action OR update a release
397
540
  /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: [...PERMISSIONS.deleteAction, ...PERMISSIONS.update], children: /* @__PURE__ */ jsxRuntime.jsxs(v2.Menu.Root, { children: [
@@ -408,16 +551,16 @@ const ReleaseActionMenu = ({
408
551
  icon: /* @__PURE__ */ jsxRuntime.jsx(icons.More, {})
409
552
  }
410
553
  ),
411
- /* @__PURE__ */ jsxRuntime.jsx(v2.Menu.Content, { top: 1, popoverPlacement: "bottom-end", children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: PERMISSIONS.deleteAction, children: /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { onSelect: handleDeleteAction, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
412
- /* @__PURE__ */ jsxRuntime.jsx(StyledCross, {}),
413
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger600", variant: "omega", children: formatMessage({
414
- id: "content-releases.content-manager-edit-view.remove-from-release",
415
- defaultMessage: "Remove from release"
416
- }) })
417
- ] }) }) }) })
554
+ /* @__PURE__ */ jsxRuntime.jsx(v2.Menu.Content, { top: 1, popoverPlacement: "bottom-end", children })
418
555
  ] }) })
419
556
  );
420
557
  };
558
+ const ReleaseActionMenu = {
559
+ Root,
560
+ EditReleaseItem,
561
+ DeleteReleaseActionItem,
562
+ ReleaseActionEntryLinkItem
563
+ };
421
564
  const getBorderLeftRadiusValue = (actionType) => {
422
565
  return actionType === "publish" ? 1 : 0;
423
566
  };
@@ -438,19 +581,40 @@ const FieldWrapper = styled__default.default(designSystem.Field)`
438
581
  text-transform: capitalize;
439
582
  }
440
583
 
441
- &:active,
442
584
  &[data-checked='true'] {
443
- color: ${({ theme }) => theme.colors.primary700};
444
- background-color: ${({ theme }) => theme.colors.primary100};
445
- border-color: ${({ theme }) => theme.colors.primary700};
585
+ color: ${({ theme, actionType }) => actionType === "publish" ? theme.colors.primary700 : theme.colors.danger600};
586
+ background-color: ${({ theme, actionType }) => actionType === "publish" ? theme.colors.primary100 : theme.colors.danger100};
587
+ border-color: ${({ theme, actionType }) => actionType === "publish" ? theme.colors.primary700 : theme.colors.danger600};
446
588
  }
447
589
 
448
590
  &[data-checked='false'] {
449
591
  border-left: ${({ actionType }) => actionType === "unpublish" && "none"};
450
592
  border-right: ${({ actionType }) => actionType === "publish" && "none"};
451
593
  }
594
+
595
+ &[data-checked='false'][data-disabled='false']:hover {
596
+ color: ${({ theme }) => theme.colors.neutral700};
597
+ background-color: ${({ theme }) => theme.colors.neutral100};
598
+ border-color: ${({ theme }) => theme.colors.neutral200};
599
+
600
+ & > label {
601
+ cursor: pointer;
602
+ }
603
+ }
604
+
605
+ &[data-disabled='true'] {
606
+ color: ${({ theme }) => theme.colors.neutral600};
607
+ background-color: ${({ theme }) => theme.colors.neutral150};
608
+ border-color: ${({ theme }) => theme.colors.neutral300};
609
+ }
452
610
  `;
453
- const ActionOption = ({ selected, actionType, handleChange, name }) => {
611
+ const ActionOption = ({
612
+ selected,
613
+ actionType,
614
+ handleChange,
615
+ name,
616
+ disabled = false
617
+ }) => {
454
618
  return /* @__PURE__ */ jsxRuntime.jsx(
455
619
  FieldWrapper,
456
620
  {
@@ -461,6 +625,7 @@ const ActionOption = ({ selected, actionType, handleChange, name }) => {
461
625
  position: "relative",
462
626
  cursor: "pointer",
463
627
  "data-checked": selected === actionType,
628
+ "data-disabled": disabled && selected !== actionType,
464
629
  children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.FieldLabel, { htmlFor: `${name}-${actionType}`, children: [
465
630
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { children: /* @__PURE__ */ jsxRuntime.jsx(
466
631
  designSystem.FieldInput,
@@ -470,7 +635,8 @@ const ActionOption = ({ selected, actionType, handleChange, name }) => {
470
635
  name,
471
636
  checked: selected === actionType,
472
637
  onChange: handleChange,
473
- value: actionType
638
+ value: actionType,
639
+ disabled
474
640
  }
475
641
  ) }),
476
642
  actionType
@@ -478,7 +644,12 @@ const ActionOption = ({ selected, actionType, handleChange, name }) => {
478
644
  }
479
645
  );
480
646
  };
481
- const ReleaseActionOptions = ({ selected, handleChange, name }) => {
647
+ const ReleaseActionOptions = ({
648
+ selected,
649
+ handleChange,
650
+ name,
651
+ disabled = false
652
+ }) => {
482
653
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { children: [
483
654
  /* @__PURE__ */ jsxRuntime.jsx(
484
655
  ActionOption,
@@ -486,7 +657,8 @@ const ReleaseActionOptions = ({ selected, handleChange, name }) => {
486
657
  actionType: "publish",
487
658
  selected,
488
659
  handleChange,
489
- name
660
+ name,
661
+ disabled
490
662
  }
491
663
  ),
492
664
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -495,7 +667,8 @@ const ReleaseActionOptions = ({ selected, handleChange, name }) => {
495
667
  actionType: "unpublish",
496
668
  selected,
497
669
  handleChange,
498
- name
670
+ name,
671
+ disabled
499
672
  }
500
673
  )
501
674
  ] });
@@ -539,6 +712,7 @@ const AddActionToReleaseModal = ({
539
712
  contentTypeUid,
540
713
  entryId
541
714
  }) => {
715
+ const releaseHeaderId = React__namespace.useId();
542
716
  const { formatMessage } = reactIntl.useIntl();
543
717
  const toggleNotification = helperPlugin.useNotification();
544
718
  const { formatAPIError } = helperPlugin.useAPIErrorHandler();
@@ -586,8 +760,8 @@ const AddActionToReleaseModal = ({
586
760
  }
587
761
  }
588
762
  };
589
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalLayout, { onClose: handleClose, labelledBy: "title", children: [
590
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "title", fontWeight: "bold", textColor: "neutral800", children: formatMessage({
763
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalLayout, { onClose: handleClose, labelledBy: releaseHeaderId, children: [
764
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: releaseHeaderId, fontWeight: "bold", textColor: "neutral800", children: formatMessage({
591
765
  id: "content-releases.content-manager-edit-view.add-to-release",
592
766
  defaultMessage: "Add to release"
593
767
  }) }) }),
@@ -657,16 +831,18 @@ const AddActionToReleaseModal = ({
657
831
  };
658
832
  const CMReleasesContainer = () => {
659
833
  const [isModalOpen, setIsModalOpen] = React__namespace.useState(false);
660
- const { formatMessage } = reactIntl.useIntl();
834
+ const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
661
835
  const {
662
836
  isCreatingEntry,
663
- allLayoutData: { contentType }
837
+ hasDraftAndPublish,
838
+ initialData: { id: entryId },
839
+ slug
664
840
  } = helperPlugin.useCMEditViewDataManager();
665
- const params = reactRouterDom.useParams();
666
- const canFetch = params?.id != null && contentType?.uid != null;
841
+ const contentTypeUid = slug;
842
+ const canFetch = entryId != null && contentTypeUid != null;
667
843
  const fetchParams = canFetch ? {
668
- contentTypeUid: contentType.uid,
669
- entryId: params.id,
844
+ contentTypeUid,
845
+ entryId,
670
846
  hasEntryAttached: true
671
847
  } : query.skipToken;
672
848
  const response = useGetReleasesForEntryQuery(fetchParams);
@@ -674,7 +850,7 @@ const CMReleasesContainer = () => {
674
850
  if (!canFetch) {
675
851
  return null;
676
852
  }
677
- if (isCreatingEntry || !contentType?.options?.draftAndPublish) {
853
+ if (isCreatingEntry || !hasDraftAndPublish) {
678
854
  return null;
679
855
  }
680
856
  const toggleModal = () => setIsModalOpen((prev) => !prev);
@@ -711,7 +887,7 @@ const CMReleasesContainer = () => {
711
887
  alignItems: "start",
712
888
  borderWidth: "1px",
713
889
  borderStyle: "solid",
714
- borderColor: getReleaseColorVariant(release.action.type, "200"),
890
+ borderColor: getReleaseColorVariant(release.actions[0].type, "200"),
715
891
  overflow: "hidden",
716
892
  hasRadius: true,
717
893
  children: [
@@ -722,35 +898,59 @@ const CMReleasesContainer = () => {
722
898
  paddingBottom: 3,
723
899
  paddingLeft: 4,
724
900
  paddingRight: 4,
725
- background: getReleaseColorVariant(release.action.type, "100"),
901
+ background: getReleaseColorVariant(release.actions[0].type, "100"),
726
902
  width: "100%",
727
903
  children: /* @__PURE__ */ jsxRuntime.jsx(
728
904
  designSystem.Typography,
729
905
  {
730
906
  fontSize: 1,
731
907
  variant: "pi",
732
- textColor: getReleaseColorVariant(release.action.type, "600"),
908
+ textColor: getReleaseColorVariant(release.actions[0].type, "600"),
733
909
  children: formatMessage(
734
910
  {
735
911
  id: "content-releases.content-manager-edit-view.list-releases.title",
736
912
  defaultMessage: "{isPublish, select, true {Will be published in} other {Will be unpublished in}}"
737
913
  },
738
- { isPublish: release.action.type === "publish" }
914
+ { isPublish: release.actions[0].type === "publish" }
739
915
  )
740
916
  }
741
917
  )
742
918
  }
743
919
  ),
744
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { padding: 4, direction: "column", gap: 3, width: "100%", alignItems: "flex-start", children: [
920
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { padding: 4, direction: "column", gap: 2, width: "100%", alignItems: "flex-start", children: [
745
921
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontSize: 2, fontWeight: "bold", variant: "omega", textColor: "neutral700", children: release.name }),
746
- /* @__PURE__ */ jsxRuntime.jsx(
747
- ReleaseActionMenu,
922
+ release.scheduledAt && release.timezone && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: formatMessage(
748
923
  {
749
- releaseId: release.id,
750
- actionId: release.action.id,
751
- hasTriggerBorder: true
924
+ id: "content-releases.content-manager-edit-view.scheduled.date",
925
+ defaultMessage: "{date} at {time} ({offset})"
926
+ },
927
+ {
928
+ date: formatDate(new Date(release.scheduledAt), {
929
+ day: "2-digit",
930
+ month: "2-digit",
931
+ year: "numeric",
932
+ timeZone: release.timezone
933
+ }),
934
+ time: formatTime(new Date(release.scheduledAt), {
935
+ hourCycle: "h23",
936
+ timeZone: release.timezone
937
+ }),
938
+ offset: getTimezoneOffset(
939
+ release.timezone,
940
+ new Date(release.scheduledAt)
941
+ )
752
942
  }
753
- )
943
+ ) }),
944
+ /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: PERMISSIONS.deleteAction, children: /* @__PURE__ */ jsxRuntime.jsxs(ReleaseActionMenu.Root, { hasTriggerBorder: true, children: [
945
+ /* @__PURE__ */ jsxRuntime.jsx(ReleaseActionMenu.EditReleaseItem, { releaseId: release.id }),
946
+ /* @__PURE__ */ jsxRuntime.jsx(
947
+ ReleaseActionMenu.DeleteReleaseActionItem,
948
+ {
949
+ releaseId: release.id,
950
+ actionId: release.actions[0].id
951
+ }
952
+ )
953
+ ] }) })
754
954
  ] })
755
955
  ]
756
956
  },
@@ -778,18 +978,281 @@ const CMReleasesContainer = () => {
778
978
  AddActionToReleaseModal,
779
979
  {
780
980
  handleClose: toggleModal,
781
- contentTypeUid: contentType.uid,
782
- entryId: params.id
981
+ contentTypeUid,
982
+ entryId
783
983
  }
784
984
  )
785
985
  ]
786
986
  }
787
987
  ) });
788
988
  };
989
+ const getContentPermissions = (subject) => {
990
+ const permissions = {
991
+ publish: [
992
+ {
993
+ action: "plugin::content-manager.explorer.publish",
994
+ subject,
995
+ id: "",
996
+ actionParameters: {},
997
+ properties: {},
998
+ conditions: []
999
+ }
1000
+ ]
1001
+ };
1002
+ return permissions;
1003
+ };
1004
+ const ReleaseAction = ({ ids, model }) => {
1005
+ const { formatMessage } = reactIntl.useIntl();
1006
+ const toggleNotification = helperPlugin.useNotification();
1007
+ const { formatAPIError } = helperPlugin.useAPIErrorHandler();
1008
+ const { modifiedData } = helperPlugin.useCMEditViewDataManager();
1009
+ const contentPermissions = getContentPermissions(model);
1010
+ const {
1011
+ allowedActions: { canPublish }
1012
+ } = helperPlugin.useRBAC(contentPermissions);
1013
+ const {
1014
+ allowedActions: { canCreate }
1015
+ } = helperPlugin.useRBAC(PERMISSIONS);
1016
+ const response = useGetReleasesQuery();
1017
+ const releases = response.data?.data;
1018
+ const [createManyReleaseActions, { isLoading }] = useCreateManyReleaseActionsMutation();
1019
+ const handleSubmit = async (values) => {
1020
+ const locale = modifiedData.locale;
1021
+ const releaseActionEntries = ids.map((id) => ({
1022
+ type: values.type,
1023
+ entry: {
1024
+ contentType: model,
1025
+ id,
1026
+ locale
1027
+ }
1028
+ }));
1029
+ const response2 = await createManyReleaseActions({
1030
+ body: releaseActionEntries,
1031
+ params: { releaseId: values.releaseId }
1032
+ });
1033
+ if ("data" in response2) {
1034
+ const notificationMessage = formatMessage(
1035
+ {
1036
+ id: "content-releases.content-manager-list-view.add-to-release.notification.success.message",
1037
+ defaultMessage: "{entriesAlreadyInRelease} out of {totalEntries} entries were already in the release."
1038
+ },
1039
+ {
1040
+ entriesAlreadyInRelease: response2.data.meta.entriesAlreadyInRelease,
1041
+ totalEntries: response2.data.meta.totalEntries
1042
+ }
1043
+ );
1044
+ const notification = {
1045
+ type: "success",
1046
+ title: formatMessage(
1047
+ {
1048
+ id: "content-releases.content-manager-list-view.add-to-release.notification.success.title",
1049
+ defaultMessage: "Successfully added to release."
1050
+ },
1051
+ {
1052
+ entriesAlreadyInRelease: response2.data.meta.entriesAlreadyInRelease,
1053
+ totalEntries: response2.data.meta.totalEntries
1054
+ }
1055
+ ),
1056
+ message: response2.data.meta.entriesAlreadyInRelease ? notificationMessage : ""
1057
+ };
1058
+ toggleNotification(notification);
1059
+ return true;
1060
+ }
1061
+ if ("error" in response2) {
1062
+ if (axios.isAxiosError(response2.error)) {
1063
+ toggleNotification({
1064
+ type: "warning",
1065
+ message: formatAPIError(response2.error)
1066
+ });
1067
+ } else {
1068
+ toggleNotification({
1069
+ type: "warning",
1070
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1071
+ });
1072
+ }
1073
+ }
1074
+ };
1075
+ if (!canCreate || !canPublish)
1076
+ return null;
1077
+ return {
1078
+ actionType: "release",
1079
+ variant: "tertiary",
1080
+ label: formatMessage({
1081
+ id: "content-manager-list-view.add-to-release",
1082
+ defaultMessage: "Add to Release"
1083
+ }),
1084
+ dialog: {
1085
+ type: "modal",
1086
+ title: formatMessage({
1087
+ id: "content-manager-list-view.add-to-release",
1088
+ defaultMessage: "Add to Release"
1089
+ }),
1090
+ content: ({ onClose }) => {
1091
+ return /* @__PURE__ */ jsxRuntime.jsx(
1092
+ formik.Formik,
1093
+ {
1094
+ onSubmit: async (values) => {
1095
+ const data = await handleSubmit(values);
1096
+ if (data) {
1097
+ return onClose();
1098
+ }
1099
+ },
1100
+ validationSchema: RELEASE_ACTION_FORM_SCHEMA,
1101
+ initialValues: INITIAL_VALUES,
1102
+ children: ({ values, setFieldValue }) => /* @__PURE__ */ jsxRuntime.jsxs(formik.Form, { children: [
1103
+ releases?.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(NoReleases, {}) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalBody, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
1104
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 6, children: /* @__PURE__ */ jsxRuntime.jsx(
1105
+ designSystem.SingleSelect,
1106
+ {
1107
+ required: true,
1108
+ label: formatMessage({
1109
+ id: "content-releases.content-manager-list-view.add-to-release.select-label",
1110
+ defaultMessage: "Select a release"
1111
+ }),
1112
+ placeholder: formatMessage({
1113
+ id: "content-releases.content-manager-list-view.add-to-release.select-placeholder",
1114
+ defaultMessage: "Select"
1115
+ }),
1116
+ onChange: (value) => setFieldValue("releaseId", value),
1117
+ value: values.releaseId,
1118
+ children: releases?.map((release) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: release.id, children: release.name }, release.id))
1119
+ }
1120
+ ) }),
1121
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.FieldLabel, { children: formatMessage({
1122
+ id: "content-releases.content-manager-list-view.add-to-release.action-type-label",
1123
+ defaultMessage: "What do you want to do with these entries?"
1124
+ }) }),
1125
+ /* @__PURE__ */ jsxRuntime.jsx(
1126
+ ReleaseActionOptions,
1127
+ {
1128
+ selected: values.type,
1129
+ handleChange: (e) => setFieldValue("type", e.target.value),
1130
+ name: "type"
1131
+ }
1132
+ )
1133
+ ] }) }),
1134
+ /* @__PURE__ */ jsxRuntime.jsx(
1135
+ designSystem.ModalFooter,
1136
+ {
1137
+ startActions: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: onClose, variant: "tertiary", name: "cancel", children: formatMessage({
1138
+ id: "content-releases.content-manager-list-view.add-to-release.cancel-button",
1139
+ defaultMessage: "Cancel"
1140
+ }) }),
1141
+ endActions: (
1142
+ /**
1143
+ * TODO: Ideally we would use isValid from Formik to disable the button, however currently it always returns true
1144
+ * for yup.string().required(), even when the value is falsy (including empty string)
1145
+ */
1146
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { type: "submit", disabled: !values.releaseId, loading: isLoading, children: formatMessage({
1147
+ id: "content-releases.content-manager-list-view.add-to-release.continue-button",
1148
+ defaultMessage: "Continue"
1149
+ }) })
1150
+ )
1151
+ }
1152
+ )
1153
+ ] })
1154
+ }
1155
+ );
1156
+ }
1157
+ }
1158
+ };
1159
+ };
1160
+ const Button = styled__default.default.button`
1161
+ svg {
1162
+ > g,
1163
+ path {
1164
+ fill: ${({ theme }) => theme.colors.neutral500};
1165
+ }
1166
+ }
1167
+ &:hover {
1168
+ svg {
1169
+ > g,
1170
+ path {
1171
+ fill: ${({ theme }) => theme.colors.neutral600};
1172
+ }
1173
+ }
1174
+ }
1175
+ &:active {
1176
+ svg {
1177
+ > g,
1178
+ path {
1179
+ fill: ${({ theme }) => theme.colors.neutral400};
1180
+ }
1181
+ }
1182
+ }
1183
+ `;
1184
+ const ActionWrapper = styled__default.default(designSystem.Flex)`
1185
+ svg {
1186
+ height: ${4 / 16}rem;
1187
+ }
1188
+ `;
1189
+ const useReleasesList = (entryId) => {
1190
+ const { uid: contentTypeUid } = useTypedSelector(
1191
+ (state) => state["content-manager_listView"].contentType
1192
+ );
1193
+ const listViewData = useTypedSelector((state) => state["content-manager_listView"].data);
1194
+ const entriesIds = listViewData.map((entry) => entry.id);
1195
+ const response = useGetMappedEntriesInReleasesQuery(
1196
+ { contentTypeUid, entriesIds },
1197
+ { skip: !entriesIds || !contentTypeUid || entriesIds.length === 0 }
1198
+ );
1199
+ const mappedEntriesInReleases = response.data || {};
1200
+ return mappedEntriesInReleases?.[entryId] || [];
1201
+ };
1202
+ const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1203
+ const { contentType } = layout;
1204
+ if (!contentType.options?.draftAndPublish) {
1205
+ return { displayedHeaders, layout };
1206
+ }
1207
+ return {
1208
+ displayedHeaders: [
1209
+ ...displayedHeaders,
1210
+ {
1211
+ key: "__release_key__",
1212
+ fieldSchema: { type: "string" },
1213
+ metadatas: { label: "To be released in", searchable: true, sortable: false },
1214
+ name: "releasedAt",
1215
+ cellFormatter: (props) => /* @__PURE__ */ jsxRuntime.jsx(ReleaseListCell, { ...props })
1216
+ }
1217
+ ],
1218
+ layout
1219
+ };
1220
+ };
1221
+ const ReleaseListCell = ({ id }) => {
1222
+ const releases = useReleasesList(id);
1223
+ const [visible, setVisible] = React__namespace.useState(false);
1224
+ const buttonRef = React__namespace.useRef(null);
1225
+ const { formatMessage } = reactIntl.useIntl();
1226
+ const handleTogglePopover = () => setVisible((prev) => !prev);
1227
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", onClick: handleTogglePopover, ref: buttonRef, children: /* @__PURE__ */ jsxRuntime.jsxs(ActionWrapper, { height: "2rem", width: "2rem", children: [
1228
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { style: { maxWidth: "252px", cursor: "pointer" }, textColor: "neutral800", children: releases.length > 0 ? formatMessage(
1229
+ {
1230
+ id: "content-releases.content-manager.list-view.releases-number",
1231
+ defaultMessage: "{number} {number, plural, one {release} other {releases}}"
1232
+ },
1233
+ {
1234
+ number: releases.length
1235
+ }
1236
+ ) : "-" }),
1237
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { children: [
1238
+ releases.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.SortIcon, {}),
1239
+ visible && /* @__PURE__ */ jsxRuntime.jsx(
1240
+ designSystem.Popover,
1241
+ {
1242
+ onDismiss: handleTogglePopover,
1243
+ source: buttonRef,
1244
+ spacing: 16,
1245
+ children: /* @__PURE__ */ jsxRuntime.jsx("ul", { children: releases.map(({ id: id2, name }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 3, as: "li", children: /* @__PURE__ */ jsxRuntime.jsx(v2.Link, { href: `/admin/plugins/content-releases/${id2}`, isExternal: false, children: name }) }, id2)) })
1246
+ }
1247
+ )
1248
+ ] })
1249
+ ] }) }) });
1250
+ };
789
1251
  const admin = {
790
1252
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
791
1253
  register(app) {
792
- if (window.strapi.features.isEnabled("cms-content-releases") && window.strapi.future.isEnabled("contentReleases")) {
1254
+ app.createHook("ContentReleases/pages/ReleaseDetails/add-locale-in-releases");
1255
+ if (window.strapi.features.isEnabled("cms-content-releases")) {
793
1256
  app.addMenuLink({
794
1257
  to: `/plugins/${pluginId}`,
795
1258
  icon: icons.PaperPlane,
@@ -798,7 +1261,7 @@ const admin = {
798
1261
  defaultMessage: "Releases"
799
1262
  },
800
1263
  async Component() {
801
- const { App } = await Promise.resolve().then(() => require("./App-o5_WfqR-.js"));
1264
+ const { App } = await Promise.resolve().then(() => require("./App-dLXY5ei3.js"));
802
1265
  return App;
803
1266
  },
804
1267
  permissions: PERMISSIONS.main
@@ -811,12 +1274,33 @@ const admin = {
811
1274
  name: `${pluginId}-link`,
812
1275
  Component: CMReleasesContainer
813
1276
  });
1277
+ app.plugins["content-manager"].apis.addBulkAction((actions) => {
1278
+ const deleteActionIndex = actions.findIndex((action) => action.name === "DeleteAction");
1279
+ actions.splice(deleteActionIndex, 0, ReleaseAction);
1280
+ return actions;
1281
+ });
1282
+ app.registerHook("Admin/CM/pages/ListView/inject-column-in-table", addColumnToTableHook);
1283
+ } else if (!window.strapi.features.isEnabled("cms-content-releases") && window.strapi?.flags?.promoteEE) {
1284
+ app.addMenuLink({
1285
+ to: `/plugins/purchase-content-releases`,
1286
+ icon: icons.PaperPlane,
1287
+ intlLabel: {
1288
+ id: `${pluginId}.plugin.name`,
1289
+ defaultMessage: "Releases"
1290
+ },
1291
+ async Component() {
1292
+ const { PurchaseContentReleases } = await Promise.resolve().then(() => require("./PurchaseContentReleases-bpIYXOfu.js"));
1293
+ return PurchaseContentReleases;
1294
+ },
1295
+ lockIcon: true
1296
+ // TODO: to replace with another name in v5
1297
+ });
814
1298
  }
815
1299
  },
816
1300
  async registerTrads({ locales }) {
817
1301
  const importedTrads = await Promise.all(
818
1302
  locales.map((locale) => {
819
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-haKSQIo8.js")) }), `./translations/${locale}.json`).then(({ default: data }) => {
1303
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-HrREghh3.js")) }), `./translations/${locale}.json`).then(({ default: data }) => {
820
1304
  return {
821
1305
  data: helperPlugin.prefixPluginTranslations(data, "content-releases"),
822
1306
  locale
@@ -836,14 +1320,17 @@ exports.PERMISSIONS = PERMISSIONS;
836
1320
  exports.ReleaseActionMenu = ReleaseActionMenu;
837
1321
  exports.ReleaseActionOptions = ReleaseActionOptions;
838
1322
  exports.admin = admin;
1323
+ exports.getTimezoneOffset = getTimezoneOffset;
839
1324
  exports.isAxiosError = isAxiosError;
840
1325
  exports.pluginId = pluginId;
1326
+ exports.releaseApi = releaseApi;
841
1327
  exports.useCreateReleaseMutation = useCreateReleaseMutation;
842
1328
  exports.useDeleteReleaseMutation = useDeleteReleaseMutation;
843
1329
  exports.useGetReleaseActionsQuery = useGetReleaseActionsQuery;
844
1330
  exports.useGetReleaseQuery = useGetReleaseQuery;
845
1331
  exports.useGetReleasesQuery = useGetReleasesQuery;
846
1332
  exports.usePublishReleaseMutation = usePublishReleaseMutation;
1333
+ exports.useTypedDispatch = useTypedDispatch;
847
1334
  exports.useUpdateReleaseActionMutation = useUpdateReleaseActionMutation;
848
1335
  exports.useUpdateReleaseMutation = useUpdateReleaseMutation;
849
- //# sourceMappingURL=index-EdBmRHRU.js.map
1336
+ //# sourceMappingURL=index-CVO0Rqdm.js.map