@strapi/content-releases 0.0.0-next.677a639124e715b4bd6bb862d1ef6536358bed8b → 0.0.0-next.68c52dc267ceb9c5b8183117142351a1b09176c5

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-pspKUC-W.js → App-OP70yd5M.js} +600 -356
  2. package/dist/_chunks/App-OP70yd5M.js.map +1 -0
  3. package/dist/_chunks/{App-8FCxPK8-.mjs → App-x6Tjj3HN.mjs} +611 -368
  4. package/dist/_chunks/App-x6Tjj3HN.mjs.map +1 -0
  5. package/dist/_chunks/PurchaseContentReleases-3tRbmbY3.mjs +51 -0
  6. package/dist/_chunks/PurchaseContentReleases-3tRbmbY3.mjs.map +1 -0
  7. package/dist/_chunks/PurchaseContentReleases-bpIYXOfu.js +51 -0
  8. package/dist/_chunks/PurchaseContentReleases-bpIYXOfu.js.map +1 -0
  9. package/dist/_chunks/{en-r9YocBH0.js → en-3SGjiVyR.js} +23 -5
  10. package/dist/_chunks/en-3SGjiVyR.js.map +1 -0
  11. package/dist/_chunks/{en-m9eTk4UF.mjs → en-bpHsnU0n.mjs} +23 -5
  12. package/dist/_chunks/en-bpHsnU0n.mjs.map +1 -0
  13. package/dist/_chunks/{index-8aK7GzI5.mjs → index-1ejXLtzt.mjs} +291 -24
  14. package/dist/_chunks/index-1ejXLtzt.mjs.map +1 -0
  15. package/dist/_chunks/{index-nGaPcY9m.js → index-ydocdaZ0.js} +279 -12
  16. package/dist/_chunks/index-ydocdaZ0.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 +885 -437
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +884 -437
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +14 -12
  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 as RelativeTime$1, 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-1ejXLtzt.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,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,22 @@ const ReleaseModal = ({
29
47
  const { formatMessage } = useIntl();
30
48
  const { pathname } = useLocation();
31
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
+ };
32
66
  return /* @__PURE__ */ jsxs(ModalLayout, { onClose: handleClose, labelledBy: "title", children: [
33
67
  /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(Typography, { id: "title", fontWeight: "bold", textColor: "neutral800", children: formatMessage(
34
68
  {
@@ -40,45 +74,130 @@ const ReleaseModal = ({
40
74
  /* @__PURE__ */ jsx(
41
75
  Formik,
42
76
  {
43
- validateOnChange: false,
44
- onSubmit: handleSubmit,
45
- 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
+ },
46
88
  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
- ) }),
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
+ ] }) }),
62
190
  /* @__PURE__ */ jsx(
63
191
  ModalFooter,
64
192
  {
65
193
  startActions: /* @__PURE__ */ jsx(Button, { onClick: handleClose, variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }),
66
- endActions: /* @__PURE__ */ jsx(
67
- Button,
194
+ endActions: /* @__PURE__ */ jsx(Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
68
195
  {
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
- )
196
+ id: "content-releases.modal.form.button.submit",
197
+ defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
198
+ },
199
+ { isCreatingRelease }
200
+ ) })
82
201
  }
83
202
  )
84
203
  ] })
@@ -86,6 +205,371 @@ const ReleaseModal = ({
86
205
  )
87
206
  ] });
