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

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-U6GbyLIE.mjs → App-5cple0kE.mjs} +607 -369
  2. package/dist/_chunks/App-5cple0kE.mjs.map +1 -0
  3. package/dist/_chunks/{App-1hHIqUoZ.js → App-RjoH8Hhy.js} +596 -357
  4. package/dist/_chunks/App-RjoH8Hhy.js.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-Aiq23qvI.js} +266 -13
  14. package/dist/_chunks/index-Aiq23qvI.js.map +1 -0
  15. package/dist/_chunks/{index-gkExFBa0.mjs → index-uYfmSaSL.mjs} +278 -25
  16. package/dist/_chunks/index-uYfmSaSL.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 +14 -12
  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-Aiq23qvI.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,371 @@ 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 RelativeTime = styled__default.default(helperPlugin.RelativeTime)`
287
+ display: inline-block;
288
+ &::first-letter {
289
+ text-transform: uppercase;
290
+ }
291
+ `;
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";
310
+ }
311
+ return {
312
+ textColor: `${color}600`,
313
+ backgroundColor: `${color}100`,
314
+ borderColor: `${color}200`
315
+ };
316
+ };
317
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
318
+ const { formatMessage } = reactIntl.useIntl();
319
+ if (isError) {
320
+ return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
321
+ }
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
+ );
338
+ }
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)) });
364
+ };
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);
385
+ const toggleNotification = helperPlugin.useNotification();
386
+ const { formatMessage } = reactIntl.useIntl();
387
+ const { push, replace } = reactRouterDom.useHistory();
388
+ const { formatAPIError } = helperPlugin.useAPIErrorHandler();
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");
394
+ const { trackUsage } = helperPlugin.useTracking();
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) {
400
+ toggleNotification({
401
+ type: "warning",
402
+ title: formatMessage({
403
+ id: "content-releases.pages.Releases.notification.error.title",
404
+ defaultMessage: "Your request could not be processed."
405
+ }),
406
+ message: formatMessage({
407
+ id: "content-releases.pages.Releases.notification.error.message",
408
+ defaultMessage: "Please try again or open another release."
409
+ })
410
+ });
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
+ })
452
+ });
453
+ trackUsage("didCreateRelease");
454
+ push(`/plugins/content-releases/${response2.data.data.id}`);
455
+ } else if (index.isAxiosError(response2.error)) {
456
+ toggleNotification({
457
+ type: "warning",
458
+ message: formatAPIError(response2.error)
459
+ });
460
+ } else {
461
+ toggleNotification({
462
+ type: "warning",
463
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
464
+ });
465
+ }
466
+ };
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
+ };
118
598
  const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
119
599
  align-self: stretch;
120
600
  border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
@@ -128,6 +608,10 @@ const StyledMenuItem = styled__default.default(v2.Menu.Item)`
128
608
  span {
129
609
  color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
130
610
  }
611
+
612
+ &:hover {
613
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
614
+ }
131
615
  `;
