@strapi/content-releases 0.0.0-next.fc231041206e6f3999b094160cfa05db2892ad54 → 0.0.0-next.fe88c7878fe2948cfdc68f4867329c1032a618b2

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 (32) hide show
  1. package/LICENSE +17 -1
  2. package/dist/_chunks/{App-l62gIUTX.js → App-dLXY5ei3.js} +48 -31
  3. package/dist/_chunks/App-dLXY5ei3.js.map +1 -0
  4. package/dist/_chunks/{App-HVXzE3i3.mjs → App-jrh58sXY.mjs} +51 -34
  5. package/dist/_chunks/App-jrh58sXY.mjs.map +1 -0
  6. package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-3tRbmbY3.mjs} +2 -2
  7. package/dist/_chunks/PurchaseContentReleases-3tRbmbY3.mjs.map +1 -0
  8. package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases-bpIYXOfu.js} +2 -2
  9. package/dist/_chunks/PurchaseContentReleases-bpIYXOfu.js.map +1 -0
  10. package/dist/_chunks/{en-faJDuv3q.js → en-HrREghh3.js} +11 -2
  11. package/dist/_chunks/en-HrREghh3.js.map +1 -0
  12. package/dist/_chunks/{en-RdapH-9X.mjs → en-ltT1TlKQ.mjs} +11 -2
  13. package/dist/_chunks/en-ltT1TlKQ.mjs.map +1 -0
  14. package/dist/_chunks/{index-ML_b3php.js → index-CVO0Rqdm.js} +315 -13
  15. package/dist/_chunks/index-CVO0Rqdm.js.map +1 -0
  16. package/dist/_chunks/{index-Ys87ROOe.mjs → index-PiOGBETy.mjs} +320 -18
  17. package/dist/_chunks/index-PiOGBETy.mjs.map +1 -0
  18. package/dist/admin/index.js +1 -1
  19. package/dist/admin/index.mjs +1 -1
  20. package/dist/server/index.js +69 -7
  21. package/dist/server/index.js.map +1 -1
  22. package/dist/server/index.mjs +69 -7
  23. package/dist/server/index.mjs.map +1 -1
  24. package/package.json +13 -13
  25. package/dist/_chunks/App-HVXzE3i3.mjs.map +0 -1
  26. package/dist/_chunks/App-l62gIUTX.js.map +0 -1
  27. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
  28. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
  29. package/dist/_chunks/en-RdapH-9X.mjs.map +0 -1
  30. package/dist/_chunks/en-faJDuv3q.js.map +0 -1
  31. package/dist/_chunks/index-ML_b3php.js.map +0 -1
  32. package/dist/_chunks/index-Ys87ROOe.mjs.map +0 -1
