@strapi/content-releases 0.0.0-next.c5f067b5650921187770124e9b6c8186e805e242 → 0.0.0-next.cfecf3ad808761571ce11e528113e5c1ea5f87fd

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/_chunks/{App-5PsAyVt2.js → App-HjWtUYmc.js} +728 -459
  2. package/dist/_chunks/App-HjWtUYmc.js.map +1 -0
  3. package/dist/_chunks/{App-3ycH2d3s.mjs → App-gu1aiP6i.mjs} +738 -470
  4. package/dist/_chunks/App-gu1aiP6i.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-2DuPv5k0.js → en-HrREghh3.js} +27 -7
  10. package/dist/_chunks/en-HrREghh3.js.map +1 -0
  11. package/dist/_chunks/{en-SOqjCdyh.mjs → en-ltT1TlKQ.mjs} +27 -7
  12. package/dist/_chunks/en-ltT1TlKQ.mjs.map +1 -0
  13. package/dist/_chunks/{index-D57Rztnc.js → index-ZNwxYN8H.js} +437 -32
  14. package/dist/_chunks/index-ZNwxYN8H.js.map +1 -0
  15. package/dist/_chunks/{index-4gUWuCQV.mjs → index-mvj9PSKd.mjs} +453 -48
  16. package/dist/_chunks/index-mvj9PSKd.mjs.map +1 -0
  17. package/dist/admin/index.js +1 -1
  18. package/dist/admin/index.mjs +2 -2
  19. package/dist/server/index.js +1037 -421
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +1036 -421
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +15 -12
  24. package/dist/_chunks/App-3ycH2d3s.mjs.map +0 -1
  25. package/dist/_chunks/App-5PsAyVt2.js.map +0 -1
  26. package/dist/_chunks/en-2DuPv5k0.js.map +0 -1
  27. package/dist/_chunks/en-SOqjCdyh.mjs.map +0 -1
  28. package/dist/_chunks/index-4gUWuCQV.mjs.map +0 -1
  29. package/dist/_chunks/index-D57Rztnc.js.map +0 -1
@@ -1,9 +1,9 @@
1
- import { getFetchClient, useNotification, useAPIErrorHandler, CheckPermissions, useCMEditViewDataManager, NoContent, prefixPluginTranslations } from "@strapi/helper-plugin";
1
+ import { getFetchClient, useNotification, useAPIErrorHandler, CheckPermissions, useCMEditViewDataManager, NoContent, useRBAC, SortIcon, prefixPluginTranslations } from "@strapi/helper-plugin";
2
2
  import { Cross, Pencil, More, Plus, PaperPlane } from "@strapi/icons";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
4
  import * as React from "react";
5
5
  import { skipToken } from "@reduxjs/toolkit/query";
6
- import { IconButton, Flex, Icon, Typography, Field, FieldLabel, VisuallyHidden, FieldInput, Box, Button, ModalLayout, ModalHeader, ModalBody, SingleSelect, SingleSelectOption, ModalFooter } from "@strapi/design-system";
6
+ import { IconButton, Flex, Icon, Typography, Field, FieldLabel, VisuallyHidden, FieldInput, Box, Button as Button$1, ModalLayout, ModalHeader, ModalBody, SingleSelect, SingleSelectOption, ModalFooter, Popover } from "@strapi/design-system";
7
7
  import { Menu, Link, LinkButton } from "@strapi/design-system/v2";
8
8
  import { isAxiosError as isAxiosError$1 } from "axios";
9
9
  import { Formik, Form } from "formik";
