@strapi/content-releases 0.0.0-next.90a86f595c31de9a89f4255318bfb0cccb30ceed → 0.0.0-next.a9d79bec775daaf0da4e506b2aebafdb4ca95b06

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/_chunks/{App-8FCxPK8-.mjs → App-bpzO2Ljh.mjs} +616 -365
  2. package/dist/_chunks/App-bpzO2Ljh.mjs.map +1 -0
  3. package/dist/_chunks/{App-pspKUC-W.js → App-p8aKBitd.js} +606 -354
  4. package/dist/_chunks/App-p8aKBitd.js.map +1 -0
  5. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs +51 -0
  6. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +1 -0
  7. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js +51 -0
  8. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +1 -0
  9. package/dist/_chunks/{en-m9eTk4UF.mjs → en-WuuhP6Bn.mjs} +15 -4
  10. package/dist/_chunks/en-WuuhP6Bn.mjs.map +1 -0
  11. package/dist/_chunks/{en-r9YocBH0.js → en-gcJJ5htG.js} +15 -4
  12. package/dist/_chunks/en-gcJJ5htG.js.map +1 -0
  13. package/dist/_chunks/{index-8aK7GzI5.mjs → index-AECgcaDa.mjs} +99 -22
  14. package/dist/_chunks/index-AECgcaDa.mjs.map +1 -0
  15. package/dist/_chunks/{index-nGaPcY9m.js → index-fP3qoWZ4.js} +88 -11
  16. package/dist/_chunks/index-fP3qoWZ4.js.map +1 -0
  17. package/dist/admin/index.js +1 -1
  18. package/dist/admin/index.mjs +2 -2
  19. package/dist/server/index.js +818 -432
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +817 -432
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +13 -11
  24. package/dist/_chunks/App-8FCxPK8-.mjs.map +0 -1
  25. package/dist/_chunks/App-pspKUC-W.js.map +0 -1
  26. package/dist/_chunks/en-m9eTk4UF.mjs.map +0 -1
  27. package/dist/_chunks/en-r9YocBH0.js.map +0 -1
  28. package/dist/_chunks/index-8aK7GzI5.mjs.map +0 -1
  29. package/dist/_chunks/index-nGaPcY9m.js.map +0 -1
@@ -3,14 +3,17 @@ 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-nGaPcY9m.js");
6
+ const index = require("./index-fP3qoWZ4.js");
7
7
  const React = require("react");
8
8
  const strapiAdmin = require("@strapi/admin/strapi-admin");
9
9
  const designSystem = require("@strapi/design-system");
10
10
  const v2 = require("@strapi/design-system/v2");
11
11
  const icons = require("@strapi/icons");
12
+ const format = require("date-fns/format");
13
+ const dateFnsTz = require("date-fns-tz");
12
14
  const reactIntl = require("react-intl");
13
15
  const styled = require("styled-components");
16
+ const dateFns = require("date-fns");
14
17
  const formik = require("formik");
15
18
  const yup = require("yup");
16
19
  require("@reduxjs/toolkit/query");
@@ -37,12 +40,28 @@ function _interopNamespace(e) {
37
40
  return Object.freeze(n);
38
41
  }
39
42
  const React__namespace = /* @__PURE__ */ _interopNamespace(React);
43
+ const format__default = /* @__PURE__ */ _interopDefault(format);
40
44
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
41
45
  const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
42
46
  const RELEASE_SCHEMA = yup__namespace.object().shape({
43
47
  name: yup__namespace.string().trim().required(),
44
- // scheduledAt is a date, but we always receive strings from the client
45
- scheduledAt: yup__namespace.string().nullable()
48
+ scheduledAt: yup__namespace.string().nullable(),
49
+ isScheduled: yup__namespace.boolean().optional(),
50
+ time: yup__namespace.string().when("isScheduled", {
51
+ is: true,
52
+ then: yup__namespace.string().trim().required(),
53
+ otherwise: yup__namespace.string().nullable()
54
+ }),
55
+ timezone: yup__namespace.string().when("isScheduled", {
56
+ is: true,
57
+ then: yup__namespace.string().required().nullable(),
58
+ otherwise: yup__namespace.string().nullable()
59
+ }),
60
+ date: yup__namespace.string().when("isScheduled", {
61
+ is: true,
62
+ then: yup__namespace.string().required().nullable(),
63
+ otherwise: yup__namespace.string().nullable()
64
+ })
46
65
  }).required().noUnknown();
