@strapi/content-releases 0.0.0-next.95a939e004e74915357523e3adb118a31fef57ed → 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.
@@ -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-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,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,24 @@ const ReleaseModal = ({
34
47
  const { formatMessage } = useIntl();
35
48
  const { pathname } = useLocation();
36
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
+ };
37
68
  return /* @__PURE__ */ jsxs(ModalLayout, { onClose: handleClose, labelledBy: "title", children: [
38
69
  /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { id: "title", fontWeight: "bold", textColor: "neutral800", children: formatMessage(
39
70
  {
@@ -45,45 +76,135 @@ const ReleaseModal = ({
45
76
  /* @__PURE__ */ jsx(
46
77
  Formik,
47
78
  {
48
- validateOnChange: false,
49
- onSubmit: handleSubmit,
50
- 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
+ },
51
90
  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
- ) }),
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
+ ] }) }),
67
197
  /* @__PURE__ */ jsx(
68
198
  ModalFooter,
69
199
  {
70
200
  startActions: /* @__PURE__ */ jsx(Button, { onClick: handleClose, variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }),
71
- endActions: /* @__PURE__ */ jsx(
72
- Button,
201
+ endActions: /* @__PURE__ */ jsx(Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
73
202
  {
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
- )
203
+ id: "content-releases.modal.form.button.submit",
204
+ defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
205
+ },
206
+ { isCreatingRelease }
207
+ ) })
87
208
  }
88
209
  )
89
210
  ] })
@@ -91,6 +212,376 @@ const ReleaseModal = ({
91
212
  )
92
213
  ] });
93
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
+ };
94
585
  const ReleaseInfoWrapper = styled(Flex)`
95
586
  align-self: stretch;
96
587
  border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
@@ -104,6 +595,10 @@ const StyledMenuItem = styled(Menu.Item)`
104
595
  span {
105
596
  color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
106
597
  }
598
+
599
+ &:hover {
600
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
601
+ }
107
602
  `;
108
603
  const PencilIcon = styled(Pencil)`
109
604
  width: ${({ theme }) => theme.spaces[3]};
@@ -170,7 +665,7 @@ const ReleaseDetailsLayout = ({
170
665
  toggleWarningSubmit,
171
666
  children
172
667
  }) => {
173
- const { formatMessage } = useIntl();
668
+ const { formatMessage, formatDate, formatTime } = useIntl();
174
669
  const { releaseId } = useParams();
175
670
  const {
176
671
  data,
@@ -252,19 +747,45 @@ const ReleaseDetailsLayout = ({
252
747
  }
253
748
  const totalEntries = release.actions.meta.count || 0;
254
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
+ ) : "";
255
779
  return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoadingDetails, children: [
256
780
  /* @__PURE__ */ jsx(
257
781
  HeaderLayout,
258
782
  {
259
783
  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({
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({
268
789
  id: "global.back",
269
790
  defaultMessage: "Back"
270
791
  }) }),
@@ -292,44 +813,30 @@ const ReleaseDetailsLayout = ({
292
813
  justifyContent: "center",
293
814
  direction: "column",
294
815
  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,
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,
317
827
  {
318
- paddingTop: 2,
319
- paddingBottom: 2,
320
- alignItems: "center",
321
- gap: 2,
322
- hasRadius: true,
323
- width: "100%",
324
- children: [
828
+ disabled: !canDelete,
829
+ onSelect: toggleWarningSubmit,
830
+ variant: "danger",
831
+ children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
325
832
  /* @__PURE__ */ jsx(TrashIcon, {}),
326
833
  /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
327
834
  id: "content-releases.header.actions.delete",
328
835
  defaultMessage: "Delete"
329
836
  }) })
330
- ]
837
+ ] })
331
838
  }
332
- ) })
839
+ )
333
840
  ]
334
841
  }
335
842
  ),
@@ -505,7 +1012,7 @@ const ReleaseDetailsBody = () => {
505
1012
  action: /* @__PURE__ */ jsx(
506
1013
  LinkButton,
507
1014
  {
508
- as: Link$1,
1015
+ as: Link$2,
509
1016
  to: {
510
1017
  pathname: "/content-manager"
511
1018
  },
@@ -688,7 +1195,7 @@ const ReleaseDetailsPage = () => {
688
1195
  const { releaseId } = useParams();
689
1196
  const toggleNotification = useNotification();
690
1197
  const { formatAPIError } = useAPIErrorHandler();
691
- const { push } = useHistory();
1198
+ const { replace } = useHistory();
692
1199
  const [releaseModalShown, setReleaseModalShown] = React.useState(false);
693
1200
  const [showWarningSubmit, setWarningSubmit] = React.useState(false);
694
1201
  const {
@@ -712,11 +1219,18 @@ const ReleaseDetailsPage = () => {
712
1219
  }
713
1220
  );
714
1221
  }
715
- 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") : "";
716
1228
  const handleEditRelease = async (values) => {
717
1229
  const response = await updateRelease({
718
1230
  id: releaseId,
719
- name: values.name
1231
+ name: values.name,
1232
+ scheduledAt: values.scheduledAt,
1233
+ timezone: values.timezone
720
1234
  });
721
1235
  if ("data" in response) {
722
1236
  toggleNotification({
@@ -744,7 +1258,7 @@ const ReleaseDetailsPage = () => {
744
1258
  id: releaseId
745
1259
  });
746
1260
  if ("data" in response) {
747
- push("/plugins/content-releases");
1261
+ replace("/plugins/content-releases");
748
1262
  } else if (isAxiosError(response.error)) {
749
1263
  toggleNotification({
750
1264
  type: "warning",
@@ -770,7 +1284,14 @@ const ReleaseDetailsPage = () => {
770
1284
  handleClose: toggleEditReleaseModal,
771
1285
  handleSubmit: handleEditRelease,
772
1286
  isLoading: isLoadingDetails || isSubmittingForm,
773
- initialValues: { name: title || "" }
1287
+ initialValues: {
1288
+ name: title || "",
1289
+ scheduledAt,
1290
+ date,
1291
+ time,
1292
+ isScheduled: Boolean(scheduledAt),
1293
+ timezone
1294
+ }
774
1295
  }
775
1296
  ),
776
1297
  /* @__PURE__ */ jsx(
@@ -790,281 +1311,6 @@ const ReleaseDetailsPage = () => {
790
1311
  }
791
1312
  );
792
1313
  };
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
1314
  const App = () => {
1069
1315
  return /* @__PURE__ */ jsx(CheckPagePermissions, { permissions: PERMISSIONS.main, children: /* @__PURE__ */ jsxs(Switch, { children: [
1070
1316
  /* @__PURE__ */ jsx(Route, { exact: true, path: `/plugins/${pluginId}`, component: ReleasesPage }),
@@ -1074,4 +1320,4 @@ const App = () => {
1074
1320
  export {
1075
1321
  App
1076
1322
  };
1077
- //# sourceMappingURL=App-U6GbyLIE.mjs.map
1323
+ //# sourceMappingURL=App-bpzO2Ljh.mjs.map