@strapi/content-releases 0.0.0-next.73143c28059b343ba62d98c29672ab114562fbbc → 0.0.0-next.734763e5757af27ff96ad1c9662161f3f677052a

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