47
66
  const ReleaseModal = ({
48
67
  handleClose,
@@ -53,6 +72,24 @@ const ReleaseModal = ({
53
72
  const { formatMessage } = reactIntl.useIntl();
54
73
  const { pathname } = reactRouterDom.useLocation();
55
74
  const isCreatingRelease = pathname === `/plugins/${index.pluginId}`;
75
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
76
+ const { timezoneList, systemTimezone = { value: "UTC+00:00-Africa/Abidjan " } } = getTimezones(
77
+ initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : /* @__PURE__ */ new Date()
78
+ );
79
+ const getScheduledTimestamp = (values) => {
80
+ const { date, time, timezone } = values;
81
+ if (!date || !time || !timezone)
82
+ 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);
86
+ };
87
+ const getTimezoneWithOffset = () => {
88
+ const currentTimezone = timezoneList.find(
89
+ (timezone) => timezone.value.split("&")[1] === initialValues.timezone
90
+ );
91
+ return currentTimezone?.value || systemTimezone.value;
92
+ };
56
93
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ModalLayout, { onClose: handleClose, labelledBy: "title", children: [
57
94
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: "title", fontWeight: "bold", textColor: "neutral800", children: formatMessage(
58
95
  {
@@ -64,45 +101,135 @@ const ReleaseModal = ({
64
101
  /* @__PURE__ */ jsxRuntime.jsx(
65
102
  formik.Formik,
66
103
  {
67
- validateOnChange: false,
68
- onSubmit: handleSubmit,
69
- initialValues,
104
+ onSubmit: (values) => {
105
+ handleSubmit({
106
+ ...values,
107
+ timezone: values.timezone ? values.timezone.split("&")[1] : null,
108
+ scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
109
+ });
110
+ },
111
+ initialValues: {
112
+ ...initialValues,
113
+ timezone: initialValues.timezone ? getTimezoneWithOffset() : systemTimezone.value
114
+ },
70
115
  validationSchema: RELEASE_SCHEMA,
71
- children: ({ values, errors, handleChange }) => /* @__PURE__ */ jsxRuntime.jsxs(formik.Form, { children: [
72
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalBody, { children: /* @__PURE__ */ jsxRuntime.jsx(
73
- designSystem.TextInput,
74
- {
75
- label: formatMessage({
76
- id: "content-releases.modal.form.input.label.release-name",
77
- defaultMessage: "Name"
78
- }),
79
- name: "name",
80
- value: values.name,
81
- error: errors.name,
82
- onChange: handleChange,
83
- required: true
84
- }
85
- ) }),
116
+ validateOnChange: false,
117
+ children: ({ values, errors, handleChange, setFieldValue }) => /* @__PURE__ */ jsxRuntime.jsxs(formik.Form, { children: [
118
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ModalBody, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
119
+ /* @__PURE__ */ jsxRuntime.jsx(
120
+ designSystem.TextInput,
121
+ {
122
+ label: formatMessage({
123
+ id: "content-releases.modal.form.input.label.release-name",
124
+ defaultMessage: "Name"
125
+ }),
126
+ name: "name",
127
+ value: values.name,
128
+ error: errors.name,
129
+ onChange: handleChange,
130
+ required: true
131
+ }
132
+ ),
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) {
142
+ setFieldValue("date", null);
143
+ 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
+ minDate: dateFnsTz.utcToZonedTime(/* @__PURE__ */ new Date(), values.timezone.split("&")[1])
192
+ }
193
+ ) }),
194
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
195
+ designSystem.TimePicker,
196
+ {
197
+ label: formatMessage({
198
+ id: "content-releases.modal.form.input.label.time",
199
+ defaultMessage: "Time"
200
+ }),
201
+ name: "time",
202
+ error: errors.time,
203
+ onChange: (time) => {
204
+ setFieldValue("time", time);
205
+ },
206
+ clearLabel: formatMessage({
207
+ id: "content-releases.modal.form.input.clearLabel",
208
+ defaultMessage: "Clear"
209
+ }),
210
+ onClear: () => {
211
+ setFieldValue("time", "");
212
+ },
213
+ value: values.time || void 0,
214
+ required: true
215
+ }
216
+ ) })
217
+ ] }),
218
+ /* @__PURE__ */ jsxRuntime.jsx(TimezoneComponent, { timezoneOptions: timezoneList })
219
+ ] })
220
+ ] })
221
+ ] }) }),
86
222
  /* @__PURE__ */ jsxRuntime.jsx(
87
223
  designSystem.ModalFooter,
88
224
  {
89
225
  startActions: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleClose, variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }),
90
- endActions: /* @__PURE__ */ jsxRuntime.jsx(
91
- designSystem.Button,
226
+ endActions: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
92
227
  {
93
- name: "submit",
94
- loading: isLoading,
95
- disabled: !values.name || values.name === initialValues.name,
96
- type: "submit",
97
- children: formatMessage(
98
- {
99
- id: "content-releases.modal.form.button.submit",
100
- defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
101
- },
102
- { isCreatingRelease }
103
- )
104
- }
105
- )
228
+ id: "content-releases.modal.form.button.submit",
229
+ defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
230
+ },
231
+ { isCreatingRelease }
232
+ ) })
106
233
  }
107
234
  )
108
235
  ] })
