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

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