@strapi/content-releases 0.0.0-next.95a939e004e74915357523e3adb118a31fef57ed → 0.0.0-next.9bff6e6b8e2a2c7445d3650f1b3459f1b0366db8

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