@@ -110,6 +237,376 @@ const ReleaseModal = ({
110
237
  )
111
238
  ] });
112
239
  };
240
+ const getTimezones = (selectedDate) => {
241
+ const timezoneList = Intl.supportedValuesOf("timeZone").map((timezone) => {
242
+ const utcOffset = index.getTimezoneOffset(timezone, selectedDate);
243
+ return { offset: utcOffset, value: `${utcOffset}&${timezone}` };
244
+ });
245
+ const systemTimezone = timezoneList.find(
246
+ (timezone) => timezone.value.split("&")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
247
+ );
248
+ return { timezoneList, systemTimezone };
249
+ };
250
+ const TimezoneComponent = ({ timezoneOptions }) => {
251
+ const { values, errors, setFieldValue } = formik.useFormikContext();
252
+ const { formatMessage } = reactIntl.useIntl();
253
+ const [timezoneList, setTimezoneList] = React__namespace.useState(timezoneOptions);
254
+ React__namespace.useEffect(() => {
255
+ if (values.date) {
256
+ const { timezoneList: timezoneList2 } = getTimezones(new Date(values.date));
257
+ setTimezoneList(timezoneList2);
258
+ const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("&")[1] === values.timezone.split("&")[1]);
259
+ if (updatedTimezone) {
260
+ setFieldValue("timezone", updatedTimezone.value);
261
+ }
262
+ }
263
+ }, [setFieldValue, values.date, values.timezone]);
264
+ return /* @__PURE__ */ jsxRuntime.jsx(
265
+ designSystem.Combobox,
266
+ {
267
+ label: formatMessage({
268
+ id: "content-releases.modal.form.input.label.timezone",
269
+ defaultMessage: "Timezone"
270
+ }),
271
+ autocomplete: { type: "list", filter: "contains" },
272
+ name: "timezone",
273
+ value: values.timezone || void 0,
274
+ textValue: values.timezone ? values.timezone.replace(/&/, " ") : void 0,
275
+ onChange: (timezone) => {
276
+ setFieldValue("timezone", timezone);
277
+ },
278
+ onTextValueChange: (timezone) => {
279
+ setFieldValue("timezone", timezone);
280
+ },
281
+ onClear: () => {
282
+ setFieldValue("timezone", "");
283
+ },
284
+ error: errors.timezone,
285
+ required: true,
286
+ children: timezoneList.map((timezone) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: timezone.value, children: timezone.value.replace(/&/, " ") }, timezone.value))
287
+ }
288
+ );
289
+ };
290
+ const LinkCard = styled__default.default(v2.Link)`
291
+ display: block;
292
+ `;
293
+ const CapitalizeRelativeTime = styled__default.default(helperPlugin.RelativeTime)`
294
+ text-transform: capitalize;
295
+ `;
296
+ const getBadgeProps = (status) => {
297
+ let color;
298
+ switch (status) {
299
+ case "ready":
300
+ color = "success";
301
+ break;
302
+ case "blocked":
303
+ color = "warning";
304
+ break;
305
+ case "failed":
306
+ color = "danger";
307
+ break;
308
+ case "done":
309
+ color = "primary";
310
+ break;
311
+ case "empty":
312
+ default:
313
+ color = "neutral";
314
+ }
315
+ return {
316
+ textColor: `${color}600`,
317
+ backgroundColor: `${color}100`,
318
+ borderColor: `${color}200`
319
+ };
320
+ };
321
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
322
+ const { formatMessage } = reactIntl.useIntl();
323
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
324
+ if (isError) {
325
+ return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
326
+ }
327
+ if (releases?.length === 0) {
328
+ return /* @__PURE__ */ jsxRuntime.jsx(
329
+ designSystem.EmptyStateLayout,
330
+ {
331
+ content: formatMessage(
332
+ {
333
+ id: "content-releases.page.Releases.tab.emptyEntries",
334
+ defaultMessage: "No releases"
335
+ },
336
+ {
337
+ target: sectionTitle
338
+ }
339
+ ),
340
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.EmptyDocuments, { width: "10rem" })
341
+ }
342
+ );
343
+ }
344
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid, { gap: 4, children: releases.map(({ id, name, actions, 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(
345
+ designSystem.Flex,
346
+ {
347
+ direction: "column",
348
+ justifyContent: "space-between",
349
+ padding: 4,
350
+ hasRadius: true,
351
+ background: "neutral0",
352
+ shadow: "tableShadow",
353
+ height: "100%",
354
+ width: "100%",
355
+ alignItems: "start",
356
+ gap: 4,
357
+ children: [
358
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "start", gap: 1, children: [
359
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
360
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: IsSchedulingEnabled ? scheduledAt ? /* @__PURE__ */ jsxRuntime.jsx(CapitalizeRelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
361
+ id: "content-releases.pages.Releases.not-scheduled",
362
+ defaultMessage: "Not scheduled"
363
+ }) : formatMessage(
364
+ {
365
+ id: "content-releases.page.Releases.release-item.entries",
366
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
367
+ },
368
+ { number: actions.meta.count }
369
+ ) })
370
+ ] }),
371
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { ...getBadgeProps(status), children: status })
372
+ ]
373
+ }
374
+ ) }) }, id)) });
375
+ };
376
+ const StyledAlert = styled__default.default(designSystem.Alert)`
377
+ button {
378
+ display: none;
379
+ }
380
+ p + div {
381
+ margin-left: auto;
382
+ }
383
+ `;
384
+ const INITIAL_FORM_VALUES = {
385
+ name: "",
386
+ date: null,
387
+ time: "",
388
+ // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
389
+ isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
390
+ scheduledAt: null,
391
+ timezone: null
392
+ };
393
+ const ReleasesPage = () => {
394
+ const tabRef = React__namespace.useRef(null);
395
+ const location = reactRouterDom.useLocation();
396
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
397
+ const toggleNotification = helperPlugin.useNotification();
398
+ const { formatMessage } = reactIntl.useIntl();
399
+ const { push, replace } = reactRouterDom.useHistory();
400
+ const { formatAPIError } = helperPlugin.useAPIErrorHandler();
401
+ const [{ query }, setQuery] = helperPlugin.useQueryParams();
402
+ const response = index.useGetReleasesQuery(query);
403
+ const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
404
+ const { getFeature } = strapiAdmin.useLicenseLimits();
405
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
406
+ const { trackUsage } = helperPlugin.useTracking();
407
+ const { isLoading, isSuccess, isError } = response;
408
+ const activeTab = response?.currentData?.meta?.activeTab || "pending";
409
+ const activeTabIndex = ["pending", "done"].indexOf(activeTab);
410
+ React__namespace.useEffect(() => {
411
+ if (location?.state?.errors) {
412
+ toggleNotification({
413
+ type: "warning",
414
+ title: formatMessage({
415
+ id: "content-releases.pages.Releases.notification.error.title",
416
+ defaultMessage: "Your request could not be processed."
417
+ }),
418
+ message: formatMessage({
419
+ id: "content-releases.pages.Releases.notification.error.message",
420
+ defaultMessage: "Please try again or open another release."
421
+ })
422
+ });
423
+ replace({ state: null });
424
+ }
425
+ }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
426
+ React__namespace.useEffect(() => {
427
+ if (tabRef.current) {
428
+ tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
429
+ }
430
+ }, [activeTabIndex]);
431
+ const toggleAddReleaseModal = () => {
432
+ setReleaseModalShown((prev) => !prev);
433
+ };
434
+ if (isLoading) {
435
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
436
+ }
437
+ const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
438
+ const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
439
+ const handleTabChange = (index2) => {
440
+ setQuery({
441
+ ...query,
442
+ page: 1,
443
+ pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
444
+ filters: {
445
+ releasedAt: {
446
+ $notNull: index2 === 0 ? false : true
447
+ }
448
+ }
449
+ });
450
+ };
451
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
452
+ const response2 = await createRelease({
453
+ name,
454
+ scheduledAt,
455
+ timezone
456
+ });
457
+ if ("data" in response2) {
458
+ toggleNotification({
459
+ type: "success",
460
+ message: formatMessage({
461
+ id: "content-releases.modal.release-created-notification-success",
462
+ defaultMessage: "Release created."
463
+ })
464
+ });
465
+ trackUsage("didCreateRelease");
466
+ push(`/plugins/content-releases/${response2.data.data.id}`);
467
+ } else if (index.isAxiosError(response2.error)) {
468
+ toggleNotification({
469
+ type: "warning",
470
+ message: formatAPIError(response2.error)
471
+ });
472
+ } else {
473
+ toggleNotification({
474
+ type: "warning",
475
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
476
+ });
477
+ }
478
+ };
479
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
480
+ /* @__PURE__ */ jsxRuntime.jsx(
481
+ designSystem.HeaderLayout,
482
+ {
483
+ title: formatMessage({
484
+ id: "content-releases.pages.Releases.title",
485
+ defaultMessage: "Releases"
486
+ }),
487
+ subtitle: formatMessage({
488
+ id: "content-releases.pages.Releases.header-subtitle",
489
+ defaultMessage: "Create and manage content updates"
490
+ }),
491
+ primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
492
+ designSystem.Button,
493
+ {
494
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
495
+ onClick: toggleAddReleaseModal,
496
+ disabled: hasReachedMaximumPendingReleases,
497
+ children: formatMessage({
498
+ id: "content-releases.header.actions.add-release",
499
+ defaultMessage: "New release"
500
+ })
501
+ }
502
+ ) })
503
+ }
504
+ ),
505
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
506
+ hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
507
+ StyledAlert,
508
+ {
509
+ marginBottom: 6,
510
+ action: /* @__PURE__ */ jsxRuntime.jsx(v2.Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
511
+ id: "content-releases.pages.Releases.max-limit-reached.action",
512
+ defaultMessage: "Explore plans"
513
+ }) }),
514
+ title: formatMessage(
515
+ {
516
+ id: "content-releases.pages.Releases.max-limit-reached.title",
517
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
518
+ },
519
+ { number: maximumReleases }
520
+ ),
521
+ onClose: () => {
522
+ },
523
+ closeLabel: "",
524
+ children: formatMessage({
525
+ id: "content-releases.pages.Releases.max-limit-reached.message",
526
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
527
+ })
528
+ }
529
+ ),
530
+ /* @__PURE__ */ jsxRuntime.jsxs(
531
+ designSystem.TabGroup,
532
+ {
533
+ label: formatMessage({
534
+ id: "content-releases.pages.Releases.tab-group.label",
535
+ defaultMessage: "Releases list"
536
+ }),
537
+ variant: "simple",
538
+ initialSelectedTabIndex: activeTabIndex,
539
+ onTabChange: handleTabChange,
540
+ ref: tabRef,
541
+ children: [
542
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingBottom: 8, children: [
543
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs, { children: [
544
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage(
545
+ {
546
+ id: "content-releases.pages.Releases.tab.pending",
547
+ defaultMessage: "Pending ({count})"
548
+ },
549
+ {
550
+ count: totalPendingReleases
551
+ }
552
+ ) }),
553
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
554
+ id: "content-releases.pages.Releases.tab.done",
555
+ defaultMessage: "Done"
556
+ }) })
557
+ ] }),
558
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {})
559
+ ] }),
560
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.TabPanels, { children: [
561
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
562
+ ReleasesGrid,
563
+ {
564
+ sectionTitle: "pending",
565
+ releases: response?.currentData?.data,
566
+ isError
567
+ }
568
+ ) }),
569
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
570
+ ReleasesGrid,
571
+ {
572
+ sectionTitle: "done",
573
+ releases: response?.currentData?.data,
574
+ isError
575
+ }
576
+ ) })
577
+ ] })
578
+ ]
579
+ }
580
+ ),
581
+ response.currentData?.meta?.pagination?.total ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
582
+ /* @__PURE__ */ jsxRuntime.jsx(
583
+ helperPlugin.PageSizeURLQuery,
584
+ {
585
+ options: ["8", "16", "32", "64"],
586
+ defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
587
+ }
588
+ ),
589
+ /* @__PURE__ */ jsxRuntime.jsx(
590
+ helperPlugin.PaginationURLQuery,
591
+ {
592
+ pagination: {
593
+ pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
594
+ }
595
+ }
596
+ )
597
+ ] }) : null
598
+ ] }) }),
599
+ releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
600
+ ReleaseModal,
601
+ {
602
+ handleClose: toggleAddReleaseModal,
603
+ handleSubmit: handleAddRelease,
604
+ isLoading: isSubmittingForm,
605
+ initialValues: INITIAL_FORM_VALUES
606
+ }
607
+ )
608
+ ] });
609
+ };
113
610
  const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