132
616
  const PencilIcon = styled__default.default(icons.Pencil)`
133
617
  width: ${({ theme }) => theme.spaces[3]};
@@ -194,7 +678,7 @@ const ReleaseDetailsLayout = ({
194
678
  toggleWarningSubmit,
195
679
  children
196
680
  }) => {
197
- const { formatMessage } = reactIntl.useIntl();
681
+ const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
198
682
  const { releaseId } = reactRouterDom.useParams();
199
683
  const {
200
684
  data,
@@ -240,7 +724,12 @@ const ReleaseDetailsLayout = ({
240
724
  }
241
725
  };
242
726
  const handleRefresh = () => {
243
- dispatch(index.releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
727
+ dispatch(
728
+ index.releaseApi.util.invalidateTags([
729
+ { type: "ReleaseAction", id: "LIST" },
730
+ { type: "Release", id: releaseId }
731
+ ])
732
+ );
244
733
  };
245
734
  const getCreatedByUser = () => {
246
735
  if (!release?.createdBy) {
@@ -276,18 +765,43 @@ const ReleaseDetailsLayout = ({
276
765
  }
277
766
  const totalEntries = release.actions.meta.count || 0;
278
767
  const hasCreatedByUser = Boolean(getCreatedByUser());
768
+ const isScheduled = release.scheduledAt && release.timezone;
769
+ const numberOfEntriesText = formatMessage(
770
+ {
771
+ id: "content-releases.pages.Details.header-subtitle",
772
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
773
+ },
774
+ { number: totalEntries }
775
+ );
776
+ const scheduledText = isScheduled ? formatMessage(
777
+ {
778
+ id: "content-releases.pages.ReleaseDetails.header-subtitle.scheduled",
779
+ defaultMessage: "Scheduled for {date} at {time} ({offset})"
780
+ },
781
+ {
782
+ date: formatDate(new Date(release.scheduledAt), {
783
+ weekday: "long",
784
+ day: "numeric",
785
+ month: "long",
786
+ year: "numeric",
787
+ timeZone: release.timezone
788
+ }),
789
+ time: formatTime(new Date(release.scheduledAt), {
790
+ timeZone: release.timezone,
791
+ hourCycle: "h23"
792
+ }),
793
+ offset: index.getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
794
+ }
795
+ ) : "";
279
796
  return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoadingDetails, children: [
280
797
  /* @__PURE__ */ jsxRuntime.jsx(
281
798
  designSystem.HeaderLayout,
282
799
  {
283
800
  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
- ),
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
+ ] }),
291
805
  navigationAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
292
806
  id: "global.back",
293
807
  defaultMessage: "Back"
@@ -318,42 +832,28 @@ const ReleaseDetailsLayout = ({
318
832
  padding: 1,
319
833
  width: "100%",
320
834
  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,
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,
341
844
  {
342
- paddingTop: 2,
343
- paddingBottom: 2,
344
- alignItems: "center",
345
- gap: 2,
346
- hasRadius: true,
347
- width: "100%",
348
- 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: [
349
849
  /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
350
850
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
351
851
  id: "content-releases.header.actions.delete",
352
852
  defaultMessage: "Delete"
353
853
  }) })
354
- ]
854
+ ] })
355
855
  }
356
- ) })
856
+ )
357
857
  ]
358
858
  }
359
859
  ),
@@ -712,7 +1212,7 @@ const ReleaseDetailsPage = () => {
712
1212
  const { releaseId } = reactRouterDom.useParams();
713
1213
  const toggleNotification = helperPlugin.useNotification();
714
1214
  const { formatAPIError } = helperPlugin.useAPIErrorHandler();
715
- const { push } = reactRouterDom.useHistory();
1215
+ const { replace } = reactRouterDom.useHistory();
716
1216
  const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
717
1217
  const [showWarningSubmit, setWarningSubmit] = React__namespace.useState(false);
718
1218
  const {
@@ -736,11 +1236,18 @@ const ReleaseDetailsPage = () => {
736
1236
  }
737
1237
  );
738
1238
  }
739
- const title = isSuccessDetails && data?.data?.name || "";
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") : "";
740
1245
  const handleEditRelease = async (values) => {
741
1246
  const response = await updateRelease({
742
1247
  id: releaseId,
743
- name: values.name
1248
+ name: values.name,
1249
+ scheduledAt: values.scheduledAt,
1250
+ timezone: values.timezone
744
1251
  });
745
1252
  if ("data" in response) {
746
1253
  toggleNotification({
@@ -768,7 +1275,7 @@ const ReleaseDetailsPage = () => {
768
1275
  id: releaseId
769
1276
  });
770
1277
  if ("data" in response) {
771
- push("/plugins/content-releases");
1278
+ replace("/plugins/content-releases");
772
1279
  } else if (index.isAxiosError(response.error)) {
773
1280
  toggleNotification({
774
1281
  type: "warning",
@@ -794,7 +1301,14 @@ const ReleaseDetailsPage = () => {
794
1301
  handleClose: toggleEditReleaseModal,
795
1302
  handleSubmit: handleEditRelease,
796
1303
  isLoading: isLoadingDetails || isSubmittingForm,
797
- initialValues: { name: title || "" }
1304
+ initialValues: {
1305
+ name: title || "",
1306
+ scheduledAt,
1307
+ date,
1308
+ time,
1309
+ isScheduled: Boolean(scheduledAt),
1310
+ timezone
1311
+ }
798
1312
  }
799
1313
  ),
800
1314
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -814,281 +1328,6 @@ const ReleaseDetailsPage = () => {
814
1328
  }
815
1329
  );
816
1330
  };
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
1331
  const App = () => {
1093
1332
  return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPagePermissions, { permissions: index.PERMISSIONS.main, children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Switch, { children: [
1094
1333
  /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { exact: true, path: `/plugins/${index.pluginId}`, component: ReleasesPage }),
@@ -1096,4 +1335,4 @@ const App = () => {
1096
1335
  ] }) });
1097
1336
  };
1098
1337
  exports.App = App;
1099
- //# sourceMappingURL=App-1hHIqUoZ.js.map
1338
+ //# sourceMappingURL=App-RjoH8Hhy.js.map