@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
@@ -1,15 +1,18 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { useNotification, useAPIErrorHandler, LoadingIndicatorPage, ConfirmDialog, useRBAC, useTracking, RelativeTime, CheckPermissions, useQueryParams, AnErrorOccurred, NoContent, Table, PageSizeURLQuery, PaginationURLQuery, CheckPagePermissions } from "@strapi/helper-plugin";
3
- import { useLocation, useParams, useHistory, Redirect, Link as Link$1, Switch, Route } from "react-router-dom";
4
- import { p as pluginId, u as useGetReleaseQuery, a as useUpdateReleaseMutation, b as useDeleteReleaseMutation, c as usePublishReleaseMutation, P as PERMISSIONS, d as useTypedDispatch, e as useGetReleaseActionsQuery, f as useUpdateReleaseActionMutation, R as ReleaseActionOptions, g as ReleaseActionMenu, i as isAxiosError, r as releaseApi, h as useGetReleasesQuery, j as useCreateReleaseMutation } from "./index-gkExFBa0.mjs";
2
+ import { RelativeTime, useNotification, useAPIErrorHandler, useQueryParams, useTracking, LoadingIndicatorPage, CheckPermissions, PageSizeURLQuery, PaginationURLQuery, AnErrorOccurred, ConfirmDialog, useRBAC, NoContent, Table, CheckPagePermissions } from "@strapi/helper-plugin";
3
+ import { useLocation, useHistory, useParams, Redirect, Link as Link$2, Switch, Route } from "react-router-dom";
4
+ import { g as getTimezoneOffset, p as pluginId, u as useGetReleasesQuery, a as useCreateReleaseMutation, P as PERMISSIONS, i as isAxiosError, b as useGetReleaseQuery, c as useUpdateReleaseMutation, d as useDeleteReleaseMutation, e as usePublishReleaseMutation, f as useTypedDispatch, h as useGetReleaseActionsQuery, j as useUpdateReleaseActionMutation, R as ReleaseActionOptions, k as ReleaseActionMenu, r as releaseApi } from "./index-ifoPtgmH.mjs";
5
5
  import * as React from "react";
6
- import { unstable_useDocument, useLicenseLimits } from "@strapi/admin/strapi-admin";
7
- import { ModalLayout, ModalHeader, Typography, ModalBody, TextInput, ModalFooter, Button, Flex, ContentLayout, Main, HeaderLayout, Link, IconButton, SingleSelect, SingleSelectOption, Badge, Tr, Td, Icon, Tooltip, Alert, TabGroup, Box, Tabs, Tab, Divider, TabPanels, TabPanel, EmptyStateLayout, Grid, GridItem } from "@strapi/design-system";
8
- import { Menu, LinkButton, Link as Link$2 } from "@strapi/design-system/v2";
9
- import { Pencil, Trash, ArrowLeft, More, CrossCircle, CheckCircle, Plus, EmptyDocuments } from "@strapi/icons";
6
+ import { useLicenseLimits, unstable_useDocument } from "@strapi/admin/strapi-admin";
7
+ import { ModalLayout, ModalHeader, Typography, ModalBody, Flex, TextInput, Box, Checkbox, DatePicker, TimePicker, ModalFooter, Button, Combobox, ComboboxOption, Alert, Main, HeaderLayout, ContentLayout, TabGroup, Tabs, Tab, Divider, TabPanels, TabPanel, EmptyStateLayout, Grid, GridItem, Badge, Link as Link$1, IconButton, SingleSelect, SingleSelectOption, Tr, Td, Icon, Tooltip } from "@strapi/design-system";
8
+ import { Link, Menu, LinkButton } from "@strapi/design-system/v2";
9
+ import { Plus, EmptyDocuments, Pencil, Trash, ArrowLeft, More, CrossCircle, CheckCircle } from "@strapi/icons";
10
+ import format from "date-fns/format";
11
+ import { utcToZonedTime, zonedTimeToUtc } from "date-fns-tz";
10
12
  import { useIntl } from "react-intl";
11
13
  import styled from "styled-components";
12
- import { Formik, Form } from "formik";
14
+ import { formatISO } from "date-fns";
15
+ import { Formik, Form, useFormikContext } from "formik";
13
16
  import * as yup from "yup";
14
17
  import "@reduxjs/toolkit/query";
15
18
  import "axios";
@@ -17,11 +20,21 @@ import "@reduxjs/toolkit/query/react";
17
20
  import "react-redux";