88
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 RelativeTime = styled(RelativeTime$1)`
262
+ display: inline-block;
263
+ &::first-letter {
264
+ text-transform: uppercase;
265
+ }
266
+ `;
267
+ const getBadgeProps = (status) => {
268
+ let color;
269
+ switch (status) {
270
+ case "ready":
271
+ color = "success";
272
+ break;
273
+ case "blocked":
274
+ color = "warning";
275
+ break;
276
+ case "failed":
277
+ color = "danger";
278
+ break;
279
+ case "done":
280
+ color = "primary";
281
+ break;
282
+ case "empty":
283
+ default:
284
+ color = "neutral";
285
+ }
286
+ return {
287
+ textColor: `${color}600`,
288
+ backgroundColor: `${color}100`,
289
+ borderColor: `${color}200`
290
+ };
291
+ };
292
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
293
+ const { formatMessage } = useIntl();
294
+ if (isError) {
295
+ return /* @__PURE__ */ jsx(AnErrorOccurred, {});
296
+ }
297
+ if (releases?.length === 0) {
298
+ return /* @__PURE__ */ jsx(
299
+ EmptyStateLayout,
300
+ {
301
+ content: formatMessage(
302
+ {
303
+ id: "content-releases.page.Releases.tab.emptyEntries",
304
+ defaultMessage: "No releases"
305
+ },
306
+ {
307
+ target: sectionTitle
308
+ }
309
+ ),
310
+ icon: /* @__PURE__ */ jsx(EmptyDocuments, { width: "10rem" })
311
+ }
312
+ );
313
+ }
314
+ 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(
315
+ Flex,
316
+ {
317
+ direction: "column",
318
+ justifyContent: "space-between",
319
+ padding: 4,
320
+ hasRadius: true,
321
+ background: "neutral0",
322
+ shadow: "tableShadow",
323
+ height: "100%",
324
+ width: "100%",
325
+ alignItems: "start",
326
+ gap: 4,
327
+ children: [
328
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", gap: 1, children: [
329
+ /* @__PURE__ */ jsx(Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
330
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: scheduledAt ? /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
331
+ id: "content-releases.pages.Releases.not-scheduled",
332
+ defaultMessage: "Not scheduled"
333
+ }) })
334
+ ] }),
335
+ /* @__PURE__ */ jsx(Badge, { ...getBadgeProps(status), children: status })
336
+ ]
337
+ }
338
+ ) }) }, id)) });
339
+ };
340
+ const StyledAlert = styled(Alert)`
341
+ button {
342
+ display: none;
343
+ }
344
+ p + div {
345
+ margin-left: auto;
346
+ }
347
+ `;
348
+ const INITIAL_FORM_VALUES = {
349
+ name: "",
350
+ date: null,
351
+ time: "",
352
+ isScheduled: true,
353
+ scheduledAt: null,
354
+ timezone: null
355
+ };
356
+ const ReleasesPage = () => {
357
+ const tabRef = React.useRef(null);
358
+ const location = useLocation();
359
+ const [releaseModalShown, setReleaseModalShown] = React.useState(false);
360
+ const toggleNotification = useNotification();
361
+ const { formatMessage } = useIntl();
362
+ const { push, replace } = useHistory();
363
+ const { formatAPIError } = useAPIErrorHandler();
364
+ const [{ query }, setQuery] = useQueryParams();
365
+ const response = useGetReleasesQuery(query);
366
+ const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
367
+ const { getFeature } = useLicenseLimits();
368
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
369
+ const { trackUsage } = useTracking();
370
+ const { isLoading, isSuccess, isError } = response;
371
+ const activeTab = response?.currentData?.meta?.activeTab || "pending";
372
+ const activeTabIndex = ["pending", "done"].indexOf(activeTab);
373
+ React.useEffect(() => {
374
+ if (location?.state?.errors) {
375
+ toggleNotification({
376
+ type: "warning",
377
+ title: formatMessage({
378
+ id: "content-releases.pages.Releases.notification.error.title",
379
+ defaultMessage: "Your request could not be processed."
380
+ }),
381
+ message: formatMessage({
382
+ id: "content-releases.pages.Releases.notification.error.message",
383
+ defaultMessage: "Please try again or open another release."
384
+ })
385
+ });
386
+ replace({ state: null });
387
+ }
388
+ }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
389
+ React.useEffect(() => {
390
+ if (tabRef.current) {
391
+ tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
392
+ }
393
+ }, [activeTabIndex]);
394
+ const toggleAddReleaseModal = () => {
395
+ setReleaseModalShown((prev) => !prev);
396
+ };
397
+ if (isLoading) {
398
+ return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
399
+ }
400
+ const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
401
+ const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
402
+ const handleTabChange = (index) => {
403
+ setQuery({
404
+ ...query,
405
+ page: 1,
406
+ pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
407
+ filters: {
408
+ releasedAt: {
409
+ $notNull: index === 0 ? false : true
410
+ }
411
+ }
412
+ });
413
+ };
414
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
415
+ const response2 = await createRelease({
416
+ name,
417
+ scheduledAt,
418
+ timezone
419
+ });
420
+ if ("data" in response2) {
421
+ toggleNotification({
422
+ type: "success",
423
+ message: formatMessage({
424
+ id: "content-releases.modal.release-created-notification-success",
425
+ defaultMessage: "Release created."
426
+ })
427
+ });
428
+ trackUsage("didCreateRelease");
429
+ push(`/plugins/content-releases/${response2.data.data.id}`);
430
+ } else if (isAxiosError(response2.error)) {
431
+ toggleNotification({
432
+ type: "warning",
433
+ message: formatAPIError(response2.error)
434
+ });
435
+ } else {
436
+ toggleNotification({
437
+ type: "warning",
438
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
439
+ });
440
+ }
441
+ };
442
+ return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoading, children: [
443
+ /* @__PURE__ */ jsx(
444
+ HeaderLayout,
445
+ {
446
+ title: formatMessage({
447
+ id: "content-releases.pages.Releases.title",
448
+ defaultMessage: "Releases"
449
+ }),
450
+ subtitle: formatMessage({
451
+ id: "content-releases.pages.Releases.header-subtitle",
452
+ defaultMessage: "Create and manage content updates"
453
+ }),
454
+ primaryAction: /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.create, children: /* @__PURE__ */ jsx(
455
+ Button,
456
+ {
457
+ startIcon: /* @__PURE__ */ jsx(Plus, {}),
458
+ onClick: toggleAddReleaseModal,
459
+ disabled: hasReachedMaximumPendingReleases,
460
+ children: formatMessage({
461
+ id: "content-releases.header.actions.add-release",
462
+ defaultMessage: "New release"
463
+ })
464
+ }
465
+ ) })
466
+ }
467
+ ),
468
+ /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
469
+ hasReachedMaximumPendingReleases && /* @__PURE__ */ jsx(
470
+ StyledAlert,
471
+ {
472
+ marginBottom: 6,
473
+ action: /* @__PURE__ */ jsx(Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
474
+ id: "content-releases.pages.Releases.max-limit-reached.action",
475
+ defaultMessage: "Explore plans"
476
+ }) }),
477
+ title: formatMessage(
478
+ {
479
+ id: "content-releases.pages.Releases.max-limit-reached.title",
480
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
481
+ },
482
+ { number: maximumReleases }
483
+ ),
484
+ onClose: () => {
485
+ },
486
+ closeLabel: "",
487
+ children: formatMessage({
488
+ id: "content-releases.pages.Releases.max-limit-reached.message",
489
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
490
+ })
491
+ }
492
+ ),
493
+ /* @__PURE__ */ jsxs(
494
+ TabGroup,
495
+ {
496
+ label: formatMessage({
497
+ id: "content-releases.pages.Releases.tab-group.label",
498
+ defaultMessage: "Releases list"
499
+ }),
500
+ variant: "simple",
501
+ initialSelectedTabIndex: activeTabIndex,
502
+ onTabChange: handleTabChange,
503
+ ref: tabRef,
504
+ children: [
505
+ /* @__PURE__ */ jsxs(Box, { paddingBottom: 8, children: [
506
+ /* @__PURE__ */ jsxs(Tabs, { children: [
507
+ /* @__PURE__ */ jsx(Tab, { children: formatMessage(
508
+ {
509
+ id: "content-releases.pages.Releases.tab.pending",
510
+ defaultMessage: "Pending ({count})"
511
+ },
512
+ {
513
+ count: totalPendingReleases
514
+ }
515
+ ) }),
516
+ /* @__PURE__ */ jsx(Tab, { children: formatMessage({
517
+ id: "content-releases.pages.Releases.tab.done",
518
+ defaultMessage: "Done"
519
+ }) })
520
+ ] }),
521
+ /* @__PURE__ */ jsx(Divider, {})
522
+ ] }),
523
+ /* @__PURE__ */ jsxs(TabPanels, { children: [
524
+ /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
525
+ ReleasesGrid,
526
+ {
527
+ sectionTitle: "pending",
528
+ releases: response?.currentData?.data,
529
+ isError
530
+ }
531
+ ) }),
532
+ /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
533
+ ReleasesGrid,
534
+ {
535
+ sectionTitle: "done",
536
+ releases: response?.currentData?.data,
537
+ isError
538
+ }
539
+ ) })
540
+ ] })
541
+ ]
542
+ }
543
+ ),
544
+ response.currentData?.meta?.pagination?.total ? /* @__PURE__ */ jsxs(Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
545
+ /* @__PURE__ */ jsx(
546
+ PageSizeURLQuery,
547
+ {
548
+ options: ["8", "16", "32", "64"],
549
+ defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
550
+ }
551
+ ),
552
+ /* @__PURE__ */ jsx(
553
+ PaginationURLQuery,
554
+ {
555
+ pagination: {
556
+ pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
557
+ }
558
+ }
559
+ )
560
+ ] }) : null
561
+ ] }) }),
562
+ releaseModalShown && /* @__PURE__ */ jsx(
563
+ ReleaseModal,
564
+ {
565
+ handleClose: toggleAddReleaseModal,
566
+ handleSubmit: handleAddRelease,
567
+ isLoading: isSubmittingForm,
568
+ initialValues: INITIAL_FORM_VALUES
569
+ }
570
+ )
571
+ ] });
572
+ };
89
573
  const ReleaseInfoWrapper = styled(Flex)`
90
574
  align-self: stretch;
91
575
  border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
@@ -99,6 +583,10 @@ const StyledMenuItem = styled(Menu.Item)`
99
583
  span {
100
584
  color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
101
585
  }