@@ -159,7 +159,7 @@ const isAxiosError = (err) => {
159
159
  const releaseApi = react.createApi({
160
160
  reducerPath: pluginId,
161
161
  baseQuery: axiosBaseQuery,
162
- tagTypes: ["Release", "ReleaseAction"],
162
+ tagTypes: ["Release", "ReleaseAction", "EntriesInRelease"],
163
163
  endpoints: (build) => {
164
164
  return {
165
165
  getReleasesForEntry: build.query({
@@ -274,6 +274,20 @@ const releaseApi = react.createApi({
274
274
  { type: "ReleaseAction", id: "LIST" }
275
275
  ]
276
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
+ }),
277
291
  updateReleaseAction: build.mutation({
278
292
  query({ body, params }) {
279
293
  return {
@@ -314,7 +328,8 @@ const releaseApi = react.createApi({
314
328
  invalidatesTags: (result, error, arg) => [
315
329
  { type: "Release", id: "LIST" },
316
330
  { type: "Release", id: arg.params.releaseId },
317
- { type: "ReleaseAction", id: "LIST" }
331
+ { type: "ReleaseAction", id: "LIST" },
332
+ { type: "EntriesInRelease" }
318
333
  ]
319
334
  }),
320
335
  publishRelease: build.mutation({
@@ -333,7 +348,22 @@ const releaseApi = react.createApi({
333
348
  method: "DELETE"
334
349
  };
335
350
  },
336
- invalidatesTags: () => [{ type: "Release", id: "LIST" }]
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" }]
337
367
  })
338
368
  };
339
369
  }
@@ -345,11 +375,13 @@ const {
345
375
  useGetReleaseActionsQuery,
346
376
  useCreateReleaseMutation,
347
377
  useCreateReleaseActionMutation,
378
+ useCreateManyReleaseActionsMutation,
348
379
  useUpdateReleaseMutation,
349
380
  useUpdateReleaseActionMutation,
350
381
  usePublishReleaseMutation,
351
382
  useDeleteReleaseActionMutation,
352
- useDeleteReleaseMutation
383
+ useDeleteReleaseMutation,
384
+ useGetMappedEntriesInReleasesQuery
353
385
  } = releaseApi;
354
386
  const getTimezoneOffset = (timezone, date) => {
355
387
  try {
@@ -855,7 +887,7 @@ const CMReleasesContainer = () => {
855
887
  alignItems: "start",
856
888
  borderWidth: "1px",
857
889
  borderStyle: "solid",
858
- borderColor: getReleaseColorVariant(release.action.type, "200"),
890
+ borderColor: getReleaseColorVariant(release.actions[0].type, "200"),
859
891
  overflow: "hidden",
860
892
  hasRadius: true,
861
893
  children: [
@@ -866,20 +898,20 @@ const CMReleasesContainer = () => {
866
898
  paddingBottom: 3,
867
899
  paddingLeft: 4,
868
900
  paddingRight: 4,
869
- background: getReleaseColorVariant(release.action.type, "100"),
901
+ background: getReleaseColorVariant(release.actions[0].type, "100"),
870
902
  width: "100%",
871
903
  children: /* @__PURE__ */ jsxRuntime.jsx(
872
904
  designSystem.Typography,
873
905
  {
874
906
  fontSize: 1,
875
907
  variant: "pi",
876
- textColor: getReleaseColorVariant(release.action.type, "600"),
908
+ textColor: getReleaseColorVariant(release.actions[0].type, "600"),
877
909
  children: formatMessage(
878
910
  {
879
911
  id: "content-releases.content-manager-edit-view.list-releases.title",
880
912
  defaultMessage: "{isPublish, select, true {Will be published in} other {Will be unpublished in}}"
881
913
  },
882
- { isPublish: release.action.type === "publish" }
914
+ { isPublish: release.actions[0].type === "publish" }
883
915
  )
884
916
  }
885
917
  )
@@ -915,7 +947,7 @@ const CMReleasesContainer = () => {
915
947
  ReleaseActionMenu.DeleteReleaseActionItem,
916
948
  {
917
949
  releaseId: release.id,
918
- actionId: release.action.id
950
+ actionId: release.actions[0].id
919
951
  }
920
952
  )
921
953
  ] }) })
@@ -954,9 +986,272 @@ const CMReleasesContainer = () => {
954
986
  }
955
987
  ) });
956
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
+ };
957
1251
  const admin = {
958
1252
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
959
1253
  register(app) {
1254
+ app.createHook("ContentReleases/pages/ReleaseDetails/add-locale-in-releases");
960
1255
  if (window.strapi.features.isEnabled("cms-content-releases")) {
961
1256
  app.addMenuLink({
962
1257
  to: `/plugins/${pluginId}`,
@@ -966,7 +1261,7 @@ const admin = {
966
1261
  defaultMessage: "Releases"
967
1262
  },
968
1263
  async Component() {
969
- const { App } = await Promise.resolve().then(() => require("./App-l62gIUTX.js"));
1264
+ const { App } = await Promise.resolve().then(() => require("./App-dLXY5ei3.js"));
970
1265
  return App;
971
1266
  },
972
1267
  permissions: PERMISSIONS.main
@@ -979,6 +1274,12 @@ const admin = {
979
1274
  name: `${pluginId}-link`,
980
1275
  Component: CMReleasesContainer
981
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);
982
1283
  } else if (!window.strapi.features.isEnabled("cms-content-releases") && window.strapi?.flags?.promoteEE) {
983
1284
  app.addMenuLink({
984
1285
  to: `/plugins/purchase-content-releases`,
@@ -988,17 +1289,18 @@ const admin = {
988
1289
  defaultMessage: "Releases"
989
1290
  },
990
1291
  async Component() {
991
- const { PurchaseContentReleases } = await Promise.resolve().then(() => require("./PurchaseContentReleases-YhAPgpG9.js"));
1292
+ const { PurchaseContentReleases } = await Promise.resolve().then(() => require("./PurchaseContentReleases-bpIYXOfu.js"));
992
1293
  return PurchaseContentReleases;
993
1294
  },
994
1295
  lockIcon: true
1296
+ // TODO: to replace with another name in v5
995
1297
  });
996
1298
  }
997
1299
  },
998
1300
  async registerTrads({ locales }) {
999
1301
  const importedTrads = await Promise.all(
1000
1302
  locales.map((locale) => {
1001
- return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-faJDuv3q.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 }) => {
1002
1304
  return {
1003
1305
  data: helperPlugin.prefixPluginTranslations(data, "content-releases"),
1004
1306
  locale
@@ -1031,4 +1333,4 @@ exports.usePublishReleaseMutation = usePublishReleaseMutation;
1031
1333
  exports.useTypedDispatch = useTypedDispatch;
1032
1334
  exports.useUpdateReleaseActionMutation = useUpdateReleaseActionMutation;
1033
1335
  exports.useUpdateReleaseMutation = useUpdateReleaseMutation;
1034
- //# sourceMappingURL=index-ML_b3php.js.map
1336
+ //# sourceMappingURL=index-CVO0Rqdm.js.map