18
21
  const RELEASE_SCHEMA = yup.object().shape({
19
22
  name: yup.string().trim().required(),
20
- // scheduledAt is a date, but we always receive strings from the client
21
23
  scheduledAt: yup.string().nullable(),
22
- timezone: yup.string().when("scheduledAt", {
23
- is: (scheduledAt) => !!scheduledAt,
24
- then: yup.string().required(),
24
+ isScheduled: yup.boolean().optional(),
25
+ time: yup.string().when("isScheduled", {
26
+ is: true,
27
+ then: yup.string().trim().required(),
28
+ otherwise: yup.string().nullable()
29
+ }),
30
+ timezone: yup.string().when("isScheduled", {
31
+ is: true,
32
+ then: yup.string().required().nullable(),
33
+ otherwise: yup.string().nullable()
34
+ }),
35
+ date: yup.string().when("isScheduled", {
36
+ is: true,
37
+ then: yup.string().required().nullable(),
25
38
  otherwise: yup.string().nullable()
26
39
  })
27
40
  }).required().noUnknown();
@@ -34,6 +47,22 @@ const ReleaseModal = ({
34
47
  const { formatMessage } = useIntl();
35
48
  const { pathname } = useLocation();
36
49
  const isCreatingRelease = pathname === `/plugins/${pluginId}`;
50
+ const { timezoneList, systemTimezone = { value: "UTC+00:00-Africa/Abidjan " } } = getTimezones(
51
+ initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : /* @__PURE__ */ new Date()
52
+ );
53
+ const getScheduledTimestamp = (values) => {
54
+ const { date, time, timezone } = values;
55
+ if (!date || !time || !timezone)
56
+ return null;
57
+ const timezoneWithoutOffset = timezone.split("&")[1];
58
+ return zonedTimeToUtc(`${date} ${time}`, timezoneWithoutOffset);
59
+ };
60
+ const getTimezoneWithOffset = () => {
61
+ const currentTimezone = timezoneList.find(
62
+ (timezone) => timezone.value.split("&")[1] === initialValues.timezone
63
+ );
64
+ return currentTimezone?.value || systemTimezone.value;
65
+ };
37
66
  return /* @__PURE__ */ jsxs(ModalLayout, { onClose: handleClose, labelledBy: "title", children: [
38
67
  /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { id: "title", fontWeight: "bold", textColor: "neutral800", children: formatMessage(
39
68
  {
@@ -45,45 +74,130 @@ const ReleaseModal = ({
45
74
  /* @__PURE__ */ jsx(
46
75
  Formik,
47
76
  {
48
- validateOnChange: false,
49
- onSubmit: handleSubmit,
50
- initialValues,
77
+ onSubmit: (values) => {
78
+ handleSubmit({
79
+ ...values,
80
+ timezone: values.timezone ? values.timezone.split("&")[1] : null,
81
+ scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
82
+ });
83
+ },
84
+ initialValues: {
85
+ ...initialValues,
86
+ timezone: initialValues.timezone ? getTimezoneWithOffset() : systemTimezone.value
87
+ },
51
88
  validationSchema: RELEASE_SCHEMA,
52
- children: ({ values, errors, handleChange }) => /* @__PURE__ */ jsxs(Form, { children: [
53
- /* @__PURE__ */ jsx(ModalBody, { children: /* @__PURE__ */ jsx(
54
- TextInput,
55
- {
56
- label: formatMessage({
57
- id: "content-releases.modal.form.input.label.release-name",
58
- defaultMessage: "Name"
59
- }),
60
- name: "name",
61
- value: values.name,
62
- error: errors.name,
63
- onChange: handleChange,
64
- required: true
65
- }
66
- ) }),
89
+ validateOnChange: false,
90
+ children: ({ values, errors, handleChange, setFieldValue }) => /* @__PURE__ */ jsxs(Form, { children: [
91
+ /* @__PURE__ */ jsx(ModalBody, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
92
+ /* @__PURE__ */ jsx(
93
+ TextInput,
94
+ {
95
+ label: formatMessage({
96
+ id: "content-releases.modal.form.input.label.release-name",
97
+ defaultMessage: "Name"
98
+ }),
99
+ name: "name",
100
+ value: values.name,
101
+ error: errors.name,
102
+ onChange: handleChange,
103
+ required: true
104
+ }
105
+ ),
106
+ /* @__PURE__ */ jsx(Box, { width: "max-content", children: /* @__PURE__ */ jsx(
107
+ Checkbox,
108
+ {
109
+ name: "isScheduled",
110
+ value: values.isScheduled,
111
+ onChange: (event) => {
112
+ setFieldValue("isScheduled", event.target.checked);
113
+ if (!event.target.checked) {
114
+ setFieldValue("date", null);
115
+ setFieldValue("time", "");
116
+ setFieldValue("timezone", null);
117
+ } else {
118
+ setFieldValue("date", initialValues.date);
119
+ setFieldValue("time", initialValues.time);
120
+ setFieldValue("timezone", initialValues.timezone ?? systemTimezone?.value);
121
+ }
122
+ },
123
+ children: /* @__PURE__ */ jsx(
124
+ Typography,
125
+ {
126
+ textColor: values.isScheduled ? "primary600" : "neutral800",
127
+ fontWeight: values.isScheduled ? "semiBold" : "regular",
128
+ children: formatMessage({
129
+ id: "modal.form.input.label.schedule-release",
130
+ defaultMessage: "Schedule release"
131
+ })
132
+ }
133
+ )
134
+ }
135
+ ) }),
136
+ values.isScheduled && /* @__PURE__ */ jsxs(Fragment, { children: [
137
+ /* @__PURE__ */ jsxs(Flex, { gap: 4, alignItems: "start", children: [
138
+ /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(
139
+ DatePicker,
140
+ {
141
+ label: formatMessage({
142
+ id: "content-releases.modal.form.input.label.date",
143
+ defaultMessage: "Date"
144
+ }),
145
+ name: "date",
146
+ error: errors.date,
147
+ onChange: (date) => {
148
+ const isoFormatDate = date ? formatISO(date, { representation: "date" }) : null;
149
+ setFieldValue("date", isoFormatDate);
150
+ },
151
+ clearLabel: formatMessage({
152
+ id: "content-releases.modal.form.input.clearLabel",
153
+ defaultMessage: "Clear"
154
+ }),
155
+ onClear: () => {
156
+ setFieldValue("date", null);
157
+ },
158
+ selectedDate: values.date || void 0,
159
+ required: true,
160
+ minDate: utcToZonedTime(/* @__PURE__ */ new Date(), values.timezone.split("&")[1])
161
+ }
162
+ ) }),
163
+ /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(
164
+ TimePicker,
165
+ {
166
+ label: formatMessage({
167
+ id: "content-releases.modal.form.input.label.time",
168
+ defaultMessage: "Time"
169
+ }),
170
+ name: "time",
171
+ error: errors.time,
172
+ onChange: (time) => {
173
+ setFieldValue("time", time);
174
+ },
175
+ clearLabel: formatMessage({
176
+ id: "content-releases.modal.form.input.clearLabel",
177
+ defaultMessage: "Clear"
178
+ }),
179
+ onClear: () => {
180
+ setFieldValue("time", "");
181
+ },
182
+ value: values.time || void 0,
183
+ required: true
184
+ }
185
+ ) })
186
+ ] }),
187
+ /* @__PURE__ */ jsx(TimezoneComponent, { timezoneOptions: timezoneList })
188
+ ] })
189
+ ] }) }),
67
190
  /* @__PURE__ */ jsx(
68
191
  ModalFooter,
69
192
  {
70
193
  startActions: /* @__PURE__ */ jsx(Button, { onClick: handleClose, variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }),
71
- endActions: /* @__PURE__ */ jsx(
72
- Button,
194
+ endActions: /* @__PURE__ */ jsx(Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
73
195
  {
74
- name: "submit",
75
- loading: isLoading,
76
- disabled: !values.name || values.name === initialValues.name,
77
- type: "submit",
78
- children: formatMessage(
79
- {
80
- id: "content-releases.modal.form.button.submit",
81
- defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
82
- },
83
- { isCreatingRelease }
84
- )
85
- }
86
- )
196
+ id: "content-releases.modal.form.button.submit",
197
+ defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
198
+ },
199
+ { isCreatingRelease }
200
+ ) })
87
201
  }
88
202
  )
89
203
  ] })
@@ -91,6 +205,368 @@ const ReleaseModal = ({
91
205
  )
92
206
  ] });
93
207
  };
208
+ const getTimezones = (selectedDate) => {
209
+ const timezoneList = Intl.supportedValuesOf("timeZone").map((timezone) => {
210
+ const utcOffset = getTimezoneOffset(timezone, selectedDate);
211
+ return { offset: utcOffset, value: `${utcOffset}&${timezone}` };
212
+ });
213
+ const systemTimezone = timezoneList.find(
214
+ (timezone) => timezone.value.split("&")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
215
+ );
216
+ return { timezoneList, systemTimezone };
217
+ };
218
+ const TimezoneComponent = ({ timezoneOptions }) => {
219
+ const { values, errors, setFieldValue } = useFormikContext();
220
+ const { formatMessage } = useIntl();
221
+ const [timezoneList, setTimezoneList] = React.useState(timezoneOptions);
222
+ React.useEffect(() => {
223
+ if (values.date) {
224
+ const { timezoneList: timezoneList2 } = getTimezones(new Date(values.date));
225
+ setTimezoneList(timezoneList2);
226
+ const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("&")[1] === values.timezone.split("&")[1]);
227
+ if (updatedTimezone) {
228
+ setFieldValue("timezone", updatedTimezone.value);
229
+ }
230
+ }
231
+ }, [setFieldValue, values.date, values.timezone]);
232
+ return /* @__PURE__ */ jsx(
233
+ Combobox,
234
+ {
235
+ label: formatMessage({
236
+ id: "content-releases.modal.form.input.label.timezone",
237
+ defaultMessage: "Timezone"
238
+ }),
239
+ autocomplete: { type: "list", filter: "contains" },
240
+ name: "timezone",
241
+ value: values.timezone || void 0,
242
+ textValue: values.timezone ? values.timezone.replace(/&/, " ") : void 0,
243
+ onChange: (timezone) => {
244
+ setFieldValue("timezone", timezone);
245
+ },
246
+ onTextValueChange: (timezone) => {
247
+ setFieldValue("timezone", timezone);
248
+ },
249
+ onClear: () => {
250
+ setFieldValue("timezone", "");
251
+ },
252
+ error: errors.timezone,
253
+ required: true,
254
+ children: timezoneList.map((timezone) => /* @__PURE__ */ jsx(ComboboxOption, { value: timezone.value, children: timezone.value.replace(/&/, " ") }, timezone.value))
255
+ }
256
+ );
257
+ };
258
+ const LinkCard = styled(Link)`
259
+ display: block;
260
+ `;
261
+ const CapitalizeRelativeTime = styled(RelativeTime)`
262
+ text-transform: capitalize;
263
+ `;
264
+ const getBadgeProps = (status) => {
265
+ let color;
266
+ switch (status) {
267
+ case "ready":
268
+ color = "success";
269
+ break;
270
+ case "blocked":
271
+ color = "warning";
272
+ break;
273
+ case "failed":
274
+ color = "danger";
275
+ break;
276
+ case "done":
277
+ color = "primary";
278
+ break;
279
+ case "empty":
280
+ default:
281
+ color = "neutral";
282
+ }
283
+ return {
284
+ textColor: `${color}600`,
285
+ backgroundColor: `${color}100`,
286
+ borderColor: `${color}200`
287
+ };
288
+ };
289
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
290
+ const { formatMessage } = useIntl();
291
+ if (isError) {
292
+ return /* @__PURE__ */ jsx(AnErrorOccurred, {});
293
+ }
294
+ if (releases?.length === 0) {
295
+ return /* @__PURE__ */ jsx(
296
+ EmptyStateLayout,
297
+ {
298
+ content: formatMessage(
299
+ {
300
+ id: "content-releases.page.Releases.tab.emptyEntries",
301
+ defaultMessage: "No releases"
302
+ },
303
+ {
304
+ target: sectionTitle
305
+ }
306
+ ),
307
+ icon: /* @__PURE__ */ jsx(EmptyDocuments, { width: "10rem" })
308
+ }
309
+ );
310
+ }
311
+ return /* @__PURE__ */ jsx(Grid, { gap: 4, children: releases.map(({ id, name, scheduledAt, status }) => /* @__PURE__ */ jsx(GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxs(
312
+ Flex,
313
+ {
314
+ direction: "column",
315
+ justifyContent: "space-between",
316
+ padding: 4,
317
+ hasRadius: true,
318
+ background: "neutral0",
319
+ shadow: "tableShadow",
320
+ height: "100%",
321
+ width: "100%",
322
+ alignItems: "start",
323
+ gap: 4,
324
+ children: [
325
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", gap: 1, children: [
326
+ /* @__PURE__ */ jsx(Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
327
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: scheduledAt ? /* @__PURE__ */ jsx(CapitalizeRelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
328
+ id: "content-releases.pages.Releases.not-scheduled",
329
+ defaultMessage: "Not scheduled"
330
+ }) })
331
+ ] }),
332
+ /* @__PURE__ */ jsx(Badge, { ...getBadgeProps(status), children: status })
333
+ ]
334
+ }
335
+ ) }) }, id)) });
336
+ };
337
+ const StyledAlert = styled(Alert)`
338
+ button {
339
+ display: none;
340
+ }
341
+ p + div {
342
+ margin-left: auto;
343
+ }
344
+ `;
345
+ const INITIAL_FORM_VALUES = {
346
+ name: "",
347
+ date: null,
348
+ time: "",
349
+ isScheduled: true,
350
+ scheduledAt: null,
351
+ timezone: null
352
+ };
353
+ const ReleasesPage = () => {
354
+ const tabRef = React.useRef(null);
355
+ const location = useLocation();
356
+ const [releaseModalShown, setReleaseModalShown] = React.useState(false);
357
+ const toggleNotification = useNotification();
358
+ const { formatMessage } = useIntl();
359
+ const { push, replace } = useHistory();
360
+ const { formatAPIError } = useAPIErrorHandler();
361
+ const [{ query }, setQuery] = useQueryParams();
362
+ const response = useGetReleasesQuery(query);
363
+ const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
364
+ const { getFeature } = useLicenseLimits();
365
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
366
+ const { trackUsage } = useTracking();
367
+ const { isLoading, isSuccess, isError } = response;
368
+ const activeTab = response?.currentData?.meta?.activeTab || "pending";
369
+ const activeTabIndex = ["pending", "done"].indexOf(activeTab);
370
+ React.useEffect(() => {
371
+ if (location?.state?.errors) {
372
+ toggleNotification({
373
+ type: "warning",
374
+ title: formatMessage({
375
+ id: "content-releases.pages.Releases.notification.error.title",
376
+ defaultMessage: "Your request could not be processed."
377
+ }),
378
+ message: formatMessage({
379
+ id: "content-releases.pages.Releases.notification.error.message",
380
+ defaultMessage: "Please try again or open another release."
381
+ })
382
+ });
383
+ replace({ state: null });
384
+ }
385
+ }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
386
+ React.useEffect(() => {
387
+ if (tabRef.current) {
388
+ tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
389
+ }
390
+ }, [activeTabIndex]);
391
+ const toggleAddReleaseModal = () => {
392
+ setReleaseModalShown((prev) => !prev);
393
+ };
394
+ if (isLoading) {
395
+ return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
396
+ }
397
+ const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
398
+ const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
399
+ const handleTabChange = (index) => {
400
+ setQuery({
401
+ ...query,
402
+ page: 1,
403
+ pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
404
+ filters: {
405
+ releasedAt: {
406
+ $notNull: index === 0 ? false : true
407
+ }
408
+ }
409
+ });
410
+ };
411
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
412
+ const response2 = await createRelease({
413
+ name,
414
+ scheduledAt,
415
+ timezone
416
+ });
417
+ if ("data" in response2) {
418
+ toggleNotification({
419
+ type: "success",
420
+ message: formatMessage({
421
+ id: "content-releases.modal.release-created-notification-success",
422
+ defaultMessage: "Release created."
423
+ })
424
+ });
425
+ trackUsage("didCreateRelease");
426
+ push(`/plugins/content-releases/${response2.data.data.id}`);
427
+ } else if (isAxiosError(response2.error)) {
428
+ toggleNotification({
429
+ type: "warning",
430
+ message: formatAPIError(response2.error)
431
+ });
432
+ } else {
433
+ toggleNotification({
434
+ type: "warning",
435
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
436
+ });
437
+ }
438
+ };
439
+ return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoading, children: [
440
+ /* @__PURE__ */ jsx(
441
+ HeaderLayout,
442
+ {
443
+ title: formatMessage({
444
+ id: "content-releases.pages.Releases.title",
445
+ defaultMessage: "Releases"
446
+ }),
447
+ subtitle: formatMessage({
448
+ id: "content-releases.pages.Releases.header-subtitle",
449
+ defaultMessage: "Create and manage content updates"
450
+ }),
451
+ primaryAction: /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.create, children: /* @__PURE__ */ jsx(
452
+ Button,
453
+ {
454
+ startIcon: /* @__PURE__ */ jsx(Plus, {}),
455
+ onClick: toggleAddReleaseModal,
456
+ disabled: hasReachedMaximumPendingReleases,
457
+ children: formatMessage({
458
+ id: "content-releases.header.actions.add-release",
459
+ defaultMessage: "New release"
460
+ })
461
+ }
462
+ ) })
463
+ }
464
+ ),
465
+ /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
466
+ hasReachedMaximumPendingReleases && /* @__PURE__ */ jsx(
467
+ StyledAlert,
468
+ {
469
+ marginBottom: 6,
470
+ action: /* @__PURE__ */ jsx(Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
471
+ id: "content-releases.pages.Releases.max-limit-reached.action",
472
+ defaultMessage: "Explore plans"
473
+ }) }),
474
+ title: formatMessage(
475
+ {
476
+ id: "content-releases.pages.Releases.max-limit-reached.title",
477
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
478
+ },
479
+ { number: maximumReleases }
480
+ ),
481
+ onClose: () => {
482
+ },
483
+ closeLabel: "",
484
+ children: formatMessage({
485
+ id: "content-releases.pages.Releases.max-limit-reached.message",
486
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
487
+ })
488
+ }
489
+ ),
490
+ /* @__PURE__ */ jsxs(
491
+ TabGroup,
492
+ {
493
+ label: formatMessage({
494
+ id: "content-releases.pages.Releases.tab-group.label",
495
+ defaultMessage: "Releases list"
496
+ }),
497
+ variant: "simple",
498
+ initialSelectedTabIndex: activeTabIndex,
499
+ onTabChange: handleTabChange,
500
+ ref: tabRef,
501
+ children: [
502
+ /* @__PURE__ */ jsxs(Box, { paddingBottom: 8, children: [
503
+ /* @__PURE__ */ jsxs(Tabs, { children: [
504
+ /* @__PURE__ */ jsx(Tab, { children: formatMessage(
505
+ {
506
+ id: "content-releases.pages.Releases.tab.pending",
507
+ defaultMessage: "Pending ({count})"
508
+ },
509
+ {
510
+ count: totalPendingReleases
511
+ }
512
+ ) }),
513
+ /* @__PURE__ */ jsx(Tab, { children: formatMessage({
514
+ id: "content-releases.pages.Releases.tab.done",
515
+ defaultMessage: "Done"
516
+ }) })
517
+ ] }),
518
+ /* @__PURE__ */ jsx(Divider, {})
519
+ ] }),
520
+ /* @__PURE__ */ jsxs(TabPanels, { children: [
521
+ /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
522
+ ReleasesGrid,
523
+ {
524
+ sectionTitle: "pending",
525
+ releases: response?.currentData?.data,
526
+ isError
527
+ }
528
+ ) }),
529
+ /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
530
+ ReleasesGrid,
531
+ {
532
+ sectionTitle: "done",
533
+ releases: response?.currentData?.data,
534
+ isError
535
+ }
536
+ ) })
537
+ ] })
538
+ ]
539
+ }
540
+ ),
541
+ response.currentData?.meta?.pagination?.total ? /* @__PURE__ */ jsxs(Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
542
+ /* @__PURE__ */ jsx(
543
+ PageSizeURLQuery,
544
+ {
545
+ options: ["8", "16", "32", "64"],
546
+ defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
547
+ }
548
+ ),
549
+ /* @__PURE__ */ jsx(
550
+ PaginationURLQuery,
551
+ {
552
+ pagination: {
553
+ pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
554
+ }
555
+ }
556
+ )
557
+ ] }) : null
558
+ ] }) }),
559
+ releaseModalShown && /* @__PURE__ */ jsx(
560
+ ReleaseModal,
561
+ {
562
+ handleClose: toggleAddReleaseModal,
563
+ handleSubmit: handleAddRelease,
564
+ isLoading: isSubmittingForm,
565
+ initialValues: INITIAL_FORM_VALUES
566
+ }
567
+ )
568
+ ] });
569
+ };
94
570
  const ReleaseInfoWrapper = styled(Flex)`
95
571
  align-self: stretch;
96
572
  border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
@@ -104,6 +580,10 @@ const StyledMenuItem = styled(Menu.Item)`
104
580
  span {
105
581
  color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
106
582
  }