586
+
587
+ &:hover {
588
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
589
+ }
102
590
  `;
103
591
  const PencilIcon = styled(Pencil)`
104
592
  width: ${({ theme }) => theme.spaces[3]};
@@ -165,7 +653,7 @@ const ReleaseDetailsLayout = ({
165
653
  toggleWarningSubmit,
166
654
  children
167
655
  }) => {
168
- const { formatMessage } = useIntl();
656
+ const { formatMessage, formatDate, formatTime } = useIntl();
169
657
  const { releaseId } = useParams();
170
658
  const {
171
659
  data,
@@ -211,7 +699,12 @@ const ReleaseDetailsLayout = ({
211
699
  }
212
700
  };
213
701
  const handleRefresh = () => {
214
- dispatch(releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
702
+ dispatch(
703
+ releaseApi.util.invalidateTags([
704
+ { type: "ReleaseAction", id: "LIST" },
705
+ { type: "Release", id: releaseId }
706
+ ])
707
+ );
215
708
  };
216
709
  const getCreatedByUser = () => {
217
710
  if (!release?.createdBy) {
@@ -247,19 +740,44 @@ const ReleaseDetailsLayout = ({
247
740
  }
248
741
  const totalEntries = release.actions.meta.count || 0;
249
742
  const hasCreatedByUser = Boolean(getCreatedByUser());
743
+ const isScheduled = release.scheduledAt && release.timezone;
744
+ const numberOfEntriesText = formatMessage(
745
+ {
746
+ id: "content-releases.pages.Details.header-subtitle",
747
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
748
+ },
749
+ { number: totalEntries }
750
+ );
751
+ const scheduledText = isScheduled ? formatMessage(
752
+ {
753
+ id: "content-releases.pages.ReleaseDetails.header-subtitle.scheduled",
754
+ defaultMessage: "Scheduled for {date} at {time} ({offset})"
755
+ },
756
+ {
757
+ date: formatDate(new Date(release.scheduledAt), {
758
+ weekday: "long",
759
+ day: "numeric",
760
+ month: "long",
761
+ year: "numeric",
762
+ timeZone: release.timezone
763
+ }),
764
+ time: formatTime(new Date(release.scheduledAt), {
765
+ timeZone: release.timezone,
766
+ hourCycle: "h23"
767
+ }),
768
+ offset: getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
769
+ }
770
+ ) : "";
250
771
  return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoadingDetails, children: [
251
772
  /* @__PURE__ */ jsx(
252
773
  HeaderLayout,
253
774
  {
254
775
  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({
776
+ subtitle: /* @__PURE__ */ jsxs(Flex, { gap: 2, lineHeight: 6, children: [
777
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", variant: "epsilon", children: numberOfEntriesText + (isScheduled ? ` - ${scheduledText}` : "") }),
778
+ /* @__PURE__ */ jsx(Badge, { ...getBadgeProps(release.status), children: release.status })
779
+ ] }),
780
+ navigationAction: /* @__PURE__ */ jsx(Link$1, { startIcon: /* @__PURE__ */ jsx(ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
263
781
  id: "global.back",
264
782
  defaultMessage: "Back"
265
783
  }) }),
@@ -287,44 +805,30 @@ const ReleaseDetailsLayout = ({
287
805
  justifyContent: "center",
288
806
  direction: "column",
289
807
  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,
808
+ width: "100%",
809
+ children: [
810
+ /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
811
+ /* @__PURE__ */ jsx(PencilIcon, {}),
812
+ /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
813
+ id: "content-releases.header.actions.edit",
814
+ defaultMessage: "Edit"
815
+ }) })
816
+ ] }) }),
817
+ /* @__PURE__ */ jsx(
818
+ StyledMenuItem,
312
819
  {
313
- paddingTop: 2,
314
- paddingBottom: 2,
315
- alignItems: "center",
316
- gap: 2,
317
- hasRadius: true,
318
- width: "100%",
319
- children: [
820
+ disabled: !canDelete,
821
+ onSelect: toggleWarningSubmit,
822
+ variant: "danger",
823
+ children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
320
824
  /* @__PURE__ */ jsx(TrashIcon, {}),
321
825
  /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
322
826
  id: "content-releases.header.actions.delete",
323
827
  defaultMessage: "Delete"
324
828
  }) })
325
- ]
829
+ ] })
326
830
  }