@@ -136,7 +136,7 @@ const isAxiosError = (err) => {
136
136
  const releaseApi = createApi({
137
137
  reducerPath: pluginId,
138
138
  baseQuery: axiosBaseQuery,
139
- tagTypes: ["Release", "ReleaseAction"],
139
+ tagTypes: ["Release", "ReleaseAction", "EntriesInRelease"],
140
140
  endpoints: (build) => {
141
141
  return {
142
142
  getReleasesForEntry: build.query({
@@ -251,6 +251,20 @@ const releaseApi = createApi({
251
251
  { type: "ReleaseAction", id: "LIST" }
252
252
  ]
253
253
  }),
254
+ createManyReleaseActions: build.mutation({
255
+ query({ body, params }) {
256
+ return {
257
+ url: `/content-releases/${params.releaseId}/actions/bulk`,
258
+ method: "POST",
259
+ data: body
260
+ };
261
+ },
262
+ invalidatesTags: [
263
+ { type: "Release", id: "LIST" },
264
+ { type: "ReleaseAction", id: "LIST" },
265
+ { type: "EntriesInRelease" }
266
+ ]
267
+ }),
254
268
  updateReleaseAction: build.mutation({
255
269
  query({ body, params }) {
256
270
  return {
@@ -288,9 +302,11 @@ const releaseApi = createApi({
288
302
  method: "DELETE"
289
303
  };
290
304
  },
291
- invalidatesTags: [
305
+ invalidatesTags: (result, error, arg) => [
292
306
  { type: "Release", id: "LIST" },
293
- { type: "ReleaseAction", id: "LIST" }
307
+ { type: "Release", id: arg.params.releaseId },
308
+ { type: "ReleaseAction", id: "LIST" },
309
+ { type: "EntriesInRelease" }
294
310
  ]
295
311
  }),
296
312
  publishRelease: build.mutation({
@@ -309,7 +325,22 @@ const releaseApi = createApi({
309
325
  method: "DELETE"
310
326
  };
311
327
  },
312
- invalidatesTags: (result, error, arg) => [{ type: "Release", id: arg.id }]
328
+ invalidatesTags: () => [{ type: "Release", id: "LIST" }, { type: "EntriesInRelease" }]
329
+ }),
330
+ getMappedEntriesInReleases: build.query({
331
+ query(params) {
332
+ return {
333
+ url: "/content-releases/mapEntriesToReleases",
334
+ method: "GET",
335
+ config: {
336
+ params
337
+ }
338
+ };
339
+ },
340
+ transformResponse(response) {
341
+ return response.data;
342
+ },
343
+ providesTags: [{ type: "EntriesInRelease" }]
313
344
  })
314
345
  };
315
346
  }
@@ -321,12 +352,30 @@ const {
321
352
  useGetReleaseActionsQuery,
322
353
  useCreateReleaseMutation,
323
354
  useCreateReleaseActionMutation,
355
+ useCreateManyReleaseActionsMutation,
324
356
  useUpdateReleaseMutation,
325
357
  useUpdateReleaseActionMutation,
326
358
  usePublishReleaseMutation,
327
359
  useDeleteReleaseActionMutation,
328
- useDeleteReleaseMutation
360
+ useDeleteReleaseMutation,
361
+ useGetMappedEntriesInReleasesQuery
329
362
  } = releaseApi;
363
+ const getTimezoneOffset = (timezone, date) => {
364
+ try {
365
+ const offsetPart = new Intl.DateTimeFormat("en", {
366
+ timeZone: timezone,
367
+ timeZoneName: "longOffset"
368
+ }).formatToParts(date).find((part) => part.type === "timeZoneName");
369
+ const offset = offsetPart ? offsetPart.value : "";
370
+ let utcOffset = offset.replace("GMT", "UTC");
371
+ if (!utcOffset.includes("+") && !utcOffset.includes("-")) {
372
+ utcOffset = `${utcOffset}+00:00`;
373
+ }
374
+ return utcOffset;
375
+ } catch (error) {
376
+ return "";
377
+ }
378
+ };
330
379
  const useTypedDispatch = useDispatch;
331
380
  const useTypedSelector = useSelector;
332
381
  const StyledMenuItem = styled(Menu.Item)`
@@ -397,7 +446,7 @@ const DeleteReleaseActionItem = ({ releaseId, actionId }) => {
397
446
  }
398
447
  };
399
448
  return /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.deleteAction, children: /* @__PURE__ */ jsx(StyledMenuItem, { variant: "danger", onSelect: handleDeleteAction, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
400
- /* @__PURE__ */ jsx(Icon, { as: Cross, padding: 1 }),
449
+ /* @__PURE__ */ jsx(Icon, { as: Cross, width: 3, height: 3 }),
401
450
  /* @__PURE__ */ jsx(Typography, { textColor: "danger600", variant: "omega", children: formatMessage({
402
451
  id: "content-releases.content-manager-edit-view.remove-from-release",
403
452
  defaultMessage: "Remove from release"
@@ -436,7 +485,7 @@ const ReleaseActionEntryLinkItem = ({
436
485
  pathname: `/content-manager/collection-types/${contentTypeUid}/${entryId}`,
437
486
  search: locale && `?plugins[i18n][locale]=${locale}`
438
487
  },
439
- startIcon: /* @__PURE__ */ jsx(Icon, { as: Pencil, padding: 1 }),
488
+ startIcon: /* @__PURE__ */ jsx(Icon, { as: Pencil, width: 3, height: 3 }),
440
489
  children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: formatMessage({
441
490
  id: "content-releases.content-manager-edit-view.edit-entry",
442
491
  defaultMessage: "Edit entry"
@@ -446,6 +495,21 @@ const ReleaseActionEntryLinkItem = ({
446
495
  }
447
496
  );
448
497
  };
498
+ const EditReleaseItem = ({ releaseId }) => {
499
+ const { formatMessage } = useIntl();
500
+ return /* @__PURE__ */ jsx(StyledMenuItem, { children: /* @__PURE__ */ jsx(
501
+ Link,
502
+ {
503
+ href: `/admin/plugins/content-releases/${releaseId}`,
504
+ startIcon: /* @__PURE__ */ jsx(Icon, { as: Pencil, width: 3, height: 3 }),
505
+ isExternal: false,
506
+ children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: formatMessage({
507
+ id: "content-releases.content-manager-edit-view.edit-release",
508
+ defaultMessage: "Edit release"
509
+ }) })
510
+ }
511
+ ) });
512
+ };
449
513
  const Root = ({ children, hasTriggerBorder = false }) => {
450
514
  const { formatMessage } = useIntl();
451
515
  return (
@@ -470,6 +534,7 @@ const Root = ({ children, hasTriggerBorder = false }) => {
470
534
  };
471
535
  const ReleaseActionMenu = {
472
536
  Root,
537
+ EditReleaseItem,
473
538
  DeleteReleaseActionItem,
474
539
  ReleaseActionEntryLinkItem
475
540
  };
@@ -493,19 +558,40 @@ const FieldWrapper = styled(Field)`
493
558
  text-transform: capitalize;
494
559
  }
495
560
 
496
- &:active,
497
561
  &[data-checked='true'] {
498
- color: ${({ theme }) => theme.colors.primary700};
499
- background-color: ${({ theme }) => theme.colors.primary100};
500
- border-color: ${({ theme }) => theme.colors.primary700};
562
+ color: ${({ theme, actionType }) => actionType === "publish" ? theme.colors.primary700 : theme.colors.danger600};
563
+ background-color: ${({ theme, actionType }) => actionType === "publish" ? theme.colors.primary100 : theme.colors.danger100};
564
+ border-color: ${({ theme, actionType }) => actionType === "publish" ? theme.colors.primary700 : theme.colors.danger600};
501
565
  }
502
566
 
503
567
  &[data-checked='false'] {
504
568
  border-left: ${({ actionType }) => actionType === "unpublish" && "none"};
505
569
  border-right: ${({ actionType }) => actionType === "publish" && "none"};
506
570
  }
571
+
572
+ &[data-checked='false'][data-disabled='false']:hover {
573
+ color: ${({ theme }) => theme.colors.neutral700};
574
+ background-color: ${({ theme }) => theme.colors.neutral100};
575
+ border-color: ${({ theme }) => theme.colors.neutral200};
576
+
577
+ & > label {
578
+ cursor: pointer;
579
+ }
580
+ }
581
+
582
+ &[data-disabled='true'] {
583
+ color: ${({ theme }) => theme.colors.neutral600};
584
+ background-color: ${({ theme }) => theme.colors.neutral150};
585
+ border-color: ${({ theme }) => theme.colors.neutral300};
586
+ }
507
587
  `;
508
- const ActionOption = ({ selected, actionType, handleChange, name }) => {
588
+ const ActionOption = ({
589
+ selected,
590
+ actionType,
591
+ handleChange,
592
+ name,
593
+ disabled = false
594
+ }) => {
509
595
  return /* @__PURE__ */ jsx(
510
596
  FieldWrapper,
511
597
  {
@@ -516,6 +602,7 @@ const ActionOption = ({ selected, actionType, handleChange, name }) => {
516
602
  position: "relative",
517
603
  cursor: "pointer",
518
604
  "data-checked": selected === actionType,
605
+ "data-disabled": disabled && selected !== actionType,
519
606
  children: /* @__PURE__ */ jsxs(FieldLabel, { htmlFor: `${name}-${actionType}`, children: [
520
607
  /* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx(
521
608
  FieldInput,
@@ -525,7 +612,8 @@ const ActionOption = ({ selected, actionType, handleChange, name }) => {
525
612
  name,
526
613
  checked: selected === actionType,
527
614
  onChange: handleChange,
528
- value: actionType
615
+ value: actionType,
616
+ disabled
529
617
  }
530
618
  ) }),
531
619
  actionType
@@ -533,7 +621,12 @@ const ActionOption = ({ selected, actionType, handleChange, name }) => {
533
621
  }
534
622
  );
535
623
  };
536
- const ReleaseActionOptions = ({ selected, handleChange, name }) => {
624
+ const ReleaseActionOptions = ({
625
+ selected,
626
+ handleChange,
627
+ name,
628
+ disabled = false
629
+ }) => {
537
630
  return /* @__PURE__ */ jsxs(Flex, { children: [
538
631
  /* @__PURE__ */ jsx(
539
632
  ActionOption,
@@ -541,7 +634,8 @@ const ReleaseActionOptions = ({ selected, handleChange, name }) => {
541
634
  actionType: "publish",
542
635
  selected,
543
636
  handleChange,
544
- name
637
+ name,
638
+ disabled
545
639
  }
546
640
  ),
547
641
  /* @__PURE__ */ jsx(
@@ -550,7 +644,8 @@ const ReleaseActionOptions = ({ selected, handleChange, name }) => {
550
644
  actionType: "unpublish",
551
645
  selected,
552
646
  handleChange,
553
- name
647
+ name,
648
+ disabled
554
649
  }
555
650
  )
556
651
  ] });
@@ -594,6 +689,7 @@ const AddActionToReleaseModal = ({
594
689
  contentTypeUid,
595
690
  entryId
596
691
  }) => {
692
+ const releaseHeaderId = React.useId();
597
693
  const { formatMessage } = useIntl();
598
694
  const toggleNotification = useNotification();
599
695
  const { formatAPIError } = useAPIErrorHandler();
@@ -641,8 +737,8 @@ const AddActionToReleaseModal = ({
641
737
  }
642
738
  }
643
739
  };
644
- return /* @__PURE__ */ jsxs(ModalLayout, { onClose: handleClose, labelledBy: "title", children: [
645
- /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { id: "title", fontWeight: "bold", textColor: "neutral800", children: formatMessage({
740
+ return /* @__PURE__ */ jsxs(ModalLayout, { onClose: handleClose, labelledBy: releaseHeaderId, children: [
741
+ /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { id: releaseHeaderId, fontWeight: "bold", textColor: "neutral800", children: formatMessage({
646
742
  id: "content-releases.content-manager-edit-view.add-to-release",
647
743
  defaultMessage: "Add to release"
648
744
  }) }) }),
@@ -688,7 +784,7 @@ const AddActionToReleaseModal = ({
688
784
  /* @__PURE__ */ jsx(
689
785
  ModalFooter,
690
786
  {
691
- startActions: /* @__PURE__ */ jsx(Button, { onClick: handleClose, variant: "tertiary", name: "cancel", children: formatMessage({
787
+ startActions: /* @__PURE__ */ jsx(Button$1, { onClick: handleClose, variant: "tertiary", name: "cancel", children: formatMessage({
692
788
  id: "content-releases.content-manager-edit-view.add-to-release.cancel-button",
693
789
  defaultMessage: "Cancel"
694
790
  }) }),
@@ -697,7 +793,7 @@ const AddActionToReleaseModal = ({
697
793
  * TODO: Ideally we would use isValid from Formik to disable the button, however currently it always returns true
698
794
  * for yup.string().required(), even when the value is falsy (including empty string)
699
795
  */
700
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled: !values.releaseId, loading: isLoading, children: formatMessage({
796
+ /* @__PURE__ */ jsx(Button$1, { type: "submit", disabled: !values.releaseId, loading: isLoading, children: formatMessage({
701
797
  id: "content-releases.content-manager-edit-view.add-to-release.continue-button",
702
798
  defaultMessage: "Continue"
703
799
  }) })
@@ -712,7 +808,7 @@ const AddActionToReleaseModal = ({
712
808
  };
713
809
  const CMReleasesContainer = () => {
714
810
  const [isModalOpen, setIsModalOpen] = React.useState(false);
715
- const { formatMessage } = useIntl();
811
+ const { formatMessage, formatDate, formatTime } = useIntl();
716
812
  const {
717
813
  isCreatingEntry,
718
814
  hasDraftAndPublish,
@@ -768,7 +864,7 @@ const CMReleasesContainer = () => {
768
864
  alignItems: "start",
769
865
  borderWidth: "1px",
770
866
  borderStyle: "solid",
771
- borderColor: getReleaseColorVariant(release.action.type, "200"),
867
+ borderColor: getReleaseColorVariant(release.actions[0].type, "200"),
772
868
  overflow: "hidden",
773
869
  hasRadius: true,
774
870
  children: [
@@ -779,34 +875,59 @@ const CMReleasesContainer = () => {
779
875
  paddingBottom: 3,
780
876
  paddingLeft: 4,
781
877
  paddingRight: 4,
782
- background: getReleaseColorVariant(release.action.type, "100"),
878
+ background: getReleaseColorVariant(release.actions[0].type, "100"),
783
879
  width: "100%",
784
880
  children: /* @__PURE__ */ jsx(
785
881
  Typography,
786
882
  {
787
883
  fontSize: 1,
788
884
  variant: "pi",
789
- textColor: getReleaseColorVariant(release.action.type, "600"),
885
+ textColor: getReleaseColorVariant(release.actions[0].type, "600"),
790
886
  children: formatMessage(
791
887
  {
792
888
  id: "content-releases.content-manager-edit-view.list-releases.title",
793
889
  defaultMessage: "{isPublish, select, true {Will be published in} other {Will be unpublished in}}"
794
890
  },
795
- { isPublish: release.action.type === "publish" }
891
+ { isPublish: release.actions[0].type === "publish" }
796
892
  )
797
893
  }
798
894
  )
799
895
  }
800
896
  ),
801
- /* @__PURE__ */ jsxs(Flex, { padding: 4, direction: "column", gap: 3, width: "100%", alignItems: "flex-start", children: [
897
+ /* @__PURE__ */ jsxs(Flex, { padding: 4, direction: "column", gap: 2, width: "100%", alignItems: "flex-start", children: [
802
898
  /* @__PURE__ */ jsx(Typography, { fontSize: 2, fontWeight: "bold", variant: "omega", textColor: "neutral700", children: release.name }),
803
- /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.deleteAction, children: /* @__PURE__ */ jsx(ReleaseActionMenu.Root, { hasTriggerBorder: true, children: /* @__PURE__ */ jsx(
804
- ReleaseActionMenu.DeleteReleaseActionItem,
899
+ release.scheduledAt && release.timezone && /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: formatMessage(
900
+ {
901
+ id: "content-releases.content-manager-edit-view.scheduled.date",
902
+ defaultMessage: "{date} at {time} ({offset})"
903
+ },
805
904
  {
806
- releaseId: release.id,
807
- actionId: release.action.id
905
+ date: formatDate(new Date(release.scheduledAt), {
906
+ day: "2-digit",
907
+ month: "2-digit",
908
+ year: "numeric",
909
+ timeZone: release.timezone
910
+ }),
911
+ time: formatTime(new Date(release.scheduledAt), {
912
+ hourCycle: "h23",
913
+ timeZone: release.timezone
914
+ }),
915
+ offset: getTimezoneOffset(
916
+ release.timezone,
917
+ new Date(release.scheduledAt)
918
+ )
808
919
  }
809
- ) }) })
920
+ ) }),
921
+ /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.deleteAction, children: /* @__PURE__ */ jsxs(ReleaseActionMenu.Root, { hasTriggerBorder: true, children: [
922
+ /* @__PURE__ */ jsx(ReleaseActionMenu.EditReleaseItem, { releaseId: release.id }),
923
+ /* @__PURE__ */ jsx(
924
+ ReleaseActionMenu.DeleteReleaseActionItem,
925
+ {
926
+ releaseId: release.id,
927
+ actionId: release.actions[0].id
928
+ }
929
+ )
930
+ ] }) })
810
931
  ] })
811
932
  ]
812
933
  },
@@ -814,7 +935,7 @@ const CMReleasesContainer = () => {
814
935
  );
815
936
  }),
816
937
  /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.createAction, children: /* @__PURE__ */ jsx(
817
- Button,
938
+ Button$1,
818
939
  {
819
940
  justifyContent: "center",
820
941
  paddingLeft: 4,
@@ -842,9 +963,272 @@ const CMReleasesContainer = () => {
842
963
  }
843
964
  ) });
844
965
  };
966
+ const getContentPermissions = (subject) => {
967
+ const permissions = {
968
+ publish: [
969
+ {
970
+ action: "plugin::content-manager.explorer.publish",
971
+ subject,
972
+ id: "",
973
+ actionParameters: {},
974
+ properties: {},
975
+ conditions: []
976
+ }
977
+ ]
978
+ };
979
+ return permissions;
980
+ };
981
+ const ReleaseAction = ({ ids, model }) => {
982
+ const { formatMessage } = useIntl();
983
+ const toggleNotification = useNotification();
984
+ const { formatAPIError } = useAPIErrorHandler();
985
+ const { modifiedData } = useCMEditViewDataManager();
986
+ const contentPermissions = getContentPermissions(model);
987
+ const {
988
+ allowedActions: { canPublish }
989
+ } = useRBAC(contentPermissions);
990
+ const {
991
+ allowedActions: { canCreate }
992
+ } = useRBAC(PERMISSIONS);
993
+ const response = useGetReleasesQuery();
994
+ const releases = response.data?.data;
995
+ const [createManyReleaseActions, { isLoading }] = useCreateManyReleaseActionsMutation();
996
+ const handleSubmit = async (values) => {
997
+ const locale = modifiedData.locale;
998
+ const releaseActionEntries = ids.map((id) => ({
999
+ type: values.type,
1000
+ entry: {
1001
+ contentType: model,
1002
+ id,
1003
+ locale
1004
+ }
1005
+ }));
1006
+ const response2 = await createManyReleaseActions({
1007
+ body: releaseActionEntries,
1008
+ params: { releaseId: values.releaseId }
1009
+ });
1010
+ if ("data" in response2) {
1011
+ const notificationMessage = formatMessage(
1012
+ {
1013
+ id: "content-releases.content-manager-list-view.add-to-release.notification.success.message",
1014
+ defaultMessage: "{entriesAlreadyInRelease} out of {totalEntries} entries were already in the release."
1015
+ },
1016
+ {
1017
+ entriesAlreadyInRelease: response2.data.meta.entriesAlreadyInRelease,
1018
+ totalEntries: response2.data.meta.totalEntries
1019
+ }
1020
+ );
1021
+ const notification = {
1022
+ type: "success",
1023
+ title: formatMessage(
1024
+ {
1025
+ id: "content-releases.content-manager-list-view.add-to-release.notification.success.title",
1026
+ defaultMessage: "Successfully added to release."
1027
+ },
1028
+ {
1029
+ entriesAlreadyInRelease: response2.data.meta.entriesAlreadyInRelease,
1030
+ totalEntries: response2.data.meta.totalEntries
1031
+ }
1032
+ ),
1033
+ message: response2.data.meta.entriesAlreadyInRelease ? notificationMessage : ""
1034
+ };
1035
+ toggleNotification(notification);
1036
+ return true;
1037
+ }
1038
+ if ("error" in response2) {
1039
+ if (isAxiosError$1(response2.error)) {
1040
+ toggleNotification({
1041
+ type: "warning",
1042
+ message: formatAPIError(response2.error)
1043
+ });
1044
+ } else {
1045
+ toggleNotification({
1046
+ type: "warning",
1047
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1048
+ });
1049
+ }
1050
+ }
1051
+ };
1052
+ if (!canCreate || !canPublish)
1053
+ return null;
1054
+ return {
1055
+ actionType: "release",
1056
+ variant: "tertiary",
1057
+ label: formatMessage({
1058
+ id: "content-manager-list-view.add-to-release",
1059
+ defaultMessage: "Add to Release"
1060
+ }),
1061
+ dialog: {
1062
+ type: "modal",
1063
+ title: formatMessage({
1064
+ id: "content-manager-list-view.add-to-release",
1065
+ defaultMessage: "Add to Release"
1066
+ }),
1067
+ content: ({ onClose }) => {
1068
+ return /* @__PURE__ */ jsx(
1069
+ Formik,
1070
+ {
1071
+ onSubmit: async (values) => {
1072
+ const data = await handleSubmit(values);
1073
+ if (data) {
1074
+ return onClose();
1075
+ }
1076
+ },
1077
+ validationSchema: RELEASE_ACTION_FORM_SCHEMA,
1078
+ initialValues: INITIAL_VALUES,
1079
+ children: ({ values, setFieldValue }) => /* @__PURE__ */ jsxs(Form, { children: [
1080
+ releases?.length === 0 ? /* @__PURE__ */ jsx(NoReleases, {}) : /* @__PURE__ */ jsx(ModalBody, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
1081
+ /* @__PURE__ */ jsx(Box, { paddingBottom: 6, children: /* @__PURE__ */ jsx(
1082
+ SingleSelect,
1083
+ {
1084
+ required: true,
1085
+ label: formatMessage({
1086
+ id: "content-releases.content-manager-list-view.add-to-release.select-label",
1087
+ defaultMessage: "Select a release"
1088
+ }),
1089
+ placeholder: formatMessage({
1090
+ id: "content-releases.content-manager-list-view.add-to-release.select-placeholder",
1091
+ defaultMessage: "Select"
1092
+ }),
1093
+ onChange: (value) => setFieldValue("releaseId", value),
1094
+ value: values.releaseId,
1095
+ children: releases?.map((release) => /* @__PURE__ */ jsx(SingleSelectOption, { value: release.id, children: release.name }, release.id))
1096
+ }
1097
+ ) }),
1098
+ /* @__PURE__ */ jsx(FieldLabel, { children: formatMessage({
1099
+ id: "content-releases.content-manager-list-view.add-to-release.action-type-label",
1100
+ defaultMessage: "What do you want to do with these entries?"
1101
+ }) }),
1102
+ /* @__PURE__ */ jsx(
1103
+ ReleaseActionOptions,
1104
+ {
1105
+ selected: values.type,
1106
+ handleChange: (e) => setFieldValue("type", e.target.value),
1107
+ name: "type"
1108
+ }
1109
+ )
1110
+ ] }) }),
1111
+ /* @__PURE__ */ jsx(
1112
+ ModalFooter,
1113
+ {
1114
+ startActions: /* @__PURE__ */ jsx(Button$1, { onClick: onClose, variant: "tertiary", name: "cancel", children: formatMessage({
1115
+ id: "content-releases.content-manager-list-view.add-to-release.cancel-button",
1116
+ defaultMessage: "Cancel"
1117
+ }) }),
1118
+ endActions: (
1119
+ /**
1120
+ * TODO: Ideally we would use isValid from Formik to disable the button, however currently it always returns true
1121
+ * for yup.string().required(), even when the value is falsy (including empty string)
1122
+ */
1123
+ /* @__PURE__ */ jsx(Button$1, { type: "submit", disabled: !values.releaseId, loading: isLoading, children: formatMessage({
1124
+ id: "content-releases.content-manager-list-view.add-to-release.continue-button",
1125
+ defaultMessage: "Continue"
1126
+ }) })
1127
+ )
1128
+ }
1129
+ )
1130
+ ] })
1131
+ }
1132
+ );
1133
+ }
1134
+ }
1135
+ };
1136
+ };
1137
+ const Button = styled.button`
1138
+ svg {
1139
+ > g,
1140
+ path {
1141
+ fill: ${({ theme }) => theme.colors.neutral500};
1142
+ }
1143
+ }
1144
+ &:hover {
1145
+ svg {
1146
+ > g,
1147
+ path {
1148
+ fill: ${({ theme }) => theme.colors.neutral600};
1149
+ }
1150
+ }
1151
+ }
1152
+ &:active {
1153
+ svg {
1154
+ > g,
1155
+ path {
1156
+ fill: ${({ theme }) => theme.colors.neutral400};
1157
+ }
1158
+ }
1159
+ }
1160
+ `;
1161
+ const ActionWrapper = styled(Flex)`
1162
+ svg {
1163
+ height: ${4 / 16}rem;
1164
+ }
1165
+ `;
1166
+ const useReleasesList = (entryId) => {
1167
+ const { uid: contentTypeUid } = useTypedSelector(
1168
+ (state) => state["content-manager_listView"].contentType
1169
+ );
1170
+ const listViewData = useTypedSelector((state) => state["content-manager_listView"].data);
1171
+ const entriesIds = listViewData.map((entry) => entry.id);
1172
+ const response = useGetMappedEntriesInReleasesQuery(
1173
+ { contentTypeUid, entriesIds },
1174
+ { skip: !entriesIds || !contentTypeUid || entriesIds.length === 0 }
1175
+ );
1176
+ const mappedEntriesInReleases = response.data || {};
1177
+ return mappedEntriesInReleases?.[entryId] || [];
1178
+ };
1179
+ const addColumnToTableHook = ({ displayedHeaders, layout }) => {
1180
+ const { contentType } = layout;
1181
+ if (!contentType.options?.draftAndPublish) {
1182
+ return { displayedHeaders, layout };
1183
+ }
1184
+ return {
1185
+ displayedHeaders: [
1186
+ ...displayedHeaders,
1187
+ {
1188
+ key: "__release_key__",
1189
+ fieldSchema: { type: "string" },
1190
+ metadatas: { label: "To be released in", searchable: true, sortable: false },
1191
+ name: "releasedAt",
1192
+ cellFormatter: (props) => /* @__PURE__ */ jsx(ReleaseListCell, { ...props })
1193
+ }
1194
+ ],
1195
+ layout
1196
+ };
1197
+ };
1198
+ const ReleaseListCell = ({ id }) => {
1199
+ const releases = useReleasesList(id);
1200
+ const [visible, setVisible] = React.useState(false);
1201
+ const buttonRef = React.useRef(null);
1202
+ const { formatMessage } = useIntl();
1203
+ const handleTogglePopover = () => setVisible((prev) => !prev);
1204
+ return /* @__PURE__ */ jsx(Flex, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(Button, { type: "button", onClick: handleTogglePopover, ref: buttonRef, children: /* @__PURE__ */ jsxs(ActionWrapper, { height: "2rem", width: "2rem", children: [
1205
+ /* @__PURE__ */ jsx(Typography, { style: { maxWidth: "252px", cursor: "pointer" }, textColor: "neutral800", children: releases.length > 0 ? formatMessage(
1206
+ {
1207
+ id: "content-releases.content-manager.list-view.releases-number",
1208
+ defaultMessage: "{number} {number, plural, one {release} other {releases}}"
1209
+ },
1210
+ {
1211
+ number: releases.length
1212
+ }
1213
+ ) : "-" }),
1214
+ /* @__PURE__ */ jsxs(Flex, { children: [
1215
+ releases.length > 0 && /* @__PURE__ */ jsx(SortIcon, {}),
1216
+ visible && /* @__PURE__ */ jsx(
1217
+ Popover,
1218
+ {
1219
+ onDismiss: handleTogglePopover,
1220
+ source: buttonRef,
1221
+ spacing: 16,
1222
+ children: /* @__PURE__ */ jsx("ul", { children: releases.map(({ id: id2, name }) => /* @__PURE__ */ jsx(Box, { padding: 3, as: "li", children: /* @__PURE__ */ jsx(Link, { href: `/admin/plugins/content-releases/${id2}`, isExternal: false, children: name }) }, id2)) })
1223
+ }
1224
+ )
1225
+ ] })
1226
+ ] }) }) });
1227
+ };
845
1228
  const admin = {
846
1229
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
847
1230
  register(app) {
1231
+ app.createHook("ContentReleases/pages/ReleaseDetails/add-locale-in-releases");
848
1232
  if (window.strapi.features.isEnabled("cms-content-releases")) {
849
1233
  app.addMenuLink({
850
1234
  to: `/plugins/${pluginId}`,
@@ -854,7 +1238,7 @@ const admin = {
854
1238
  defaultMessage: "Releases"
855
1239
  },
856
1240
  async Component() {
857
- const { App } = await import("./App-3ycH2d3s.mjs");
1241
+ const { App } = await import("./App-gu1aiP6i.mjs");
858
1242
  return App;
859
1243
  },
860
1244
  permissions: PERMISSIONS.main
@@ -867,12 +1251,32 @@ const admin = {
867
1251
  name: `${pluginId}-link`,
868
1252
  Component: CMReleasesContainer
869
1253
  });
1254
+ app.plugins["content-manager"].apis.addBulkAction((actions) => {
1255
+ const deleteActionIndex = actions.findIndex((action) => action.name === "DeleteAction");
1256
+ actions.splice(deleteActionIndex, 0, ReleaseAction);
1257
+ return actions;
1258
+ });
1259
+ app.registerHook("Admin/CM/pages/ListView/inject-column-in-table", addColumnToTableHook);
1260
+ } else if (!window.strapi.features.isEnabled("cms-content-releases") && window.strapi?.flags?.promoteEE) {
1261
+ app.addMenuLink({
1262
+ to: `/plugins/purchase-content-releases`,
1263
+ icon: PaperPlane,
1264
+ intlLabel: {
1265
+ id: `${pluginId}.plugin.name`,
1266
+ defaultMessage: "Releases"
1267
+ },
1268
+ async Component() {
1269
+ const { PurchaseContentReleases } = await import("./PurchaseContentReleases-3tRbmbY3.mjs");
1270
+ return PurchaseContentReleases;
1271
+ },
1272
+ lockIcon: true
1273
+ });
870
1274
  }
871
1275
  },
872
1276
  async registerTrads({ locales }) {
873
1277
  const importedTrads = await Promise.all(
874
1278
  locales.map((locale) => {
875
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-SOqjCdyh.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
1279
+ return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-ltT1TlKQ.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
876
1280
  return {
877
1281
  data: prefixPluginTranslations(data, "content-releases"),
878
1282
  locale
@@ -891,19 +1295,20 @@ const admin = {
891
1295
  export {
892
1296
  PERMISSIONS as P,
893
1297
  ReleaseActionOptions as R,
894
- useUpdateReleaseMutation as a,
895
- useDeleteReleaseMutation as b,
896
- usePublishReleaseMutation as c,
897
- useTypedDispatch as d,
898
- useGetReleaseActionsQuery as e,
899
- useUpdateReleaseActionMutation as f,
900
- ReleaseActionMenu as g,
901
- useGetReleasesQuery as h,
1298
+ useCreateReleaseMutation as a,
1299
+ useGetReleaseQuery as b,
1300
+ useUpdateReleaseMutation as c,
1301
+ useDeleteReleaseMutation as d,
1302
+ usePublishReleaseMutation as e,
1303
+ useTypedDispatch as f,
1304
+ getTimezoneOffset as g,
1305
+ useGetReleaseActionsQuery as h,
902
1306
  isAxiosError as i,
903
- useCreateReleaseMutation as j,
904
- admin as k,
1307
+ useUpdateReleaseActionMutation as j,
1308
+ ReleaseActionMenu as k,
1309
+ admin as l,
905
1310
  pluginId as p,
906
1311
  releaseApi as r,
907
- useGetReleaseQuery as u
1312
+ useGetReleasesQuery as u
908
1313
  };
909
- //# sourceMappingURL=index-4gUWuCQV.mjs.map
1314
+ //# sourceMappingURL=index-mvj9PSKd.mjs.map