583
+
584
+ &:hover {
585
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
586
+ }
107
587
  `;
108
588
  const PencilIcon = styled(Pencil)`
109
589
  width: ${({ theme }) => theme.spaces[3]};
@@ -170,7 +650,7 @@ const ReleaseDetailsLayout = ({
170
650
  toggleWarningSubmit,
171
651
  children
172
652
  }) => {
173
- const { formatMessage } = useIntl();
653
+ const { formatMessage, formatDate, formatTime } = useIntl();
174
654
  const { releaseId } = useParams();
175
655
  const {
176
656
  data,
@@ -216,7 +696,12 @@ const ReleaseDetailsLayout = ({
216
696
  }
217
697
  };
218
698
  const handleRefresh = () => {
219
- dispatch(releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
699
+ dispatch(
700
+ releaseApi.util.invalidateTags([
701
+ { type: "ReleaseAction", id: "LIST" },
702
+ { type: "Release", id: releaseId }
703
+ ])
704
+ );
220
705
  };
221
706
  const getCreatedByUser = () => {
222
707
  if (!release?.createdBy) {
@@ -252,19 +737,44 @@ const ReleaseDetailsLayout = ({
252
737
  }
253
738
  const totalEntries = release.actions.meta.count || 0;
254
739
  const hasCreatedByUser = Boolean(getCreatedByUser());
740
+ const isScheduled = release.scheduledAt && release.timezone;
741
+ const numberOfEntriesText = formatMessage(
742
+ {
743
+ id: "content-releases.pages.Details.header-subtitle",
744
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
745
+ },
746
+ { number: totalEntries }
747
+ );
748
+ const scheduledText = isScheduled ? formatMessage(
749
+ {
750
+ id: "content-releases.pages.ReleaseDetails.header-subtitle.scheduled",
751
+ defaultMessage: "Scheduled for {date} at {time} ({offset})"
752
+ },
753
+ {
754
+ date: formatDate(new Date(release.scheduledAt), {
755
+ weekday: "long",
756
+ day: "numeric",
757
+ month: "long",
758
+ year: "numeric",
759
+ timeZone: release.timezone
760
+ }),
761
+ time: formatTime(new Date(release.scheduledAt), {
762
+ timeZone: release.timezone,
763
+ hourCycle: "h23"
764
+ }),
765
+ offset: getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
766
+ }
767
+ ) : "";
255
768
  return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoadingDetails, children: [
256
769
  /* @__PURE__ */ jsx(
257
770
  HeaderLayout,
258
771
  {
259
772
  title: release.name,
260
- subtitle: formatMessage(
261
- {
262
- id: "content-releases.pages.Details.header-subtitle",
263
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
264
- },
265
- { number: totalEntries }
266
- ),
267
- navigationAction: /* @__PURE__ */ jsx(Link, { startIcon: /* @__PURE__ */ jsx(ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
773
+ subtitle: /* @__PURE__ */ jsxs(Flex, { gap: 2, lineHeight: 6, children: [
774
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", variant: "epsilon", children: numberOfEntriesText + (isScheduled ? ` - ${scheduledText}` : "") }),
775
+ /* @__PURE__ */ jsx(Badge, { ...getBadgeProps(release.status), children: release.status })
776
+ ] }),
777
+ navigationAction: /* @__PURE__ */ jsx(Link$1, { startIcon: /* @__PURE__ */ jsx(ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
268
778
  id: "global.back",
269
779
  defaultMessage: "Back"
270
780
  }) }),
@@ -292,44 +802,30 @@ const ReleaseDetailsLayout = ({
292
802
  justifyContent: "center",
293
803
  direction: "column",
294
804
  padding: 1,
295
- width: "100%",
296
- children: [
297
- /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(
298
- Flex,
299
- {
300
- paddingTop: 2,
301
- paddingBottom: 2,
302
- alignItems: "center",
303
- gap: 2,
304
- hasRadius: true,
305
- width: "100%",
306
- children: [
307
- /* @__PURE__ */ jsx(PencilIcon, {}),
308
- /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
309
- id: "content-releases.header.actions.edit",
310
- defaultMessage: "Edit"
311
- }) })
312
- ]
313
- }
314
- ) }),
315
- /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxs(
316
- Flex,
805
+ width: "100%",
806
+ children: [
807
+ /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
808
+ /* @__PURE__ */ jsx(PencilIcon, {}),
809
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
810
+ id: "content-releases.header.actions.edit",
811
+ defaultMessage: "Edit"
812
+ }) })
813
+ ] }) }),
814
+ /* @__PURE__ */ jsx(
815
+ StyledMenuItem,
317
816
  {
318
- paddingTop: 2,
319
- paddingBottom: 2,
320
- alignItems: "center",
321
- gap: 2,
322
- hasRadius: true,
323
- width: "100%",
324
- children: [
817
+ disabled: !canDelete,
818
+ onSelect: toggleWarningSubmit,
819
+ variant: "danger",
820
+ children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
325
821
  /* @__PURE__ */ jsx(TrashIcon, {}),
326
822
  /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
327
823
  id: "content-releases.header.actions.delete",
328
824
  defaultMessage: "Delete"
329
825
  }) })