327
- ) })
831
+ )
328
832
  ]
329
833
  }
330
834
  ),
@@ -342,7 +846,7 @@ const ReleaseDetailsLayout = ({
342
846
  defaultMessage: "Created"
343
847
  }) }),
344
848
  /* @__PURE__ */ jsxs(Typography, { variant: "pi", color: "neutral300", children: [
345
- /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(release.createdAt) }),
849
+ /* @__PURE__ */ jsx(RelativeTime$1, { timestamp: new Date(release.createdAt) }),
346
850
  formatMessage(
347
851
  {
348
852
  id: "content-releases.header.actions.created.description",
@@ -500,7 +1004,7 @@ const ReleaseDetailsBody = () => {
500
1004
  action: /* @__PURE__ */ jsx(
501
1005
  LinkButton,
502
1006
  {
503
- as: Link$1,
1007
+ as: Link$2,
504
1008
  to: {
505
1009
  pathname: "/content-manager"
506
1010
  },
@@ -683,7 +1187,7 @@ const ReleaseDetailsPage = () => {
683
1187
  const { releaseId } = useParams();
684
1188
  const toggleNotification = useNotification();
685
1189
  const { formatAPIError } = useAPIErrorHandler();
686
- const { push } = useHistory();
1190
+ const { replace } = useHistory();
687
1191
  const [releaseModalShown, setReleaseModalShown] = React.useState(false);
688
1192
  const [showWarningSubmit, setWarningSubmit] = React.useState(false);
689
1193
  const {
@@ -707,11 +1211,18 @@ const ReleaseDetailsPage = () => {
707
1211
  }
708
1212
  );
709
1213
  }
710
- const title = isSuccessDetails && data?.data?.name || "";
1214
+ const releaseData = isSuccessDetails && data?.data || null;
1215
+ const title = releaseData?.name || "";
1216
+ const timezone = releaseData?.timezone ?? null;
1217
+ const scheduledAt = releaseData?.scheduledAt && timezone ? utcToZonedTime(releaseData.scheduledAt, timezone) : null;
1218
+ const date = scheduledAt ? format(scheduledAt, "yyyy-MM-dd") : null;
1219
+ const time = scheduledAt ? format(scheduledAt, "HH:mm") : "";
711
1220
  const handleEditRelease = async (values) => {
712
1221
  const response = await updateRelease({
713
1222
  id: releaseId,
714
- name: values.name
1223
+ name: values.name,
1224
+ scheduledAt: values.scheduledAt,
1225
+ timezone: values.timezone
715
1226
  });
716
1227
  if ("data" in response) {
717
1228
  toggleNotification({
@@ -721,6 +1232,7 @@ const ReleaseDetailsPage = () => {
721
1232
  defaultMessage: "Release updated."
722
1233
  })
723
1234
  });
1235
+ toggleEditReleaseModal();
724
1236
  } else if (isAxiosError(response.error)) {
725
1237
  toggleNotification({
726
1238
  type: "warning",
@@ -732,14 +1244,13 @@ const ReleaseDetailsPage = () => {
732
1244
  message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
733
1245
  });
734
1246
  }
735
- toggleEditReleaseModal();
736
1247
  };
737
1248
  const handleDeleteRelease = async () => {
738
1249
  const response = await deleteRelease({
739
1250
  id: releaseId
740
1251
  });
741
1252
  if ("data" in response) {
742
- push("/plugins/content-releases");
1253
+ replace("/plugins/content-releases");
743
1254
  } else if (isAxiosError(response.error)) {
744
1255
  toggleNotification({
745
1256
  type: "warning",
@@ -765,7 +1276,14 @@ const ReleaseDetailsPage = () => {
765
1276
  handleClose: toggleEditReleaseModal,
766
1277
  handleSubmit: handleEditRelease,
767
1278
  isLoading: isLoadingDetails || isSubmittingForm,
768
- initialValues: { name: title || "" }
1279
+ initialValues: {
1280
+ name: title || "",
1281
+ scheduledAt,
1282
+ date,
1283
+ time,
1284
+ isScheduled: Boolean(scheduledAt),
1285
+ timezone
1286
+ }
769
1287
  }
770
1288
  ),
771
1289
  /* @__PURE__ */ jsx(
@@ -785,281 +1303,6 @@ const ReleaseDetailsPage = () => {
785
1303
  }
786
1304
  );
787
1305
  };
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
1306
  const App = () => {
1064
1307
  return /* @__PURE__ */ jsx(CheckPagePermissions, { permissions: PERMISSIONS.main, children: /* @__PURE__ */ jsxs(Switch, { children: [
1065
1308
  /* @__PURE__ */ jsx(Route, { exact: true, path: `/plugins/${pluginId}`, component: ReleasesPage }),
@@ -1069,4 +1312,4 @@ const App = () => {
1069
1312
  export {
1070
1313
  App
1071
1314
  };
1072
- //# sourceMappingURL=App-8FCxPK8-.mjs.map
1315
+ //# sourceMappingURL=App-x6Tjj3HN.mjs.map