@strapi/content-releases 0.0.0-experimental.d4cb32ce579e12a4436d68036f2327132fba1309 → 0.0.0-experimental.d5b46d578a5c055b8dcc66939e1b5d540976fafb

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-OK4Xac-O.js → App-OP70yd5M.js} +640 -617
  2. package/dist/_chunks/App-OP70yd5M.js.map +1 -0
  3. package/dist/_chunks/{App-xAkiD42p.mjs → App-x6Tjj3HN.mjs} +653 -630
  4. package/dist/_chunks/App-x6Tjj3HN.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-r0otWaln.js → en-3SGjiVyR.js} +13 -5
  10. package/dist/_chunks/en-3SGjiVyR.js.map +1 -0
  11. package/dist/_chunks/{en-veqvqeEr.mjs → en-bpHsnU0n.mjs} +13 -5
  12. package/dist/_chunks/en-bpHsnU0n.mjs.map +1 -0
  13. package/dist/_chunks/{index-exoiSU3V.mjs → index-1ejXLtzt.mjs} +235 -26
  14. package/dist/_chunks/index-1ejXLtzt.mjs.map +1 -0
  15. package/dist/_chunks/{index-JvA2_26n.js → index-ydocdaZ0.js} +224 -15
  16. package/dist/_chunks/index-ydocdaZ0.js.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 +554 -170
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +553 -170
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +12 -12
  24. package/dist/_chunks/App-OK4Xac-O.js.map +0 -1
  25. package/dist/_chunks/App-xAkiD42p.mjs.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-r0otWaln.js.map +0 -1
  29. package/dist/_chunks/en-veqvqeEr.mjs.map +0 -1
  30. package/dist/_chunks/index-JvA2_26n.js.map +0 -1
  31. package/dist/_chunks/index-exoiSU3V.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-JvA2_26n.js");
6
+ const index = require("./index-ydocdaZ0.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,137 +261,201 @@ 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
  },
271
+ onTextValueChange: (timezone) => {
272
+ setFieldValue("timezone", timezone);
273
+ },
276
274
  onClear: () => {
277
275
  setFieldValue("timezone", "");
278
276
  },
279
277
  error: errors.timezone,
280
278
  required: true,
281
- 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))
282
280
  }
283
281
  );
284
282
  };
285
- const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
286
- align-self: stretch;
287
- border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
288
- border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
289
- border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
290
- `;
291
- const StyledMenuItem = styled__default.default(v2.Menu.Item)`
292
- svg path {
293
- fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
294
- }
295
- span {
296
- color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
297
- }
283
+ const LinkCard = styled__default.default(v2.Link)`
284
+ display: block;
298
285
  `;
299
- const PencilIcon = styled__default.default(icons.Pencil)`
300
- width: ${({ theme }) => theme.spaces[3]};
301
- height: ${({ theme }) => theme.spaces[3]};
302
- path {
303
- fill: ${({ theme }) => theme.colors.neutral600};
286
+ const RelativeTime = styled__default.default(helperPlugin.RelativeTime)`
287
+ display: inline-block;
288
+ &::first-letter {
289
+ text-transform: uppercase;
304
290
  }