330
- ]
826
+ ] })
331
827
  }
332
- ) })
828
+ )
333
829
  ]
334
830
  }
335
831
  ),
@@ -505,7 +1001,7 @@ const ReleaseDetailsBody = () => {
505
1001
  action: /* @__PURE__ */ jsx(
506
1002
  LinkButton,
507
1003
  {
508
- as: Link$1,
1004
+ as: Link$2,
509
1005
  to: {
510
1006
  pathname: "/content-manager"
511
1007
  },
@@ -688,7 +1184,7 @@ const ReleaseDetailsPage = () => {
688
1184
  const { releaseId } = useParams();
689
1185
  const toggleNotification = useNotification();
690
1186
  const { formatAPIError } = useAPIErrorHandler();
691
- const { push } = useHistory();
1187
+ const { replace } = useHistory();
692
1188
  const [releaseModalShown, setReleaseModalShown] = React.useState(false);
693
1189
  const [showWarningSubmit, setWarningSubmit] = React.useState(false);
694
1190
  const {
@@ -712,11 +1208,18 @@ const ReleaseDetailsPage = () => {
712
1208
  }
713
1209
  );
714
1210
  }
715
- const title = isSuccessDetails && data?.data?.name || "";
1211
+ const releaseData = isSuccessDetails && data?.data || null;
1212
+ const title = releaseData?.name || "";
1213
+ const timezone = releaseData?.timezone ?? null;
1214
+ const scheduledAt = releaseData?.scheduledAt && timezone ? utcToZonedTime(releaseData.scheduledAt, timezone) : null;
1215
+ const date = scheduledAt ? format(scheduledAt, "yyyy-MM-dd") : null;
1216
+ const time = scheduledAt ? format(scheduledAt, "HH:mm") : "";
716
1217
  const handleEditRelease = async (values) => {
717
1218
  const response = await updateRelease({
718
1219
  id: releaseId,
719
- name: values.name
1220
+ name: values.name,
1221
+ scheduledAt: values.scheduledAt,
1222
+ timezone: values.timezone
720
1223
  });
721
1224
  if ("data" in response) {
722
1225
  toggleNotification({
@@ -744,7 +1247,7 @@ const ReleaseDetailsPage = () => {
744
1247
  id: releaseId
745
1248
  });
746
1249
  if ("data" in response) {
747
- push("/plugins/content-releases");
1250
+ replace("/plugins/content-releases");
748
1251
  } else if (isAxiosError(response.error)) {
749
1252
  toggleNotification({
750
1253
  type: "warning",
@@ -770,7 +1273,14 @@ const ReleaseDetailsPage = () => {
770
1273
  handleClose: toggleEditReleaseModal,
771
1274
  handleSubmit: handleEditRelease,
772
1275
  isLoading: isLoadingDetails || isSubmittingForm,
773
- initialValues: { name: title || "" }
1276
+ initialValues: {
1277
+ name: title || "",
1278
+ scheduledAt,
1279
+ date,
1280
+ time,
1281
+ isScheduled: Boolean(scheduledAt),
1282
+ timezone
1283
+ }
774
1284
  }
775
1285
  ),
776
1286
  /* @__PURE__ */ jsx(
@@ -790,281 +1300,6 @@ const ReleaseDetailsPage = () => {
790
1300
  }
791
1301
  );
792
1302
  };
793
- const LinkCard = styled(Link$2)`
794
- display: block;
795
- `;
796
- const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
797
- const { formatMessage } = useIntl();
798
- if (isError) {
799
- return /* @__PURE__ */ jsx(AnErrorOccurred, {});
800
- }
801
- if (releases?.length === 0) {
802
- return /* @__PURE__ */ jsx(
803
- EmptyStateLayout,
804
- {
805
- content: formatMessage(
806
- {
807
- id: "content-releases.page.Releases.tab.emptyEntries",
808
- defaultMessage: "No releases"
809
- },
810
- {
811
- target: sectionTitle
812
- }
813
- ),
814
- icon: /* @__PURE__ */ jsx(EmptyDocuments, { width: "10rem" })
815
- }
816
- );
817
- }
818
- return /* @__PURE__ */ jsx(Grid, { gap: 4, children: releases.map(({ id, name, actions }) => /* @__PURE__ */ jsx(GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxs(
819
- Flex,
820
- {
821
- direction: "column",
822
- justifyContent: "space-between",
823
- padding: 4,
824
- hasRadius: true,
825
- background: "neutral0",
826
- shadow: "tableShadow",
827
- height: "100%",
828
- width: "100%",
829
- alignItems: "start",
830
- gap: 2,
831
- children: [
832
- /* @__PURE__ */ jsx(Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
833
- /* @__PURE__ */ jsx(Typography, { variant: "pi", children: formatMessage(
834
- {
835
- id: "content-releases.page.Releases.release-item.entries",
836
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
837
- },
838
- { number: actions.meta.count }
839
- ) })
840
- ]
841
- }
842
- ) }) }, id)) });
843
- };
844
- const StyledAlert = styled(Alert)`
845
- button {
846
- display: none;
847
- }
848
- p + div {
849
- margin-left: auto;
850
- }
851
- `;
852
- const INITIAL_FORM_VALUES = {
853
- name: ""
854
- };
855
- const ReleasesPage = () => {
856
- const tabRef = React.useRef(null);
857
- const location = useLocation();
858
- const [releaseModalShown, setReleaseModalShown] = React.useState(false);
859
- const toggleNotification = useNotification();
860
- const { formatMessage } = useIntl();
861
- const { push, replace } = useHistory();
862
- const { formatAPIError } = useAPIErrorHandler();
863
- const [{ query }, setQuery] = useQueryParams();
864
- const response = useGetReleasesQuery(query);
865
- const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
866
- const { getFeature } = useLicenseLimits();
867
- const { maximumReleases = 3 } = getFeature("cms-content-releases");
868
- const { trackUsage } = useTracking();
869
- const { isLoading, isSuccess, isError } = response;
870
- const activeTab = response?.currentData?.meta?.activeTab || "pending";
871
- const activeTabIndex = ["pending", "done"].indexOf(activeTab);
872
- React.useEffect(() => {
873
- if (location?.state?.errors) {
874
- toggleNotification({
875
- type: "warning",
876
- title: formatMessage({
877
- id: "content-releases.pages.Releases.notification.error.title",
878
- defaultMessage: "Your request could not be processed."
879
- }),
880
- message: formatMessage({
881
- id: "content-releases.pages.Releases.notification.error.message",
882
- defaultMessage: "Please try again or open another release."
883
- })
884
- });
885
- replace({ state: null });
886
- }
887
- }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
888
- React.useEffect(() => {
889
- if (tabRef.current) {
890
- tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
891
- }
892
- }, [activeTabIndex]);
893
- const toggleAddReleaseModal = () => {
894
- setReleaseModalShown((prev) => !prev);
895
- };
896
- if (isLoading) {
897
- return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
898
- }
899
- const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
900
- const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
901
- const handleTabChange = (index) => {
902
- setQuery({
903
- ...query,
904
- page: 1,
905
- pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
906
- filters: {
907
- releasedAt: {
908
- $notNull: index === 0 ? false : true
909
- }
910
- }
911
- });
912
- };
913
- const handleAddRelease = async (values) => {
914
- const response2 = await createRelease({
915
- name: values.name
916
- });
917
- if ("data" in response2) {
918
- toggleNotification({
919
- type: "success",
920
- message: formatMessage({
921
- id: "content-releases.modal.release-created-notification-success",
922
- defaultMessage: "Release created."
923
- })
924
- });
925
- trackUsage("didCreateRelease");
926
- push(`/plugins/content-releases/${response2.data.data.id}`);
927
- } else if (isAxiosError(response2.error)) {
928
- toggleNotification({
929
- type: "warning",
930
- message: formatAPIError(response2.error)
931
- });
932
- } else {
933
- toggleNotification({
934
- type: "warning",
935
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
936
- });
937
- }
938
- };
939
- return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoading, children: [
940
- /* @__PURE__ */ jsx(
941
- HeaderLayout,
942
- {
943
- title: formatMessage({
944
- id: "content-releases.pages.Releases.title",
945
- defaultMessage: "Releases"
946
- }),
947
- subtitle: formatMessage(
948
- {
949
- id: "content-releases.pages.Releases.header-subtitle",
950
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
951
- },
952
- { number: totalReleases }
953
- ),
954
- primaryAction: /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.create, children: /* @__PURE__ */ jsx(
955
- Button,
956
- {
957
- startIcon: /* @__PURE__ */ jsx(Plus, {}),
958
- onClick: toggleAddReleaseModal,
959
- disabled: hasReachedMaximumPendingReleases,
960
- children: formatMessage({
961
- id: "content-releases.header.actions.add-release",
962
- defaultMessage: "New release"
963
- })
964
- }
965
- ) })
966
- }
967
- ),
968
- /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
969
- activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsx(
970
- StyledAlert,
971
- {
972
- marginBottom: 6,
973
- action: /* @__PURE__ */ jsx(Link$2, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
974
- id: "content-releases.pages.Releases.max-limit-reached.action",
975
- defaultMessage: "Explore plans"
976
- }) }),
977
- title: formatMessage(
978
- {
979
- id: "content-releases.pages.Releases.max-limit-reached.title",
980
- defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
981
- },
982
- { number: maximumReleases }
983
- ),
984
- onClose: () => {
985
- },
986
- closeLabel: "",
987
- children: formatMessage({
988
- id: "content-releases.pages.Releases.max-limit-reached.message",
989
- defaultMessage: "Upgrade to manage an unlimited number of releases."
990
- })
991
- }
992
- ),
993
- /* @__PURE__ */ jsxs(
994
- TabGroup,
995
- {
996
- label: formatMessage({
997
- id: "content-releases.pages.Releases.tab-group.label",
998
- defaultMessage: "Releases list"
999
- }),
1000
- variant: "simple",
1001
- initialSelectedTabIndex: activeTabIndex,
1002
- onTabChange: handleTabChange,
1003
- ref: tabRef,
1004
- children: [
1005
- /* @__PURE__ */ jsxs(Box, { paddingBottom: 8, children: [
1006
- /* @__PURE__ */ jsxs(Tabs, { children: [
1007
- /* @__PURE__ */ jsx(Tab, { children: formatMessage({
1008
- id: "content-releases.pages.Releases.tab.pending",
1009
- defaultMessage: "Pending"
1010
- }) }),
1011
- /* @__PURE__ */ jsx(Tab, { children: formatMessage({
1012
- id: "content-releases.pages.Releases.tab.done",
1013
- defaultMessage: "Done"
1014
- }) })
1015
- ] }),
1016
- /* @__PURE__ */ jsx(Divider, {})
1017
- ] }),
1018
- /* @__PURE__ */ jsxs(TabPanels, { children: [
1019
- /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
1020
- ReleasesGrid,
1021
- {
1022
- sectionTitle: "pending",
1023
- releases: response?.currentData?.data,
1024
- isError
1025
- }
1026
- ) }),
1027
- /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
1028
- ReleasesGrid,
1029
- {
1030
- sectionTitle: "done",
1031
- releases: response?.currentData?.data,
1032
- isError
1033
- }
1034
- ) })
1035
- ] })
1036
- ]
1037
- }
1038
- ),
1039
- totalReleases > 0 && /* @__PURE__ */ jsxs(Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1040
- /* @__PURE__ */ jsx(
1041
- PageSizeURLQuery,
1042
- {
1043
- options: ["8", "16", "32", "64"],
1044
- defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
1045
- }
1046
- ),
1047
- /* @__PURE__ */ jsx(
1048
- PaginationURLQuery,
1049
- {
1050
- pagination: {
1051
- pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
1052
- }
1053
- }
1054
- )
1055
- ] })
1056
- ] }) }),
1057
- releaseModalShown && /* @__PURE__ */ jsx(
1058
- ReleaseModal,
1059
- {
1060
- handleClose: toggleAddReleaseModal,
1061
- handleSubmit: handleAddRelease,
1062
- isLoading: isSubmittingForm,
1063
- initialValues: INITIAL_FORM_VALUES
1064
- }
1065
- )
1066
- ] });
1067
- };
1068
1303
  const App = () => {
1069
1304
  return /* @__PURE__ */ jsx(CheckPagePermissions, { permissions: PERMISSIONS.main, children: /* @__PURE__ */ jsxs(Switch, { children: [
1070
1305
  /* @__PURE__ */ jsx(Route, { exact: true, path: `/plugins/${pluginId}`, component: ReleasesPage }),
@@ -1074,4 +1309,4 @@ const App = () => {
1074
1309
  export {
1075
1310
  App
1076
1311
  };
1077
- //# sourceMappingURL=App-U6GbyLIE.mjs.map
1312
+ //# sourceMappingURL=App-xQ5ljY7-.mjs.map