114
611
  align-self: stretch;
115
612
  border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
@@ -123,6 +620,10 @@ const StyledMenuItem = styled__default.default(v2.Menu.Item)`
123
620
  span {
124
621
  color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
125
622
  }
623
+
624
+ &:hover {
625
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
626
+ }
126
627
  `;
127
628
  const PencilIcon = styled__default.default(icons.Pencil)`
128
629
  width: ${({ theme }) => theme.spaces[3]};
@@ -189,7 +690,7 @@ const ReleaseDetailsLayout = ({
189
690
  toggleWarningSubmit,
190
691
  children
191
692
  }) => {
192
- const { formatMessage } = reactIntl.useIntl();
693
+ const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
193
694
  const { releaseId } = reactRouterDom.useParams();
194
695
  const {
195
696
  data,
@@ -271,18 +772,44 @@ const ReleaseDetailsLayout = ({
271
772
  }
272
773
  const totalEntries = release.actions.meta.count || 0;
273
774
  const hasCreatedByUser = Boolean(getCreatedByUser());
775
+ const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
776
+ const isScheduled = release.scheduledAt && release.timezone;
777
+ const numberOfEntriesText = formatMessage(
778
+ {
779
+ id: "content-releases.pages.Details.header-subtitle",
780
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
781
+ },
782
+ { number: totalEntries }
783
+ );
784
+ const scheduledText = isScheduled ? formatMessage(
785
+ {
786
+ id: "content-releases.pages.ReleaseDetails.header-subtitle.scheduled",
787
+ defaultMessage: "Scheduled for {date} at {time} ({offset})"
788
+ },
789
+ {
790
+ date: formatDate(new Date(release.scheduledAt), {
791
+ weekday: "long",
792
+ day: "numeric",
793
+ month: "long",
794
+ year: "numeric",
795
+ timeZone: release.timezone
796
+ }),
797
+ time: formatTime(new Date(release.scheduledAt), {
798
+ timeZone: release.timezone,
799
+ hourCycle: "h23"
800
+ }),
801
+ offset: index.getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
802
+ }
803
+ ) : "";
274
804
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoadingDetails, children: [
275
805
  /* @__PURE__ */ jsxRuntime.jsx(
276
806
  designSystem.HeaderLayout,
277
807
  {
278
808
  title: release.name,
279
- subtitle: formatMessage(
280
- {
281
- id: "content-releases.pages.Details.header-subtitle",
282
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
283
- },
284
- { number: totalEntries }
285
- ),
809
+ subtitle: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, lineHeight: 6, children: [
810
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", variant: "epsilon", children: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? ` - ${scheduledText}` : "") }),
811
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { ...getBadgeProps(release.status), children: release.status })
812
+ ] }),
286
813
  navigationAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
287
814
  id: "global.back",
288
815
  defaultMessage: "Back"
@@ -313,42 +840,28 @@ const ReleaseDetailsLayout = ({
313
840
  padding: 1,
314
841
  width: "100%",
315
842
  children: [
316
- /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(
317
- designSystem.Flex,
318
- {
319
- paddingTop: 2,
320
- paddingBottom: 2,
321
- alignItems: "center",
322
- gap: 2,
323
- hasRadius: true,
324
- width: "100%",
325
- children: [
326
- /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
327
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
328
- id: "content-releases.header.actions.edit",
329
- defaultMessage: "Edit"
330
- }) })
331
- ]
332
- }
333
- ) }),
334
- /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxRuntime.jsxs(
335
- designSystem.Flex,
843
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
844
+ /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
845
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
846
+ id: "content-releases.header.actions.edit",
847
+ defaultMessage: "Edit"
848
+ }) })
849
+ ] }) }),
850
+ /* @__PURE__ */ jsxRuntime.jsx(
851
+ StyledMenuItem,
336
852
  {
337
- paddingTop: 2,
338
- paddingBottom: 2,
339
- alignItems: "center",
340
- gap: 2,
341
- hasRadius: true,
342
- width: "100%",
343
- children: [
853
+ disabled: !canDelete,
854
+ onSelect: toggleWarningSubmit,
855
+ variant: "danger",
856
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
344
857
  /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
345
858
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
346
859
  id: "content-releases.header.actions.delete",
347
860
  defaultMessage: "Delete"
348
861
  }) })
349
- ]
862
+ ] })
350
863
  }
351
- ) })
864
+ )
352
865
  ]
353
866
  }
354
867
  ),
@@ -707,7 +1220,7 @@ const ReleaseDetailsPage = () => {
707
1220
  const { releaseId } = reactRouterDom.useParams();
708
1221
  const toggleNotification = helperPlugin.useNotification();
709
1222
  const { formatAPIError } = helperPlugin.useAPIErrorHandler();
710
- const { push } = reactRouterDom.useHistory();
1223
+ const { replace } = reactRouterDom.useHistory();
711
1224
  const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
712
1225
  const [showWarningSubmit, setWarningSubmit] = React__namespace.useState(false);
713
1226
  const {
@@ -731,11 +1244,18 @@ const ReleaseDetailsPage = () => {
731
1244
  }
732
1245
  );
733
1246
  }
734
- const title = isSuccessDetails && data?.data?.name || "";
1247
+ const releaseData = isSuccessDetails && data?.data || null;
1248
+ const title = releaseData?.name || "";
1249
+ const timezone = releaseData?.timezone ?? null;
1250
+ const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
1251
+ const date = scheduledAt ? new Date(format__default.default(scheduledAt, "yyyy-MM-dd")) : null;
1252
+ const time = scheduledAt ? format__default.default(scheduledAt, "HH:mm") : "";
735
1253
  const handleEditRelease = async (values) => {
736
1254
  const response = await updateRelease({
737
1255
  id: releaseId,
738
- name: values.name
1256
+ name: values.name,
1257
+ scheduledAt: values.scheduledAt,
1258
+ timezone: values.timezone
739
1259
  });
740
1260
  if ("data" in response) {
741
1261
  toggleNotification({
@@ -763,7 +1283,7 @@ const ReleaseDetailsPage = () => {
763
1283
  id: releaseId
764
1284
  });
765
1285
  if ("data" in response) {
766
- push("/plugins/content-releases");
1286
+ replace("/plugins/content-releases");
767
1287
  } else if (index.isAxiosError(response.error)) {
768
1288
  toggleNotification({
769
1289
  type: "warning",
@@ -789,7 +1309,14 @@ const ReleaseDetailsPage = () => {
789
1309
  handleClose: toggleEditReleaseModal,
790
1310
  handleSubmit: handleEditRelease,
791
1311
  isLoading: isLoadingDetails || isSubmittingForm,
792
- initialValues: { name: title || "" }
1312
+ initialValues: {
1313
+ name: title || "",
1314
+ scheduledAt,
1315
+ date,
1316
+ time,
1317
+ isScheduled: Boolean(scheduledAt),
1318
+ timezone
1319
+ }
793
1320
  }
794
1321
  ),
795
1322
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -809,281 +1336,6 @@ const ReleaseDetailsPage = () => {
809
1336
  }
810
1337
  );
811
1338
  };
812
- const LinkCard = styled__default.default(v2.Link)`
813
- display: block;
814
- `;
815
- const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
816
- const { formatMessage } = reactIntl.useIntl();
817
- if (isError) {
818
- return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
819
- }
820
- if (releases?.length === 0) {
821
- return /* @__PURE__ */ jsxRuntime.jsx(
822
- designSystem.EmptyStateLayout,
823
- {
824
- content: formatMessage(
825
- {
826
- id: "content-releases.page.Releases.tab.emptyEntries",
827
- defaultMessage: "No releases"
828
- },
829
- {
830
- target: sectionTitle
831
- }
832
- ),
833
- icon: /* @__PURE__ */ jsxRuntime.jsx(icons.EmptyDocuments, { width: "10rem" })
834
- }
835
- );
836
- }
837
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid, { gap: 4, children: releases.map(({ id, name, actions }) => /* @__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(
838
- designSystem.Flex,
839
- {
840
- direction: "column",
841
- justifyContent: "space-between",
842
- padding: 4,
843
- hasRadius: true,
844
- background: "neutral0",
845
- shadow: "tableShadow",
846
- height: "100%",
847
- width: "100%",
848
- alignItems: "start",
849
- gap: 2,
850
- children: [
851
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
852
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: formatMessage(
853
- {
854
- id: "content-releases.page.Releases.release-item.entries",
855
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
856
- },
857
- { number: actions.meta.count }
858
- ) })
859
- ]
860
- }
861
- ) }) }, id)) });
862
- };
863
- const StyledAlert = styled__default.default(designSystem.Alert)`
864
- button {
865
- display: none;
866
- }
867
- p + div {
868
- margin-left: auto;
869
- }
870
- `;
871
- const INITIAL_FORM_VALUES = {
872
- name: ""
873
- };
874
- const ReleasesPage = () => {
875
- const tabRef = React__namespace.useRef(null);
876
- const location = reactRouterDom.useLocation();
877
- const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
878
- const toggleNotification = helperPlugin.useNotification();
879
- const { formatMessage } = reactIntl.useIntl();
880
- const { push, replace } = reactRouterDom.useHistory();
881
- const { formatAPIError } = helperPlugin.useAPIErrorHandler();
882
- const [{ query }, setQuery] = helperPlugin.useQueryParams();
883
- const response = index.useGetReleasesQuery(query);
884
- const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
885
- const { getFeature } = strapiAdmin.useLicenseLimits();
886
- const { maximumReleases = 3 } = getFeature("cms-content-releases");
887
- const { trackUsage } = helperPlugin.useTracking();
888
- const { isLoading, isSuccess, isError } = response;
889
- const activeTab = response?.currentData?.meta?.activeTab || "pending";
890
- const activeTabIndex = ["pending", "done"].indexOf(activeTab);
891
- React__namespace.useEffect(() => {
892
- if (location?.state?.errors) {
893
- toggleNotification({
894
- type: "warning",
895
- title: formatMessage({
896
- id: "content-releases.pages.Releases.notification.error.title",
897
- defaultMessage: "Your request could not be processed."
898
- }),
899
- message: formatMessage({
900
- id: "content-releases.pages.Releases.notification.error.message",
901
- defaultMessage: "Please try again or open another release."
902
- })
903
- });
904
- replace({ state: null });
905
- }
906
- }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
907
- React__namespace.useEffect(() => {
908
- if (tabRef.current) {
909
- tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
910
- }
911
- }, [activeTabIndex]);
912
- const toggleAddReleaseModal = () => {
913
- setReleaseModalShown((prev) => !prev);
914
- };
915
- if (isLoading) {
916
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
917
- }
918
- const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
919
- const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
920
- const handleTabChange = (index2) => {
921
- setQuery({
922
- ...query,
923
- page: 1,
924
- pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
925
- filters: {
926
- releasedAt: {
927
- $notNull: index2 === 0 ? false : true
928
- }
929
- }
930
- });
931
- };
932
- const handleAddRelease = async (values) => {
933
- const response2 = await createRelease({
934
- name: values.name
935
- });
936
- if ("data" in response2) {
937
- toggleNotification({
938
- type: "success",
939
- message: formatMessage({
940
- id: "content-releases.modal.release-created-notification-success",
941
- defaultMessage: "Release created."
942
- })
943
- });
944
- trackUsage("didCreateRelease");
945
- push(`/plugins/content-releases/${response2.data.data.id}`);
946
- } else if (index.isAxiosError(response2.error)) {
947
- toggleNotification({
948
- type: "warning",
949
- message: formatAPIError(response2.error)
950
- });
951
- } else {
952
- toggleNotification({
953
- type: "warning",
954
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
955
- });
956
- }
957
- };
958
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
959
- /* @__PURE__ */ jsxRuntime.jsx(
960
- designSystem.HeaderLayout,
961
- {
962
- title: formatMessage({
963
- id: "content-releases.pages.Releases.title",
964
- defaultMessage: "Releases"
965
- }),
966
- subtitle: formatMessage(
967
- {
968
- id: "content-releases.pages.Releases.header-subtitle",
969
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
970
- },
971
- { number: totalReleases }
972
- ),
973
- primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
974
- designSystem.Button,
975
- {
976
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
977
- onClick: toggleAddReleaseModal,
978
- disabled: hasReachedMaximumPendingReleases,
979
- children: formatMessage({
980
- id: "content-releases.header.actions.add-release",
981
- defaultMessage: "New release"
982
- })
983
- }
984
- ) })
985
- }
986
- ),
987
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
988
- activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
989
- StyledAlert,
990
- {
991
- marginBottom: 6,
992
- action: /* @__PURE__ */ jsxRuntime.jsx(v2.Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
993
- id: "content-releases.pages.Releases.max-limit-reached.action",
994
- defaultMessage: "Explore plans"
995
- }) }),
996
- title: formatMessage(
997
- {
998
- id: "content-releases.pages.Releases.max-limit-reached.title",
999
- defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
1000
- },
1001
- { number: maximumReleases }
1002
- ),
1003
- onClose: () => {
1004
- },
1005
- closeLabel: "",
1006
- children: formatMessage({
1007
- id: "content-releases.pages.Releases.max-limit-reached.message",
1008
- defaultMessage: "Upgrade to manage an unlimited number of releases."
1009
- })
1010
- }
1011
- ),
1012
- /* @__PURE__ */ jsxRuntime.jsxs(
1013
- designSystem.TabGroup,
1014
- {
1015
- label: formatMessage({
1016
- id: "content-releases.pages.Releases.tab-group.label",
1017
- defaultMessage: "Releases list"
1018
- }),
1019
- variant: "simple",
1020
- initialSelectedTabIndex: activeTabIndex,
1021
- onTabChange: handleTabChange,
1022
- ref: tabRef,
1023
- children: [
1024
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingBottom: 8, children: [
1025
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs, { children: [
1026
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1027
- id: "content-releases.pages.Releases.tab.pending",
1028
- defaultMessage: "Pending"
1029
- }) }),
1030
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1031
- id: "content-releases.pages.Releases.tab.done",
1032
- defaultMessage: "Done"
1033
- }) })
1034
- ] }),
1035
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {})
1036
- ] }),
1037
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.TabPanels, { children: [
1038
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
1039
- ReleasesGrid,
1040
- {
1041
- sectionTitle: "pending",
1042
- releases: response?.currentData?.data,
1043
- isError
1044
- }
1045
- ) }),
1046
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
1047
- ReleasesGrid,
1048
- {
1049
- sectionTitle: "done",
1050
- releases: response?.currentData?.data,
1051
- isError
1052
- }
1053
- ) })
1054
- ] })
1055
- ]
1056
- }
1057
- ),
1058
- totalReleases > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1059
- /* @__PURE__ */ jsxRuntime.jsx(
1060
- helperPlugin.PageSizeURLQuery,
1061
- {
1062
- options: ["8", "16", "32", "64"],
1063
- defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
1064
- }
1065
- ),
1066
- /* @__PURE__ */ jsxRuntime.jsx(
1067
- helperPlugin.PaginationURLQuery,
1068
- {
1069
- pagination: {
1070
- pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
1071
- }
1072
- }
1073
- )
1074
- ] })
1075
- ] }) }),
1076
- releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
1077
- ReleaseModal,
1078
- {
1079
- handleClose: toggleAddReleaseModal,
1080
- handleSubmit: handleAddRelease,
1081
- isLoading: isSubmittingForm,
1082
- initialValues: INITIAL_FORM_VALUES
1083
- }
1084
- )
1085
- ] });
1086
- };
1087
1339
  const App = () => {
1088
1340
  return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPagePermissions, { permissions: index.PERMISSIONS.main, children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Switch, { children: [
1089
1341
  /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { exact: true, path: `/plugins/${index.pluginId}`, component: ReleasesPage }),
@@ -1091,4 +1343,4 @@ const App = () => {
1091
1343
  ] }) });
1092
1344
  };
1093
1345
  exports.App = App;
1094
- //# sourceMappingURL=App-pspKUC-W.js.map
1346
+ //# sourceMappingURL=App-p8aKBitd.js.map