305
291
  `;
306
- const TrashIcon = styled__default.default(icons.Trash)`
307
- width: ${({ theme }) => theme.spaces[3]};
308
- height: ${({ theme }) => theme.spaces[3]};
309
- path {
310
- fill: ${({ theme }) => theme.colors.danger600};
292
+ const getBadgeProps = (status) => {
293
+ let color;
294
+ switch (status) {
295
+ case "ready":
296
+ color = "success";
297
+ break;
298
+ case "blocked":
299
+ color = "warning";
300
+ break;
301
+ case "failed":
302
+ color = "danger";
303
+ break;
304
+ case "done":
305
+ color = "primary";
306
+ break;
307
+ case "empty":
308
+ default:
309
+ color = "neutral";
311
310
  }
312
- `;
313
- const TypographyMaxWidth = styled__default.default(designSystem.Typography)`
314
- max-width: 300px;
315
- `;
316
- const EntryValidationText = ({ action, schema, components, entry }) => {
311
+ return {
312
+ textColor: `${color}600`,
313
+ backgroundColor: `${color}100`,
314
+ borderColor: `${color}200`
315
+ };
316
+ };
317
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
317
318
  const { formatMessage } = reactIntl.useIntl();
318
- const { validate } = strapiAdmin.unstable_useDocument();
319
- const { errors } = validate(entry, {
320
- contentType: schema,
321
- components,
322
- isCreatingEntry: false
323
- });
324
- if (Object.keys(errors).length > 0) {
325
- const validationErrorsMessages = Object.entries(errors).map(
326
- ([key, value]) => formatMessage(
327
- { id: `${value.id}.withField`, defaultMessage: value.defaultMessage },
328
- { field: key }
329
- )
330
- ).join(" ");
331
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
332
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "danger600", as: icons.CrossCircle }),
333
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsxRuntime.jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
334
- ] });
319
+ if (isError) {
320
+ return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
335
321
  }
336
- if (action == "publish") {
337
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
338
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
339
- entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
340
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
341
- defaultMessage: "Already published"
342
- }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
343
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
344
- defaultMessage: "Ready to publish"
345
- }) })
346
- ] });
322
+ if (releases?.length === 0) {
323
+ return /* @__PURE__ */ jsxRuntime.jsx(
324
+ designSystem.EmptyStateLayout,
325
+ {
326
+ content: formatMessage(
327
+ {
328
+ id: "content-releases.page.Releases.tab.emptyEntries",
329
+ defaultMessage: "No releases"
330
+ },
331
+ {
332
+ target: sectionTitle
333
+ }
334
+ ),
335
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.EmptyDocuments, { width: "10rem" })
336
+ }
337
+ );
347
338
  }
348
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
349
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
350
- !entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
351
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
352
- defaultMessage: "Already unpublished"
353
- }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
354
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
355
- defaultMessage: "Ready to unpublish"
356
- }) })
357
- ] });
339
+ 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(
340
+ designSystem.Flex,
341
+ {
342
+ direction: "column",
343
+ justifyContent: "space-between",
344
+ padding: 4,
345
+ hasRadius: true,
346
+ background: "neutral0",
347
+ shadow: "tableShadow",
348
+ height: "100%",
349
+ width: "100%",
350
+ alignItems: "start",
351
+ gap: 4,
352
+ children: [
353
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "start", gap: 1, children: [
354
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
355
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: scheduledAt ? /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
356
+ id: "content-releases.pages.Releases.not-scheduled",
357
+ defaultMessage: "Not scheduled"
358
+ }) })
359
+ ] }),
360
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { ...getBadgeProps(status), children: status })
361
+ ]
362
+ }
363
+ ) }) }, id)) });
358
364
  };
359
- const ReleaseDetailsLayout = ({
360
- toggleEditReleaseModal,
361
- toggleWarningSubmit,
362
- children
363
- }) => {
364
- const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
365
- const { releaseId } = reactRouterDom.useParams();
366
- const {
367
- data,
368
- isLoading: isLoadingDetails,
369
- isError,
370
- error
371
- } = index.useGetReleaseQuery({ id: releaseId });
372
- const [publishRelease, { isLoading: isPublishing }] = index.usePublishReleaseMutation();
365
+ const StyledAlert = styled__default.default(designSystem.Alert)`
366
+ button {
367
+ display: none;
368
+ }
369
+ p + div {
370
+ margin-left: auto;
371
+ }
372
+ `;
373
+ const INITIAL_FORM_VALUES = {
374
+ name: "",
375
+ date: null,
376
+ time: "",
377
+ isScheduled: true,
378
+ scheduledAt: null,
379
+ timezone: null
380
+ };
381
+ const ReleasesPage = () => {
382
+ const tabRef = React__namespace.useRef(null);
383
+ const location = reactRouterDom.useLocation();
384
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
373
385
  const toggleNotification = helperPlugin.useNotification();
386
+ const { formatMessage } = reactIntl.useIntl();
387
+ const { push, replace } = reactRouterDom.useHistory();
374
388
  const { formatAPIError } = helperPlugin.useAPIErrorHandler();
375
- const {
376
- allowedActions: { canUpdate, canDelete }
377
- } = helperPlugin.useRBAC(index.PERMISSIONS);
378
- const dispatch = index.useTypedDispatch();
389
+ const [{ query }, setQuery] = helperPlugin.useQueryParams();
390
+ const response = index.useGetReleasesQuery(query);
391
+ const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
392
+ const { getFeature } = strapiAdmin.useLicenseLimits();
393
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
379
394
  const { trackUsage } = helperPlugin.useTracking();
380
- const release = data?.data;
381
- const handlePublishRelease = async () => {
382
- const response = await publishRelease({ id: releaseId });
383
- if ("data" in response) {
395
+ const { isLoading, isSuccess, isError } = response;
396
+ const activeTab = response?.currentData?.meta?.activeTab || "pending";
397
+ const activeTabIndex = ["pending", "done"].indexOf(activeTab);
398
+ React__namespace.useEffect(() => {
399
+ if (location?.state?.errors) {
384
400
  toggleNotification({
385
- type: "success",
401
+ type: "warning",
402
+ title: formatMessage({
403
+ id: "content-releases.pages.Releases.notification.error.title",
404
+ defaultMessage: "Your request could not be processed."
405
+ }),
386
406
  message: formatMessage({
387
- id: "content-releases.pages.ReleaseDetails.publish-notification-success",
388
- defaultMessage: "Release was published successfully."
407
+ id: "content-releases.pages.Releases.notification.error.message",
408
+ defaultMessage: "Please try again or open another release."
389
409
  })
390
410
  });
391
- const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
392
- trackUsage("didPublishRelease", {
393
- totalEntries: totalEntries2,
394
- totalPublishedEntries,
395
- totalUnpublishedEntries
411
+ replace({ state: null });
412
+ }
413
+ }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
414
+ React__namespace.useEffect(() => {
415
+ if (tabRef.current) {
416
+ tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
417
+ }
418
+ }, [activeTabIndex]);
419
+ const toggleAddReleaseModal = () => {
420
+ setReleaseModalShown((prev) => !prev);
421
+ };
422
+ if (isLoading) {
423
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
424
+ }
425
+ const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
426
+ const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
427
+ const handleTabChange = (index2) => {
428
+ setQuery({
429
+ ...query,
430
+ page: 1,
431
+ pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
432
+ filters: {
433
+ releasedAt: {
434
+ $notNull: index2 === 0 ? false : true
435
+ }
436
+ }
437
+ });
438
+ };
439
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
440
+ const response2 = await createRelease({
441
+ name,
442
+ scheduledAt,
443
+ timezone
444
+ });
445
+ if ("data" in response2) {
446
+ toggleNotification({
447
+ type: "success",
448
+ message: formatMessage({
449
+ id: "content-releases.modal.release-created-notification-success",
450
+ defaultMessage: "Release created."
451
+ })
396
452
  });
397
- } else if (index.isAxiosError(response.error)) {
453
+ trackUsage("didCreateRelease");
454
+ push(`/plugins/content-releases/${response2.data.data.id}`);
455
+ } else if (index.isAxiosError(response2.error)) {
398
456
  toggleNotification({
399
457
  type: "warning",
400
- message: formatAPIError(response.error)
458
+ message: formatAPIError(response2.error)
401
459
  });
402
460
  } else {
403
461
  toggleNotification({
@@ -406,19 +464,283 @@ const ReleaseDetailsLayout = ({
406
464
  });
407
465
  }
408
466
  };
409
- const handleRefresh = () => {
410
- dispatch(index.releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
411
- };
412
- const getCreatedByUser = () => {
413
- if (!release?.createdBy) {
414
- return null;
415
- }
416
- if (release.createdBy.username) {
417
- return release.createdBy.username;
418
- }
419
- if (release.createdBy.firstname) {
420
- return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
421
- }
467
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
468
+ /* @__PURE__ */ jsxRuntime.jsx(
469
+ designSystem.HeaderLayout,
470
+ {
471
+ title: formatMessage({
472
+ id: "content-releases.pages.Releases.title",
473
+ defaultMessage: "Releases"
474
+ }),
475
+ subtitle: formatMessage({
476
+ id: "content-releases.pages.Releases.header-subtitle",
477
+ defaultMessage: "Create and manage content updates"
478
+ }),
479
+ primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
480
+ designSystem.Button,
481
+ {
482
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
483
+ onClick: toggleAddReleaseModal,
484
+ disabled: hasReachedMaximumPendingReleases,
485
+ children: formatMessage({
486
+ id: "content-releases.header.actions.add-release",
487
+ defaultMessage: "New release"
488
+ })
489
+ }
490
+ ) })
491
+ }
492
+ ),
493
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
494
+ hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
495
+ StyledAlert,
496
+ {
497
+ marginBottom: 6,
498
+ action: /* @__PURE__ */ jsxRuntime.jsx(v2.Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
499
+ id: "content-releases.pages.Releases.max-limit-reached.action",
500
+ defaultMessage: "Explore plans"
501
+ }) }),
502
+ title: formatMessage(
503
+ {
504
+ id: "content-releases.pages.Releases.max-limit-reached.title",
505
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
506
+ },
507
+ { number: maximumReleases }
508
+ ),
509
+ onClose: () => {
510
+ },
511
+ closeLabel: "",
512
+ children: formatMessage({
513
+ id: "content-releases.pages.Releases.max-limit-reached.message",
514
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
515
+ })
516
+ }
517
+ ),
518
+ /* @__PURE__ */ jsxRuntime.jsxs(
519
+ designSystem.TabGroup,
520
+ {
521
+ label: formatMessage({
522
+ id: "content-releases.pages.Releases.tab-group.label",
523
+ defaultMessage: "Releases list"
524
+ }),
525
+ variant: "simple",
526
+ initialSelectedTabIndex: activeTabIndex,
527
+ onTabChange: handleTabChange,
528
+ ref: tabRef,
529
+ children: [
530
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingBottom: 8, children: [
531
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs, { children: [
532
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage(
533
+ {
534
+ id: "content-releases.pages.Releases.tab.pending",
535
+ defaultMessage: "Pending ({count})"
536
+ },
537
+ {
538
+ count: totalPendingReleases
539
+ }
540
+ ) }),
541
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
542
+ id: "content-releases.pages.Releases.tab.done",
543
+ defaultMessage: "Done"
544
+ }) })
545
+ ] }),
546
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {})
547
+ ] }),
548
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.TabPanels, { children: [
549
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
550
+ ReleasesGrid,
551
+ {
552
+ sectionTitle: "pending",
553
+ releases: response?.currentData?.data,
554
+ isError
555
+ }
556
+ ) }),
557
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
558
+ ReleasesGrid,
559
+ {
560
+ sectionTitle: "done",
561
+ releases: response?.currentData?.data,
562
+ isError
563
+ }
564
+ ) })
565
+ ] })
566
+ ]
567
+ }
568
+ ),
569
+ response.currentData?.meta?.pagination?.total ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
570
+ /* @__PURE__ */ jsxRuntime.jsx(
571
+ helperPlugin.PageSizeURLQuery,
572
+ {
573
+ options: ["8", "16", "32", "64"],
574
+ defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
575
+ }
576
+ ),
577
+ /* @__PURE__ */ jsxRuntime.jsx(
578
+ helperPlugin.PaginationURLQuery,
579
+ {
580
+ pagination: {
581
+ pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
582
+ }
583
+ }
584
+ )
585
+ ] }) : null
586
+ ] }) }),
587
+ releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
588
+ ReleaseModal,
589
+ {
590
+ handleClose: toggleAddReleaseModal,
591
+ handleSubmit: handleAddRelease,
592
+ isLoading: isSubmittingForm,
593
+ initialValues: INITIAL_FORM_VALUES
594
+ }
595
+ )
596
+ ] });
597
+ };
598
+ const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
599
+ align-self: stretch;
600
+ border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
601
+ border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
602
+ border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
603
+ `;
604
+ const StyledMenuItem = styled__default.default(v2.Menu.Item)`
605
+ svg path {
606
+ fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
607
+ }
608
+ span {
609
+ color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
610
+ }
611
+
612
+ &:hover {
613
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
614
+ }
615
+ `;
616
+ const PencilIcon = styled__default.default(icons.Pencil)`
617
+ width: ${({ theme }) => theme.spaces[3]};
618
+ height: ${({ theme }) => theme.spaces[3]};
619
+ path {
620
+ fill: ${({ theme }) => theme.colors.neutral600};
621
+ }
622
+ `;
623
+ const TrashIcon = styled__default.default(icons.Trash)`
624
+ width: ${({ theme }) => theme.spaces[3]};
625
+ height: ${({ theme }) => theme.spaces[3]};
626
+ path {
627
+ fill: ${({ theme }) => theme.colors.danger600};
628
+ }
629
+ `;
630
+ const TypographyMaxWidth = styled__default.default(designSystem.Typography)`
631
+ max-width: 300px;
632
+ `;
633
+ const EntryValidationText = ({ action, schema, components, entry }) => {
634
+ const { formatMessage } = reactIntl.useIntl();
635
+ const { validate } = strapiAdmin.unstable_useDocument();
636
+ const { errors } = validate(entry, {
637
+ contentType: schema,
638
+ components,
639
+ isCreatingEntry: false
640
+ });
641
+ if (Object.keys(errors).length > 0) {
642
+ const validationErrorsMessages = Object.entries(errors).map(
643
+ ([key, value]) => formatMessage(
644
+ { id: `${value.id}.withField`, defaultMessage: value.defaultMessage },
645
+ { field: key }
646
+ )
647
+ ).join(" ");
648
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
649
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "danger600", as: icons.CrossCircle }),
650
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsxRuntime.jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
651
+ ] });
652
+ }
653
+ if (action == "publish") {
654
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
655
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
656
+ entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
657
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
658
+ defaultMessage: "Already published"
659
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
660
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
661
+ defaultMessage: "Ready to publish"
662
+ }) })
663
+ ] });
664
+ }
665
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
666
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
667
+ !entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
668
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
669
+ defaultMessage: "Already unpublished"
670
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
671
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
672
+ defaultMessage: "Ready to unpublish"
673
+ }) })
674
+ ] });
675
+ };
676
+ const ReleaseDetailsLayout = ({
677
+ toggleEditReleaseModal,
678
+ toggleWarningSubmit,
679
+ children
680
+ }) => {
681
+ const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
682
+ const { releaseId } = reactRouterDom.useParams();
683
+ const {
684
+ data,
685
+ isLoading: isLoadingDetails,
686
+ isError,
687
+ error
688
+ } = index.useGetReleaseQuery({ id: releaseId });
689
+ const [publishRelease, { isLoading: isPublishing }] = index.usePublishReleaseMutation();
690
+ const toggleNotification = helperPlugin.useNotification();
691
+ const { formatAPIError } = helperPlugin.useAPIErrorHandler();
692
+ const {
693
+ allowedActions: { canUpdate, canDelete }
694
+ } = helperPlugin.useRBAC(index.PERMISSIONS);
695
+ const dispatch = index.useTypedDispatch();
696
+ const { trackUsage } = helperPlugin.useTracking();
697
+ const release = data?.data;
698
+ const handlePublishRelease = async () => {
699
+ const response = await publishRelease({ id: releaseId });
700
+ if ("data" in response) {
701
+ toggleNotification({
702
+ type: "success",
703
+ message: formatMessage({
704
+ id: "content-releases.pages.ReleaseDetails.publish-notification-success",
705
+ defaultMessage: "Release was published successfully."
706
+ })
707
+ });
708
+ const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
709
+ trackUsage("didPublishRelease", {
710
+ totalEntries: totalEntries2,
711
+ totalPublishedEntries,
712
+ totalUnpublishedEntries
713
+ });
714
+ } else if (index.isAxiosError(response.error)) {
715
+ toggleNotification({
716
+ type: "warning",
717
+ message: formatAPIError(response.error)
718
+ });
719
+ } else {
720
+ toggleNotification({
721
+ type: "warning",
722
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
723
+ });
724
+ }
725
+ };
726
+ const handleRefresh = () => {
727
+ dispatch(
728
+ index.releaseApi.util.invalidateTags([
729
+ { type: "ReleaseAction", id: "LIST" },
730
+ { type: "Release", id: releaseId }
731
+ ])
732
+ );
733
+ };
734
+ const getCreatedByUser = () => {
735
+ if (!release?.createdBy) {
736
+ return null;
737
+ }
738
+ if (release.createdBy.username) {
739
+ return release.createdBy.username;
740
+ }
741
+ if (release.createdBy.firstname) {
742
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
743
+ }
422
744
  return release.createdBy.email;
423
745
  };
424
746
  if (isLoadingDetails) {
@@ -443,7 +765,6 @@ const ReleaseDetailsLayout = ({
443
765
  }
444
766
  const totalEntries = release.actions.meta.count || 0;
445
767
  const hasCreatedByUser = Boolean(getCreatedByUser());
446
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
447
768
  const isScheduled = release.scheduledAt && release.timezone;
448
769
  const numberOfEntriesText = formatMessage(
449
770
  {
@@ -477,7 +798,10 @@ const ReleaseDetailsLayout = ({
477
798
  designSystem.HeaderLayout,
478
799
  {
479
800
  title: release.name,
480
- subtitle: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? ` - ${scheduledText}` : ""),
801
+ subtitle: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, lineHeight: 6, children: [
802
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", variant: "epsilon", children: numberOfEntriesText + (isScheduled ? ` - ${scheduledText}` : "") }),
803
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { ...getBadgeProps(release.status), children: release.status })
804
+ ] }),
481
805
  navigationAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
482
806
  id: "global.back",
483
807
  defaultMessage: "Back"
@@ -508,42 +832,28 @@ const ReleaseDetailsLayout = ({
508
832
  padding: 1,
509
833
  width: "100%",
510
834
  children: [
511
- /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(
512
- designSystem.Flex,
513
- {
514
- paddingTop: 2,
515
- paddingBottom: 2,
516
- alignItems: "center",
517
- gap: 2,
518
- hasRadius: true,
519
- width: "100%",
520
- children: [
521
- /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
522
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
523
- id: "content-releases.header.actions.edit",
524
- defaultMessage: "Edit"
525
- }) })
526
- ]
527
- }
528
- ) }),
529
- /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxRuntime.jsxs(
530
- designSystem.Flex,
835
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
836
+ /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
837
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
838
+ id: "content-releases.header.actions.edit",
839
+ defaultMessage: "Edit"
840
+ }) })
841
+ ] }) }),
842
+ /* @__PURE__ */ jsxRuntime.jsx(
843
+ StyledMenuItem,
531
844
  {
532
- paddingTop: 2,
533
- paddingBottom: 2,
534
- alignItems: "center",
535
- gap: 2,
536
- hasRadius: true,
537
- width: "100%",
538
- children: [
845
+ disabled: !canDelete,
846
+ onSelect: toggleWarningSubmit,
847
+ variant: "danger",
848
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
539
849
  /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
540
850
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
541
851
  id: "content-releases.header.actions.delete",
542
852
  defaultMessage: "Delete"
543
853
  }) })
544
- ]
854
+ ] })
545
855
  }
546
- ) })
856
+ )
547
857
  ]
548
858
  }
549
859
  ),
@@ -899,275 +1209,59 @@ const ReleaseDetailsBody = () => {
899
1209
  };
900
1210
  const ReleaseDetailsPage = () => {
901
1211
  const { formatMessage } = reactIntl.useIntl();
902
- const { releaseId } = reactRouterDom.useParams();
903
- const toggleNotification = helperPlugin.useNotification();
904
- const { formatAPIError } = helperPlugin.useAPIErrorHandler();
905
- const { push } = reactRouterDom.useHistory();
906
- const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
907
- const [showWarningSubmit, setWarningSubmit] = React__namespace.useState(false);
908
- const {
909
- isLoading: isLoadingDetails,
910
- data,
911
- isSuccess: isSuccessDetails
912
- } = index.useGetReleaseQuery({ id: releaseId });
913
- const [updateRelease, { isLoading: isSubmittingForm }] = index.useUpdateReleaseMutation();
914
- const [deleteRelease, { isLoading: isDeletingRelease }] = index.useDeleteReleaseMutation();
915
- const toggleEditReleaseModal = () => {
916
- setReleaseModalShown((prev) => !prev);
917
- };
918
- const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
919
- if (isLoadingDetails) {
920
- return /* @__PURE__ */ jsxRuntime.jsx(
921
- ReleaseDetailsLayout,
922
- {
923
- toggleEditReleaseModal,
924
- toggleWarningSubmit,
925
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) })
926
- }
927
- );
928
- }
929
- const releaseData = isSuccessDetails && data?.data || null;
930
- const title = releaseData?.name || "";
931
- const timezone = releaseData?.timezone ?? null;
932
- const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
933
- const date = scheduledAt ? new Date(format__default.default(scheduledAt, "yyyy-MM-dd")) : null;
934
- const time = scheduledAt ? format__default.default(scheduledAt, "HH:mm") : "";
935
- const handleEditRelease = async (values) => {
936
- const response = await updateRelease({
937
- id: releaseId,
938
- name: values.name,
939
- scheduledAt: values.scheduledAt,
940
- timezone: values.timezone
941
- });
942
- if ("data" in response) {
943
- toggleNotification({
944
- type: "success",
945
- message: formatMessage({
946
- id: "content-releases.modal.release-updated-notification-success",
947
- defaultMessage: "Release updated."
948
- })
949
- });
950
- } else if (index.isAxiosError(response.error)) {
951
- toggleNotification({
952
- type: "warning",
953
- message: formatAPIError(response.error)
954
- });
955
- } else {
956
- toggleNotification({
957
- type: "warning",
958
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
959
- });
960
- }
961
- toggleEditReleaseModal();
962
- };
963
- const handleDeleteRelease = async () => {
964
- const response = await deleteRelease({
965
- id: releaseId
966
- });
967
- if ("data" in response) {
968
- push("/plugins/content-releases");
969
- } else if (index.isAxiosError(response.error)) {
970
- toggleNotification({
971
- type: "warning",
972
- message: formatAPIError(response.error)
973
- });
974
- } else {
975
- toggleNotification({
976
- type: "warning",
977
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
978
- });
979
- }
980
- };
981
- return /* @__PURE__ */ jsxRuntime.jsxs(
982
- ReleaseDetailsLayout,
983
- {
984
- toggleEditReleaseModal,
985
- toggleWarningSubmit,
986
- children: [
987
- /* @__PURE__ */ jsxRuntime.jsx(ReleaseDetailsBody, {}),
988
- releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
989
- ReleaseModal,
990
- {
991
- handleClose: toggleEditReleaseModal,
992
- handleSubmit: handleEditRelease,
993
- isLoading: isLoadingDetails || isSubmittingForm,
994
- initialValues: {
995
- name: title || "",
996
- scheduledAt,
997
- date,
998
- time,
999
- isScheduled: Boolean(scheduledAt),
1000
- timezone
1001
- }
1002
- }
1003
- ),
1004
- /* @__PURE__ */ jsxRuntime.jsx(
1005
- helperPlugin.ConfirmDialog,
1006
- {
1007
- bodyText: {
1008
- id: "content-releases.dialog.confirmation-message",
1009
- defaultMessage: "Are you sure you want to delete this release?"
1010
- },
1011
- isOpen: showWarningSubmit,
1012
- isConfirmButtonLoading: isDeletingRelease,
1013
- onToggleDialog: toggleWarningSubmit,
1014
- onConfirm: handleDeleteRelease
1015
- }
1016
- )
1017
- ]
1018
- }
1019
- );
1020
- };
1021
- const LinkCard = styled__default.default(v2.Link)`
1022
- display: block;
1023
- `;
1024
- const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
1025
- const { formatMessage } = reactIntl.useIntl();
1026
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
1027
- if (isError) {
1028
- return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
1029
- }
1030
- if (releases?.length === 0) {
1031
- return /* @__PURE__ */ jsxRuntime.jsx(
1032
- designSystem.EmptyStateLayout,
1033
- {
1034
- content: formatMessage(
1035
- {
1036
- id: "content-releases.page.Releases.tab.emptyEntries",
1037
- defaultMessage: "No releases"
1038
- },
1039
- {
1040
- target: sectionTitle
1041
- }
1042
- ),
1043
- icon: /* @__PURE__ */ jsxRuntime.jsx(icons.EmptyDocuments, { width: "10rem" })
1044
- }
1045
- );
1046
- }
1047
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid, { gap: 4, children: releases.map(({ id, name, actions, scheduledAt }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxRuntime.jsxs(
1048
- designSystem.Flex,
1049
- {
1050
- direction: "column",
1051
- justifyContent: "space-between",
1052
- padding: 4,
1053
- hasRadius: true,
1054
- background: "neutral0",
1055
- shadow: "tableShadow",
1056
- height: "100%",
1057
- width: "100%",
1058
- alignItems: "start",
1059
- gap: 2,
1060
- children: [
1061
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
1062
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: IsSchedulingEnabled ? scheduledAt ? /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
1063
- id: "content-releases.pages.Releases.not-scheduled",
1064
- defaultMessage: "Not scheduled"
1065
- }) : formatMessage(
1066
- {
1067
- id: "content-releases.page.Releases.release-item.entries",
1068
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
1069
- },
1070
- { number: actions.meta.count }
1071
- ) })
1072
- ]
1073
- }
1074
- ) }) }, id)) });
1075
- };
1076
- const StyledAlert = styled__default.default(designSystem.Alert)`
1077
- button {
1078
- display: none;
1079
- }
1080
- p + div {
1081
- margin-left: auto;
1082
- }
1083
- `;
1084
- const INITIAL_FORM_VALUES = {
1085
- name: "",
1086
- date: null,
1087
- time: "",
1088
- // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
1089
- isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
1090
- scheduledAt: null,
1091
- timezone: null
1092
- };
1093
- const ReleasesPage = () => {
1094
- const tabRef = React__namespace.useRef(null);
1095
- const location = reactRouterDom.useLocation();
1096
- const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
1097
- const toggleNotification = helperPlugin.useNotification();
1098
- const { formatMessage } = reactIntl.useIntl();
1099
- const { push, replace } = reactRouterDom.useHistory();
1212
+ const { releaseId } = reactRouterDom.useParams();
1213
+ const toggleNotification = helperPlugin.useNotification();
1100
1214
  const { formatAPIError } = helperPlugin.useAPIErrorHandler();
1101
- const [{ query }, setQuery] = helperPlugin.useQueryParams();
1102
- const response = index.useGetReleasesQuery(query);
1103
- const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
1104
- const { getFeature } = strapiAdmin.useLicenseLimits();
1105
- const { maximumReleases = 3 } = getFeature("cms-content-releases");
1106
- const { trackUsage } = helperPlugin.useTracking();
1107
- const { isLoading, isSuccess, isError } = response;
1108
- const activeTab = response?.currentData?.meta?.activeTab || "pending";
1109
- const activeTabIndex = ["pending", "done"].indexOf(activeTab);
1110
- React__namespace.useEffect(() => {
1111
- if (location?.state?.errors) {
1112
- toggleNotification({
1113
- type: "warning",
1114
- title: formatMessage({
1115
- id: "content-releases.pages.Releases.notification.error.title",
1116
- defaultMessage: "Your request could not be processed."
1117
- }),
1118
- message: formatMessage({
1119
- id: "content-releases.pages.Releases.notification.error.message",
1120
- defaultMessage: "Please try again or open another release."
1121
- })
1122
- });
1123
- replace({ state: null });
1124
- }
1125
- }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
1126
- React__namespace.useEffect(() => {
1127
- if (tabRef.current) {
1128
- tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
1129
- }
1130
- }, [activeTabIndex]);
1131
- const toggleAddReleaseModal = () => {
1215
+ const { replace } = reactRouterDom.useHistory();
1216
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
1217
+ const [showWarningSubmit, setWarningSubmit] = React__namespace.useState(false);
1218
+ const {
1219
+ isLoading: isLoadingDetails,
1220
+ data,
1221
+ isSuccess: isSuccessDetails
1222
+ } = index.useGetReleaseQuery({ id: releaseId });
1223
+ const [updateRelease, { isLoading: isSubmittingForm }] = index.useUpdateReleaseMutation();
1224
+ const [deleteRelease, { isLoading: isDeletingRelease }] = index.useDeleteReleaseMutation();
1225
+ const toggleEditReleaseModal = () => {
1132
1226
  setReleaseModalShown((prev) => !prev);
1133
1227
  };
1134
- if (isLoading) {
1135
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
1136
- }
1137
- const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
1138
- const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
1139
- const handleTabChange = (index2) => {
1140
- setQuery({
1141
- ...query,
1142
- page: 1,
1143
- pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
1144
- filters: {
1145
- releasedAt: {
1146
- $notNull: index2 === 0 ? false : true
1147
- }
1228
+ const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
1229
+ if (isLoadingDetails) {
1230
+ return /* @__PURE__ */ jsxRuntime.jsx(
1231
+ ReleaseDetailsLayout,
1232
+ {
1233
+ toggleEditReleaseModal,
1234
+ toggleWarningSubmit,
1235
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) })
1148
1236
  }
1237
+ );
1238
+ }
1239
+ const releaseData = isSuccessDetails && data?.data || null;
1240
+ const title = releaseData?.name || "";
1241
+ const timezone = releaseData?.timezone ?? null;
1242
+ const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
1243
+ const date = scheduledAt ? format__default.default(scheduledAt, "yyyy-MM-dd") : null;
1244
+ const time = scheduledAt ? format__default.default(scheduledAt, "HH:mm") : "";
1245
+ const handleEditRelease = async (values) => {
1246
+ const response = await updateRelease({
1247
+ id: releaseId,
1248
+ name: values.name,
1249
+ scheduledAt: values.scheduledAt,
1250
+ timezone: values.timezone
1149
1251
  });
1150
- };
1151
- const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
1152
- const response2 = await createRelease({
1153
- name,
1154
- scheduledAt,
1155
- timezone
1156
- });
1157
- if ("data" in response2) {
1252
+ if ("data" in response) {
1158
1253
  toggleNotification({
1159
1254
  type: "success",
1160
1255
  message: formatMessage({
1161
- id: "content-releases.modal.release-created-notification-success",
1162
- defaultMessage: "Release created."
1256
+ id: "content-releases.modal.release-updated-notification-success",
1257
+ defaultMessage: "Release updated."
1163
1258
  })
1164
1259
  });
1165
- trackUsage("didCreateRelease");
1166
- push(`/plugins/content-releases/${response2.data.data.id}`);
1167
- } else if (index.isAxiosError(response2.error)) {
1260
+ toggleEditReleaseModal();
1261
+ } else if (index.isAxiosError(response.error)) {
1168
1262
  toggleNotification({
1169
1263
  type: "warning",
1170
- message: formatAPIError(response2.error)
1264
+ message: formatAPIError(response.error)
1171
1265
  });
1172
1266
  } else {
1173
1267
  toggleNotification({
@@ -1176,134 +1270,63 @@ const ReleasesPage = () => {
1176
1270
  });
1177
1271
  }
1178
1272
  };
1179
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
1180
- /* @__PURE__ */ jsxRuntime.jsx(
1181
- designSystem.HeaderLayout,
1182
- {
1183
- title: formatMessage({
1184
- id: "content-releases.pages.Releases.title",
1185
- defaultMessage: "Releases"
1186
- }),
1187
- subtitle: formatMessage(
1188
- {
1189
- id: "content-releases.pages.Releases.header-subtitle",
1190
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
1191
- },
1192
- { number: totalReleases }
1193
- ),
1194
- primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
1195
- designSystem.Button,
1196
- {
1197
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1198
- onClick: toggleAddReleaseModal,
1199
- disabled: hasReachedMaximumPendingReleases,
1200
- children: formatMessage({
1201
- id: "content-releases.header.actions.add-release",
1202
- defaultMessage: "New release"
1203
- })
1204
- }
1205
- ) })
1206
- }
1207
- ),
1208
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1209
- activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
1210
- StyledAlert,
1211
- {
1212
- marginBottom: 6,
1213
- action: /* @__PURE__ */ jsxRuntime.jsx(v2.Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
1214
- id: "content-releases.pages.Releases.max-limit-reached.action",
1215
- defaultMessage: "Explore plans"
1216
- }) }),
1217
- title: formatMessage(
1218
- {
1219
- id: "content-releases.pages.Releases.max-limit-reached.title",
1220
- defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
1221
- },
1222
- { number: maximumReleases }
1223
- ),
1224
- onClose: () => {
1225
- },
1226
- closeLabel: "",
1227
- children: formatMessage({
1228
- id: "content-releases.pages.Releases.max-limit-reached.message",
1229
- defaultMessage: "Upgrade to manage an unlimited number of releases."
1230
- })
1231
- }
1232
- ),
1233
- /* @__PURE__ */ jsxRuntime.jsxs(
1234
- designSystem.TabGroup,
1235
- {
1236
- label: formatMessage({
1237
- id: "content-releases.pages.Releases.tab-group.label",
1238
- defaultMessage: "Releases list"
1239
- }),
1240
- variant: "simple",
1241
- initialSelectedTabIndex: activeTabIndex,
1242
- onTabChange: handleTabChange,
1243
- ref: tabRef,
1244
- children: [
1245
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingBottom: 8, children: [
1246
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs, { children: [
1247
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1248
- id: "content-releases.pages.Releases.tab.pending",
1249
- defaultMessage: "Pending"
1250
- }) }),
1251
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1252
- id: "content-releases.pages.Releases.tab.done",
1253
- defaultMessage: "Done"
1254
- }) })
1255
- ] }),
1256
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {})
1257
- ] }),
1258
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.TabPanels, { children: [
1259
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
1260
- ReleasesGrid,
1261
- {
1262
- sectionTitle: "pending",
1263
- releases: response?.currentData?.data,
1264
- isError
1265
- }
1266
- ) }),
1267
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
1268
- ReleasesGrid,
1269
- {
1270
- sectionTitle: "done",
1271
- releases: response?.currentData?.data,
1272
- isError
1273
- }
1274
- ) })
1275
- ] })
1276
- ]
1277
- }
1278
- ),
1279
- totalReleases > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1280
- /* @__PURE__ */ jsxRuntime.jsx(
1281
- helperPlugin.PageSizeURLQuery,
1273
+ const handleDeleteRelease = async () => {
1274
+ const response = await deleteRelease({
1275
+ id: releaseId
1276
+ });
1277
+ if ("data" in response) {
1278
+ replace("/plugins/content-releases");
1279
+ } else if (index.isAxiosError(response.error)) {
1280
+ toggleNotification({
1281
+ type: "warning",
1282
+ message: formatAPIError(response.error)
1283
+ });
1284
+ } else {
1285
+ toggleNotification({
1286
+ type: "warning",
1287
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1288
+ });
1289
+ }
1290
+ };
1291
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1292
+ ReleaseDetailsLayout,
1293
+ {
1294
+ toggleEditReleaseModal,
1295
+ toggleWarningSubmit,
1296
+ children: [
1297
+ /* @__PURE__ */ jsxRuntime.jsx(ReleaseDetailsBody, {}),
1298
+ releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
1299
+ ReleaseModal,
1282
1300
  {
1283
- options: ["8", "16", "32", "64"],
1284
- defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
1301
+ handleClose: toggleEditReleaseModal,
1302
+ handleSubmit: handleEditRelease,
1303
+ isLoading: isLoadingDetails || isSubmittingForm,
1304
+ initialValues: {
1305
+ name: title || "",
1306
+ scheduledAt,
1307
+ date,
1308
+ time,
1309
+ isScheduled: Boolean(scheduledAt),
1310
+ timezone
1311
+ }
1285
1312
  }
1286
1313
  ),
1287
1314
  /* @__PURE__ */ jsxRuntime.jsx(
1288
- helperPlugin.PaginationURLQuery,
1315
+ helperPlugin.ConfirmDialog,
1289
1316
  {
1290
- pagination: {
1291
- pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
1292
- }
1317
+ bodyText: {
1318
+ id: "content-releases.dialog.confirmation-message",
1319
+ defaultMessage: "Are you sure you want to delete this release?"
1320
+ },
1321
+ isOpen: showWarningSubmit,
1322
+ isConfirmButtonLoading: isDeletingRelease,
1323
+ onToggleDialog: toggleWarningSubmit,
1324
+ onConfirm: handleDeleteRelease
1293
1325
  }
1294
1326
  )
1295
- ] })
1296
- ] }) }),
1297
- releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
1298
- ReleaseModal,
1299
- {
1300
- handleClose: toggleAddReleaseModal,
1301
- handleSubmit: handleAddRelease,
1302
- isLoading: isSubmittingForm,
1303
- initialValues: INITIAL_FORM_VALUES
1304
- }
1305
- )
1306
- ] });
1327
+ ]
1328
+ }
1329
+ );
1307
1330
  };
1308
1331
  const App = () => {
1309
1332
  return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPagePermissions, { permissions: index.PERMISSIONS.main, children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Switch, { children: [
@@ -1312,4 +1335,4 @@ const App = () => {
1312
1335
  ] }) });
1313
1336
  };
1314
1337
  exports.App = App;
1315
- //# sourceMappingURL=App-OK4Xac-O.js.map
1338
+ //# sourceMappingURL=App-OP70yd5M.js.map