@strapi/content-releases 0.0.0-experimental.d4cb32ce579e12a4436d68036f2327132fba1309 → 0.0.0-experimental.d5b46d578a5c055b8dcc66939e1b5d540976fafb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/_chunks/{App-OK4Xac-O.js → App-OP70yd5M.js} +640 -617
  2. package/dist/_chunks/App-OP70yd5M.js.map +1 -0
  3. package/dist/_chunks/{App-xAkiD42p.mjs → App-x6Tjj3HN.mjs} +653 -630
  4. package/dist/_chunks/App-x6Tjj3HN.mjs.map +1 -0
  5. package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-3tRbmbY3.mjs} +2 -2
  6. package/dist/_chunks/PurchaseContentReleases-3tRbmbY3.mjs.map +1 -0
  7. package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases-bpIYXOfu.js} +2 -2
  8. package/dist/_chunks/PurchaseContentReleases-bpIYXOfu.js.map +1 -0
  9. package/dist/_chunks/{en-r0otWaln.js → en-3SGjiVyR.js} +13 -5
  10. package/dist/_chunks/en-3SGjiVyR.js.map +1 -0
  11. package/dist/_chunks/{en-veqvqeEr.mjs → en-bpHsnU0n.mjs} +13 -5
  12. package/dist/_chunks/en-bpHsnU0n.mjs.map +1 -0
  13. package/dist/_chunks/{index-exoiSU3V.mjs → index-1ejXLtzt.mjs} +235 -26
  14. package/dist/_chunks/index-1ejXLtzt.mjs.map +1 -0
  15. package/dist/_chunks/{index-JvA2_26n.js → index-ydocdaZ0.js} +224 -15
  16. package/dist/_chunks/index-ydocdaZ0.js.map +1 -0
  17. package/dist/admin/index.js +1 -1
  18. package/dist/admin/index.mjs +1 -1
  19. package/dist/server/index.js +554 -170
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +553 -170
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +12 -12
  24. package/dist/_chunks/App-OK4Xac-O.js.map +0 -1
  25. package/dist/_chunks/App-xAkiD42p.mjs.map +0 -1
  26. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
  27. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
  28. package/dist/_chunks/en-r0otWaln.js.map +0 -1
  29. package/dist/_chunks/en-veqvqeEr.mjs.map +0 -1
  30. package/dist/_chunks/index-JvA2_26n.js.map +0 -1
  31. package/dist/_chunks/index-exoiSU3V.mjs.map +0 -1
@@ -1,17 +1,17 @@
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 { g as getTimezoneOffset, 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, h as ReleaseActionMenu, i as isAxiosError, r as releaseApi, j as useGetReleasesQuery, k as useCreateReleaseMutation } from "./index-exoiSU3V.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, Flex, TextInput, Box, Checkbox, DatePicker, TimePicker, ModalFooter, Button, Combobox, ComboboxOption, ContentLayout, Main, HeaderLayout, Link, IconButton, SingleSelect, SingleSelectOption, Badge, Tr, Td, Icon, Tooltip, Alert, TabGroup, 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
10
  import format from "date-fns/format";
11
- import { zonedTimeToUtc, utcToZonedTime } from "date-fns-tz";
11
+ import { utcToZonedTime, zonedTimeToUtc } from "date-fns-tz";
12
12
  import { useIntl } from "react-intl";
13
13
  import styled from "styled-components";
14
- import { formatISO, parse } from "date-fns";
14
+ import { formatISO } from "date-fns";
15
15
  import { Formik, Form, useFormikContext } from "formik";
16
16
  import * as yup from "yup";
17
17
  import "@reduxjs/toolkit/query";
@@ -47,7 +47,6 @@ const ReleaseModal = ({
47
47
  const { formatMessage } = useIntl();
48
48
  const { pathname } = useLocation();
49
49
  const isCreatingRelease = pathname === `/plugins/${pluginId}`;
50
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
51
50
  const { timezoneList, systemTimezone = { value: "UTC+00:00-Africa/Abidjan " } } = getTimezones(
52
51
  initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : /* @__PURE__ */ new Date()
53
52
  );
@@ -55,13 +54,12 @@ const ReleaseModal = ({
55
54
  const { date, time, timezone } = values;
56
55
  if (!date || !time || !timezone)
57
56
  return null;
58
- const formattedDate = parse(time, "HH:mm", new Date(date));
59
- const timezoneWithoutOffset = timezone.split("_")[1];
60
- return zonedTimeToUtc(formattedDate, timezoneWithoutOffset);
57
+ const timezoneWithoutOffset = timezone.split("&")[1];
58
+ return zonedTimeToUtc(`${date} ${time}`, timezoneWithoutOffset);
61
59
  };
62
60
  const getTimezoneWithOffset = () => {
63
61
  const currentTimezone = timezoneList.find(
64
- (timezone) => timezone.value.split("_")[1] === initialValues.timezone
62
+ (timezone) => timezone.value.split("&")[1] === initialValues.timezone
65
63
  );
66
64
  return currentTimezone?.value || systemTimezone.value;
67
65
  };
@@ -79,7 +77,7 @@ const ReleaseModal = ({
79
77
  onSubmit: (values) => {
80
78
  handleSubmit({
81
79
  ...values,
82
- timezone: values.timezone ? values.timezone.split("_")[1] : null,
80
+ timezone: values.timezone ? values.timezone.split("&")[1] : null,
83
81
  scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
84
82
  });
85
83
  },
@@ -105,92 +103,88 @@ const ReleaseModal = ({
105
103
  required: true
106
104
  }
107
105
  ),
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) {
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: () => {
117
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: () => {
118
180
  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
- }
167
- ) }),
168
- /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(
169
- TimePicker,
170
- {
171
- label: formatMessage({
172
- id: "content-releases.modal.form.input.label.time",
173
- defaultMessage: "Time"
174
- }),
175
- name: "time",
176
- error: errors.time,
177
- onChange: (time) => {
178
- setFieldValue("time", time);
179
- },
180
- clearLabel: formatMessage({
181
- id: "content-releases.modal.form.input.clearLabel",
182
- defaultMessage: "Clear"
183
- }),
184
- onClear: () => {
185
- setFieldValue("time", "");
186
- },
187
- value: values.time || void 0,
188
- required: true
189
- }
190
- ) })
191
- ] }),
192
- /* @__PURE__ */ jsx(TimezoneComponent, { timezoneOptions: timezoneList })
193
- ] })
181
+ },
182
+ value: values.time || void 0,
183
+ required: true
184
+ }
185
+ ) })
186
+ ] }),
187
+ /* @__PURE__ */ jsx(TimezoneComponent, { timezoneOptions: timezoneList })
194
188
  ] })
195
189
  ] }) }),
196
190
  /* @__PURE__ */ jsx(
@@ -214,10 +208,10 @@ const ReleaseModal = ({
214
208
  const getTimezones = (selectedDate) => {
215
209
  const timezoneList = Intl.supportedValuesOf("timeZone").map((timezone) => {
216
210
  const utcOffset = getTimezoneOffset(timezone, selectedDate);
217
- return { offset: utcOffset, value: `${utcOffset}_${timezone}` };
211
+ return { offset: utcOffset, value: `${utcOffset}&${timezone}` };
218
212
  });
219
213
  const systemTimezone = timezoneList.find(
220
- (timezone) => timezone.value.split("_")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
214
+ (timezone) => timezone.value.split("&")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
221
215
  );
222
216
  return { timezoneList, systemTimezone };
223
217
  };
@@ -229,7 +223,7 @@ const TimezoneComponent = ({ timezoneOptions }) => {
229
223
  if (values.date) {
230
224
  const { timezoneList: timezoneList2 } = getTimezones(new Date(values.date));
231
225
  setTimezoneList(timezoneList2);
232
- const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("_")[1] === values.timezone.split("_")[1]);
226
+ const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("&")[1] === values.timezone.split("&")[1]);
233
227
  if (updatedTimezone) {
234
228
  setFieldValue("timezone", updatedTimezone.value);
235
229
  }
@@ -242,137 +236,201 @@ const TimezoneComponent = ({ timezoneOptions }) => {
242
236
  id: "content-releases.modal.form.input.label.timezone",
243
237
  defaultMessage: "Timezone"
244
238
  }),
239
+ autocomplete: { type: "list", filter: "contains" },
245
240
  name: "timezone",
246
241
  value: values.timezone || void 0,
247
- textValue: values.timezone ? values.timezone.replace("_", " ") : void 0,
242
+ textValue: values.timezone ? values.timezone.replace(/&/, " ") : void 0,
248
243
  onChange: (timezone) => {
249
244
  setFieldValue("timezone", timezone);
250
245
  },
246
+ onTextValueChange: (timezone) => {
247
+ setFieldValue("timezone", timezone);
248
+ },
251
249
  onClear: () => {
252
250
  setFieldValue("timezone", "");
253
251
  },
254
252
  error: errors.timezone,
255
253
  required: true,
256
- children: timezoneList.map((timezone) => /* @__PURE__ */ jsx(ComboboxOption, { value: timezone.value, children: timezone.value.replace("_", " ") }, timezone.value))
254
+ children: timezoneList.map((timezone) => /* @__PURE__ */ jsx(ComboboxOption, { value: timezone.value, children: timezone.value.replace(/&/, " ") }, timezone.value))
257
255
  }
258
256
  );
259
257
  };
260
- const ReleaseInfoWrapper = styled(Flex)`
261
- align-self: stretch;
262
- border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
263
- border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
264
- border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
265
- `;
266
- const StyledMenuItem = styled(Menu.Item)`
267
- svg path {
268
- fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
269
- }
270
- span {
271
- color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
272
- }
258
+ const LinkCard = styled(Link)`
259
+ display: block;
273
260
  `;
274
- const PencilIcon = styled(Pencil)`
275
- width: ${({ theme }) => theme.spaces[3]};
276
- height: ${({ theme }) => theme.spaces[3]};
277
- path {
278
- fill: ${({ theme }) => theme.colors.neutral600};
261
+ const RelativeTime = styled(RelativeTime$1)`
262
+ display: inline-block;
263
+ &::first-letter {
264
+ text-transform: uppercase;
279
265
  }
280
266
  `;
281
- const TrashIcon = styled(Trash)`
282
- width: ${({ theme }) => theme.spaces[3]};
283
- height: ${({ theme }) => theme.spaces[3]};
284
- path {
285
- fill: ${({ theme }) => theme.colors.danger600};
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";
286
285
  }
287
- `;
288
- const TypographyMaxWidth = styled(Typography)`
289
- max-width: 300px;
290
- `;
291
- const EntryValidationText = ({ action, schema, components, entry }) => {
286
+ return {
287
+ textColor: `${color}600`,
288
+ backgroundColor: `${color}100`,
289
+ borderColor: `${color}200`
290
+ };
291
+ };
292
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
292
293
  const { formatMessage } = useIntl();
293
- const { validate } = unstable_useDocument();
294
- const { errors } = validate(entry, {
295
- contentType: schema,
296
- components,
297
- isCreatingEntry: false
298
- });
299
- if (Object.keys(errors).length > 0) {
300
- const validationErrorsMessages = Object.entries(errors).map(
301
- ([key, value]) => formatMessage(
302
- { id: `${value.id}.withField`, defaultMessage: value.defaultMessage },
303
- { field: key }
304
- )
305
- ).join(" ");
306
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
307
- /* @__PURE__ */ jsx(Icon, { color: "danger600", as: CrossCircle }),
308
- /* @__PURE__ */ jsx(Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
309
- ] });
294
+ if (isError) {
295
+ return /* @__PURE__ */ jsx(AnErrorOccurred, {});
310
296
  }
311
- if (action == "publish") {
312
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
313
- /* @__PURE__ */ jsx(Icon, { color: "success600", as: CheckCircle }),
314
- entry.publishedAt ? /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
315
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
316
- defaultMessage: "Already published"
317
- }) }) : /* @__PURE__ */ jsx(Typography, { children: formatMessage({
318
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
319
- defaultMessage: "Ready to publish"
320
- }) })
321
- ] });
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
+ );
322
313
  }
323
- return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
324
- /* @__PURE__ */ jsx(Icon, { color: "success600", as: CheckCircle }),
325
- !entry.publishedAt ? /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
326
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
327
- defaultMessage: "Already unpublished"
328
- }) }) : /* @__PURE__ */ jsx(Typography, { children: formatMessage({
329
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
330
- defaultMessage: "Ready to unpublish"
331
- }) })
332
- ] });
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)) });
333
339
  };
334
- const ReleaseDetailsLayout = ({
335
- toggleEditReleaseModal,
336
- toggleWarningSubmit,
337
- children
338
- }) => {
339
- const { formatMessage, formatDate, formatTime } = useIntl();
340
- const { releaseId } = useParams();
341
- const {
342
- data,
343
- isLoading: isLoadingDetails,
344
- isError,
345
- error
346
- } = useGetReleaseQuery({ id: releaseId });
347
- const [publishRelease, { isLoading: isPublishing }] = usePublishReleaseMutation();
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);
348
360
  const toggleNotification = useNotification();
361
+ const { formatMessage } = useIntl();
362
+ const { push, replace } = useHistory();
349
363
  const { formatAPIError } = useAPIErrorHandler();
350
- const {
351
- allowedActions: { canUpdate, canDelete }
352
- } = useRBAC(PERMISSIONS);
353
- const dispatch = useTypedDispatch();
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");
354
369
  const { trackUsage } = useTracking();
355
- const release = data?.data;
356
- const handlePublishRelease = async () => {
357
- const response = await publishRelease({ id: releaseId });
358
- if ("data" in response) {
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) {
359
375
  toggleNotification({
360
- type: "success",
376
+ type: "warning",
377
+ title: formatMessage({
378
+ id: "content-releases.pages.Releases.notification.error.title",
379
+ defaultMessage: "Your request could not be processed."
380
+ }),
361
381
  message: formatMessage({
362
- id: "content-releases.pages.ReleaseDetails.publish-notification-success",
363
- defaultMessage: "Release was published successfully."
382
+ id: "content-releases.pages.Releases.notification.error.message",
383
+ defaultMessage: "Please try again or open another release."
364
384
  })
365
385
  });
366
- const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
367
- trackUsage("didPublishRelease", {
368
- totalEntries: totalEntries2,
369
- totalPublishedEntries,
370
- totalUnpublishedEntries
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
+ })
371
427
  });
372
- } else if (isAxiosError(response.error)) {
428
+ trackUsage("didCreateRelease");
429
+ push(`/plugins/content-releases/${response2.data.data.id}`);
430
+ } else if (isAxiosError(response2.error)) {
373
431
  toggleNotification({
374
432
  type: "warning",
375
- message: formatAPIError(response.error)
433
+ message: formatAPIError(response2.error)
376
434
  });
377
435
  } else {
378
436
  toggleNotification({
@@ -381,20 +439,284 @@ const ReleaseDetailsLayout = ({
381
439
  });
382
440
  }
383
441
  };
384
- const handleRefresh = () => {
385
- dispatch(releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
386
- };
387
- const getCreatedByUser = () => {
388
- if (!release?.createdBy) {
389
- return null;
390
- }
391
- if (release.createdBy.username) {
392
- return release.createdBy.username;
393
- }
394
- if (release.createdBy.firstname) {
395
- return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
396
- }
397
- return release.createdBy.email;
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
+ };
573
+ const ReleaseInfoWrapper = styled(Flex)`
574
+ align-self: stretch;
575
+ border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
576
+ border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
577
+ border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
578
+ `;
579
+ const StyledMenuItem = styled(Menu.Item)`
580
+ svg path {
581
+ fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
582
+ }
583
+ span {
584
+ color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
585
+ }
586
+
587
+ &:hover {
588
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
589
+ }
590
+ `;
591
+ const PencilIcon = styled(Pencil)`
592
+ width: ${({ theme }) => theme.spaces[3]};
593
+ height: ${({ theme }) => theme.spaces[3]};
594
+ path {
595
+ fill: ${({ theme }) => theme.colors.neutral600};
596
+ }
597
+ `;
598
+ const TrashIcon = styled(Trash)`
599
+ width: ${({ theme }) => theme.spaces[3]};
600
+ height: ${({ theme }) => theme.spaces[3]};
601
+ path {
602
+ fill: ${({ theme }) => theme.colors.danger600};
603
+ }
604
+ `;
605
+ const TypographyMaxWidth = styled(Typography)`
606
+ max-width: 300px;
607
+ `;
608
+ const EntryValidationText = ({ action, schema, components, entry }) => {
609
+ const { formatMessage } = useIntl();
610
+ const { validate } = unstable_useDocument();
611
+ const { errors } = validate(entry, {
612
+ contentType: schema,
613
+ components,
614
+ isCreatingEntry: false
615
+ });
616
+ if (Object.keys(errors).length > 0) {
617
+ const validationErrorsMessages = Object.entries(errors).map(
618
+ ([key, value]) => formatMessage(
619
+ { id: `${value.id}.withField`, defaultMessage: value.defaultMessage },
620
+ { field: key }
621
+ )
622
+ ).join(" ");
623
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
624
+ /* @__PURE__ */ jsx(Icon, { color: "danger600", as: CrossCircle }),
625
+ /* @__PURE__ */ jsx(Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
626
+ ] });
627
+ }
628
+ if (action == "publish") {
629
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
630
+ /* @__PURE__ */ jsx(Icon, { color: "success600", as: CheckCircle }),
631
+ entry.publishedAt ? /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
632
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
633
+ defaultMessage: "Already published"
634
+ }) }) : /* @__PURE__ */ jsx(Typography, { children: formatMessage({
635
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
636
+ defaultMessage: "Ready to publish"
637
+ }) })
638
+ ] });
639
+ }
640
+ return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
641
+ /* @__PURE__ */ jsx(Icon, { color: "success600", as: CheckCircle }),
642
+ !entry.publishedAt ? /* @__PURE__ */ jsx(Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
643
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
644
+ defaultMessage: "Already unpublished"
645
+ }) }) : /* @__PURE__ */ jsx(Typography, { children: formatMessage({
646
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
647
+ defaultMessage: "Ready to unpublish"
648
+ }) })
649
+ ] });
650
+ };
651
+ const ReleaseDetailsLayout = ({
652
+ toggleEditReleaseModal,
653
+ toggleWarningSubmit,
654
+ children
655
+ }) => {
656
+ const { formatMessage, formatDate, formatTime } = useIntl();
657
+ const { releaseId } = useParams();
658
+ const {
659
+ data,
660
+ isLoading: isLoadingDetails,
661
+ isError,
662
+ error
663
+ } = useGetReleaseQuery({ id: releaseId });
664
+ const [publishRelease, { isLoading: isPublishing }] = usePublishReleaseMutation();
665
+ const toggleNotification = useNotification();
666
+ const { formatAPIError } = useAPIErrorHandler();
667
+ const {
668
+ allowedActions: { canUpdate, canDelete }
669
+ } = useRBAC(PERMISSIONS);
670
+ const dispatch = useTypedDispatch();
671
+ const { trackUsage } = useTracking();
672
+ const release = data?.data;
673
+ const handlePublishRelease = async () => {
674
+ const response = await publishRelease({ id: releaseId });
675
+ if ("data" in response) {
676
+ toggleNotification({
677
+ type: "success",
678
+ message: formatMessage({
679
+ id: "content-releases.pages.ReleaseDetails.publish-notification-success",
680
+ defaultMessage: "Release was published successfully."
681
+ })
682
+ });
683
+ const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
684
+ trackUsage("didPublishRelease", {
685
+ totalEntries: totalEntries2,
686
+ totalPublishedEntries,
687
+ totalUnpublishedEntries
688
+ });
689
+ } else if (isAxiosError(response.error)) {
690
+ toggleNotification({
691
+ type: "warning",
692
+ message: formatAPIError(response.error)
693
+ });
694
+ } else {
695
+ toggleNotification({
696
+ type: "warning",
697
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
698
+ });
699
+ }
700
+ };
701
+ const handleRefresh = () => {
702
+ dispatch(
703
+ releaseApi.util.invalidateTags([
704
+ { type: "ReleaseAction", id: "LIST" },
705
+ { type: "Release", id: releaseId }
706
+ ])
707
+ );
708
+ };
709
+ const getCreatedByUser = () => {
710
+ if (!release?.createdBy) {
711
+ return null;
712
+ }
713
+ if (release.createdBy.username) {
714
+ return release.createdBy.username;
715
+ }
716
+ if (release.createdBy.firstname) {
717
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
718
+ }
719
+ return release.createdBy.email;
398
720
  };
399
721
  if (isLoadingDetails) {
400
722
  return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoadingDetails, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
@@ -418,7 +740,6 @@ const ReleaseDetailsLayout = ({
418
740
  }
419
741
  const totalEntries = release.actions.meta.count || 0;
420
742
  const hasCreatedByUser = Boolean(getCreatedByUser());
421
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
422
743
  const isScheduled = release.scheduledAt && release.timezone;
423
744
  const numberOfEntriesText = formatMessage(
424
745
  {
@@ -452,8 +773,11 @@ const ReleaseDetailsLayout = ({
452
773
  HeaderLayout,
453
774
  {
454
775
  title: release.name,
455
- subtitle: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? ` - ${scheduledText}` : ""),
456
- 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({
457
781
  id: "global.back",
458
782
  defaultMessage: "Back"
459
783
  }) }),
@@ -483,42 +807,28 @@ const ReleaseDetailsLayout = ({
483
807
  padding: 1,
484
808
  width: "100%",
485
809
  children: [
486
- /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxs(
487
- Flex,
488
- {
489
- paddingTop: 2,
490
- paddingBottom: 2,
491
- alignItems: "center",
492
- gap: 2,
493
- hasRadius: true,
494
- width: "100%",
495
- children: [
496
- /* @__PURE__ */ jsx(PencilIcon, {}),
497
- /* @__PURE__ */ jsx(Typography, { ellipsis: true, children: formatMessage({
498
- id: "content-releases.header.actions.edit",
499
- defaultMessage: "Edit"
500
- }) })
501
- ]
502
- }
503
- ) }),
504
- /* @__PURE__ */ jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxs(
505
- Flex,
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,
506
819
  {
507
- paddingTop: 2,
508
- paddingBottom: 2,
509
- alignItems: "center",
510
- gap: 2,
511
- hasRadius: true,
512
- width: "100%",
513
- children: [
820
+ disabled: !canDelete,
821
+ onSelect: toggleWarningSubmit,
822
+ variant: "danger",
823
+ children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
514
824
  /* @__PURE__ */ jsx(TrashIcon, {}),
515
825
  /* @__PURE__ */ jsx(Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
516
826
  id: "content-releases.header.actions.delete",
517
827
  defaultMessage: "Delete"
518
828
  }) })
519
- ]
829
+ ] })
520
830
  }
521
- ) })
831
+ )
522
832
  ]
523
833
  }
524
834
  ),
@@ -536,7 +846,7 @@ const ReleaseDetailsLayout = ({
536
846
  defaultMessage: "Created"
537
847
  }) }),
538
848
  /* @__PURE__ */ jsxs(Typography, { variant: "pi", color: "neutral300", children: [
539
- /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(release.createdAt) }),
849
+ /* @__PURE__ */ jsx(RelativeTime$1, { timestamp: new Date(release.createdAt) }),
540
850
  formatMessage(
541
851
  {
542
852
  id: "content-releases.header.actions.created.description",
@@ -694,7 +1004,7 @@ const ReleaseDetailsBody = () => {
694
1004
  action: /* @__PURE__ */ jsx(
695
1005
  LinkButton,
696
1006
  {
697
- as: Link$1,
1007
+ as: Link$2,
698
1008
  to: {
699
1009
  pathname: "/content-manager"
700
1010
  },
@@ -872,277 +1182,61 @@ const ReleaseDetailsBody = () => {
872
1182
  ] })
873
1183
  ] }) });
874
1184
  };
875
- const ReleaseDetailsPage = () => {
876
- const { formatMessage } = useIntl();
877
- const { releaseId } = useParams();
878
- const toggleNotification = useNotification();
879
- const { formatAPIError } = useAPIErrorHandler();
880
- const { push } = useHistory();
881
- const [releaseModalShown, setReleaseModalShown] = React.useState(false);
882
- const [showWarningSubmit, setWarningSubmit] = React.useState(false);
883
- const {
884
- isLoading: isLoadingDetails,
885
- data,
886
- isSuccess: isSuccessDetails
887
- } = useGetReleaseQuery({ id: releaseId });
888
- const [updateRelease, { isLoading: isSubmittingForm }] = useUpdateReleaseMutation();
889
- const [deleteRelease, { isLoading: isDeletingRelease }] = useDeleteReleaseMutation();
890
- const toggleEditReleaseModal = () => {
891
- setReleaseModalShown((prev) => !prev);
892
- };
893
- const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
894
- if (isLoadingDetails) {
895
- return /* @__PURE__ */ jsx(
896
- ReleaseDetailsLayout,
897
- {
898
- toggleEditReleaseModal,
899
- toggleWarningSubmit,
900
- children: /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) })
901
- }
902
- );
903
- }
904
- const releaseData = isSuccessDetails && data?.data || null;
905
- const title = releaseData?.name || "";
906
- const timezone = releaseData?.timezone ?? null;
907
- const scheduledAt = releaseData?.scheduledAt && timezone ? utcToZonedTime(releaseData.scheduledAt, timezone) : null;
908
- const date = scheduledAt ? new Date(format(scheduledAt, "yyyy-MM-dd")) : null;
909
- const time = scheduledAt ? format(scheduledAt, "HH:mm") : "";
910
- const handleEditRelease = async (values) => {
911
- const response = await updateRelease({
912
- id: releaseId,
913
- name: values.name,
914
- scheduledAt: values.scheduledAt,
915
- timezone: values.timezone
916
- });
917
- if ("data" in response) {
918
- toggleNotification({
919
- type: "success",
920
- message: formatMessage({
921
- id: "content-releases.modal.release-updated-notification-success",
922
- defaultMessage: "Release updated."
923
- })
924
- });
925
- } else if (isAxiosError(response.error)) {
926
- toggleNotification({
927
- type: "warning",
928
- message: formatAPIError(response.error)
929
- });
930
- } else {
931
- toggleNotification({
932
- type: "warning",
933
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
934
- });
935
- }
936
- toggleEditReleaseModal();
937
- };
938
- const handleDeleteRelease = async () => {
939
- const response = await deleteRelease({
940
- id: releaseId
941
- });
942
- if ("data" in response) {
943
- push("/plugins/content-releases");
944
- } else if (isAxiosError(response.error)) {
945
- toggleNotification({
946
- type: "warning",
947
- message: formatAPIError(response.error)
948
- });
949
- } else {
950
- toggleNotification({
951
- type: "warning",
952
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
953
- });
954
- }
955
- };
956
- return /* @__PURE__ */ jsxs(
957
- ReleaseDetailsLayout,
958
- {
959
- toggleEditReleaseModal,
960
- toggleWarningSubmit,
961
- children: [
962
- /* @__PURE__ */ jsx(ReleaseDetailsBody, {}),
963
- releaseModalShown && /* @__PURE__ */ jsx(
964
- ReleaseModal,
965
- {
966
- handleClose: toggleEditReleaseModal,
967
- handleSubmit: handleEditRelease,
968
- isLoading: isLoadingDetails || isSubmittingForm,
969
- initialValues: {
970
- name: title || "",
971
- scheduledAt,
972
- date,
973
- time,
974
- isScheduled: Boolean(scheduledAt),
975
- timezone
976
- }
977
- }
978
- ),
979
- /* @__PURE__ */ jsx(
980
- ConfirmDialog,
981
- {
982
- bodyText: {
983
- id: "content-releases.dialog.confirmation-message",
984
- defaultMessage: "Are you sure you want to delete this release?"
985
- },
986
- isOpen: showWarningSubmit,
987
- isConfirmButtonLoading: isDeletingRelease,
988
- onToggleDialog: toggleWarningSubmit,
989
- onConfirm: handleDeleteRelease
990
- }
991
- )
992
- ]
993
- }
994
- );
995
- };
996
- const LinkCard = styled(Link$2)`
997
- display: block;
998
- `;
999
- const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
1000
- const { formatMessage } = useIntl();
1001
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
1002
- if (isError) {
1003
- return /* @__PURE__ */ jsx(AnErrorOccurred, {});
1004
- }
1005
- if (releases?.length === 0) {
1006
- return /* @__PURE__ */ jsx(
1007
- EmptyStateLayout,
1008
- {
1009
- content: formatMessage(
1010
- {
1011
- id: "content-releases.page.Releases.tab.emptyEntries",
1012
- defaultMessage: "No releases"
1013
- },
1014
- {
1015
- target: sectionTitle
1016
- }
1017
- ),
1018
- icon: /* @__PURE__ */ jsx(EmptyDocuments, { width: "10rem" })
1019
- }
1020
- );
1021
- }
1022
- return /* @__PURE__ */ jsx(Grid, { gap: 4, children: releases.map(({ id, name, actions, scheduledAt }) => /* @__PURE__ */ jsx(GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxs(
1023
- Flex,
1024
- {
1025
- direction: "column",
1026
- justifyContent: "space-between",
1027
- padding: 4,
1028
- hasRadius: true,
1029
- background: "neutral0",
1030
- shadow: "tableShadow",
1031
- height: "100%",
1032
- width: "100%",
1033
- alignItems: "start",
1034
- gap: 2,
1035
- children: [
1036
- /* @__PURE__ */ jsx(Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
1037
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: IsSchedulingEnabled ? scheduledAt ? /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
1038
- id: "content-releases.pages.Releases.not-scheduled",
1039
- defaultMessage: "Not scheduled"
1040
- }) : formatMessage(
1041
- {
1042
- id: "content-releases.page.Releases.release-item.entries",
1043
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
1044
- },
1045
- { number: actions.meta.count }
1046
- ) })
1047
- ]
1048
- }
1049
- ) }) }, id)) });
1050
- };
1051
- const StyledAlert = styled(Alert)`
1052
- button {
1053
- display: none;
1054
- }
1055
- p + div {
1056
- margin-left: auto;
1057
- }
1058
- `;
1059
- const INITIAL_FORM_VALUES = {
1060
- name: "",
1061
- date: null,
1062
- time: "",
1063
- // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
1064
- isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
1065
- scheduledAt: null,
1066
- timezone: null
1067
- };
1068
- const ReleasesPage = () => {
1069
- const tabRef = React.useRef(null);
1070
- const location = useLocation();
1071
- const [releaseModalShown, setReleaseModalShown] = React.useState(false);
1072
- const toggleNotification = useNotification();
1185
+ const ReleaseDetailsPage = () => {
1073
1186
  const { formatMessage } = useIntl();
1074
- const { push, replace } = useHistory();
1187
+ const { releaseId } = useParams();
1188
+ const toggleNotification = useNotification();
1075
1189
  const { formatAPIError } = useAPIErrorHandler();
1076
- const [{ query }, setQuery] = useQueryParams();
1077
- const response = useGetReleasesQuery(query);
1078
- const [createRelease, { isLoading: isSubmittingForm }] = useCreateReleaseMutation();
1079
- const { getFeature } = useLicenseLimits();
1080
- const { maximumReleases = 3 } = getFeature("cms-content-releases");
1081
- const { trackUsage } = useTracking();
1082
- const { isLoading, isSuccess, isError } = response;
1083
- const activeTab = response?.currentData?.meta?.activeTab || "pending";
1084
- const activeTabIndex = ["pending", "done"].indexOf(activeTab);
1085
- React.useEffect(() => {
1086
- if (location?.state?.errors) {
1087
- toggleNotification({
1088
- type: "warning",
1089
- title: formatMessage({
1090
- id: "content-releases.pages.Releases.notification.error.title",
1091
- defaultMessage: "Your request could not be processed."
1092
- }),
1093
- message: formatMessage({
1094
- id: "content-releases.pages.Releases.notification.error.message",
1095
- defaultMessage: "Please try again or open another release."
1096
- })
1097
- });
1098
- replace({ state: null });
1099
- }
1100
- }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
1101
- React.useEffect(() => {
1102
- if (tabRef.current) {
1103
- tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
1104
- }
1105
- }, [activeTabIndex]);
1106
- const toggleAddReleaseModal = () => {
1190
+ const { replace } = useHistory();
1191
+ const [releaseModalShown, setReleaseModalShown] = React.useState(false);
1192
+ const [showWarningSubmit, setWarningSubmit] = React.useState(false);
1193
+ const {
1194
+ isLoading: isLoadingDetails,
1195
+ data,
1196
+ isSuccess: isSuccessDetails
1197
+ } = useGetReleaseQuery({ id: releaseId });
1198
+ const [updateRelease, { isLoading: isSubmittingForm }] = useUpdateReleaseMutation();
1199
+ const [deleteRelease, { isLoading: isDeletingRelease }] = useDeleteReleaseMutation();
1200
+ const toggleEditReleaseModal = () => {
1107
1201
  setReleaseModalShown((prev) => !prev);
1108
1202
  };
1109
- if (isLoading) {
1110
- return /* @__PURE__ */ jsx(Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) });
1111
- }
1112
- const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
1113
- const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
1114
- const handleTabChange = (index) => {
1115
- setQuery({
1116
- ...query,
1117
- page: 1,
1118
- pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
1119
- filters: {
1120
- releasedAt: {
1121
- $notNull: index === 0 ? false : true
1122
- }
1203
+ const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
1204
+ if (isLoadingDetails) {
1205
+ return /* @__PURE__ */ jsx(
1206
+ ReleaseDetailsLayout,
1207
+ {
1208
+ toggleEditReleaseModal,
1209
+ toggleWarningSubmit,
1210
+ children: /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsx(LoadingIndicatorPage, {}) })
1123
1211
  }
1212
+ );
1213
+ }
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") : "";
1220
+ const handleEditRelease = async (values) => {
1221
+ const response = await updateRelease({
1222
+ id: releaseId,
1223
+ name: values.name,
1224
+ scheduledAt: values.scheduledAt,
1225
+ timezone: values.timezone
1124
1226
  });
1125
- };
1126
- const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
1127
- const response2 = await createRelease({
1128
- name,
1129
- scheduledAt,
1130
- timezone
1131
- });
1132
- if ("data" in response2) {
1227
+ if ("data" in response) {
1133
1228
  toggleNotification({
1134
1229
  type: "success",
1135
1230
  message: formatMessage({
1136
- id: "content-releases.modal.release-created-notification-success",
1137
- defaultMessage: "Release created."
1231
+ id: "content-releases.modal.release-updated-notification-success",
1232
+ defaultMessage: "Release updated."
1138
1233
  })
1139
1234
  });
1140
- trackUsage("didCreateRelease");
1141
- push(`/plugins/content-releases/${response2.data.data.id}`);
1142
- } else if (isAxiosError(response2.error)) {
1235
+ toggleEditReleaseModal();
1236
+ } else if (isAxiosError(response.error)) {
1143
1237
  toggleNotification({
1144
1238
  type: "warning",
1145
- message: formatAPIError(response2.error)
1239
+ message: formatAPIError(response.error)
1146
1240
  });
1147
1241
  } else {
1148
1242
  toggleNotification({
@@ -1151,134 +1245,63 @@ const ReleasesPage = () => {
1151
1245
  });
1152
1246
  }
1153
1247
  };
1154
- return /* @__PURE__ */ jsxs(Main, { "aria-busy": isLoading, children: [
1155
- /* @__PURE__ */ jsx(
1156
- HeaderLayout,
1157
- {
1158
- title: formatMessage({
1159
- id: "content-releases.pages.Releases.title",
1160
- defaultMessage: "Releases"
1161
- }),
1162
- subtitle: formatMessage(
1163
- {
1164
- id: "content-releases.pages.Releases.header-subtitle",
1165
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
1166
- },
1167
- { number: totalReleases }
1168
- ),
1169
- primaryAction: /* @__PURE__ */ jsx(CheckPermissions, { permissions: PERMISSIONS.create, children: /* @__PURE__ */ jsx(
1170
- Button,
1171
- {
1172
- startIcon: /* @__PURE__ */ jsx(Plus, {}),
1173
- onClick: toggleAddReleaseModal,
1174
- disabled: hasReachedMaximumPendingReleases,
1175
- children: formatMessage({
1176
- id: "content-releases.header.actions.add-release",
1177
- defaultMessage: "New release"
1178
- })
1179
- }
1180
- ) })
1181
- }
1182
- ),
1183
- /* @__PURE__ */ jsx(ContentLayout, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
1184
- activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsx(
1185
- StyledAlert,
1186
- {
1187
- marginBottom: 6,
1188
- action: /* @__PURE__ */ jsx(Link$2, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
1189
- id: "content-releases.pages.Releases.max-limit-reached.action",
1190
- defaultMessage: "Explore plans"
1191
- }) }),
1192
- title: formatMessage(
1193
- {
1194
- id: "content-releases.pages.Releases.max-limit-reached.title",
1195
- defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
1196
- },
1197
- { number: maximumReleases }
1198
- ),
1199
- onClose: () => {
1200
- },
1201
- closeLabel: "",
1202
- children: formatMessage({
1203
- id: "content-releases.pages.Releases.max-limit-reached.message",
1204
- defaultMessage: "Upgrade to manage an unlimited number of releases."
1205
- })
1206
- }
1207
- ),
1208
- /* @__PURE__ */ jsxs(
1209
- TabGroup,
1210
- {
1211
- label: formatMessage({
1212
- id: "content-releases.pages.Releases.tab-group.label",
1213
- defaultMessage: "Releases list"
1214
- }),
1215
- variant: "simple",
1216
- initialSelectedTabIndex: activeTabIndex,
1217
- onTabChange: handleTabChange,
1218
- ref: tabRef,
1219
- children: [
1220
- /* @__PURE__ */ jsxs(Box, { paddingBottom: 8, children: [
1221
- /* @__PURE__ */ jsxs(Tabs, { children: [
1222
- /* @__PURE__ */ jsx(Tab, { children: formatMessage({
1223
- id: "content-releases.pages.Releases.tab.pending",
1224
- defaultMessage: "Pending"
1225
- }) }),
1226
- /* @__PURE__ */ jsx(Tab, { children: formatMessage({
1227
- id: "content-releases.pages.Releases.tab.done",
1228
- defaultMessage: "Done"
1229
- }) })
1230
- ] }),
1231
- /* @__PURE__ */ jsx(Divider, {})
1232
- ] }),
1233
- /* @__PURE__ */ jsxs(TabPanels, { children: [
1234
- /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
1235
- ReleasesGrid,
1236
- {
1237
- sectionTitle: "pending",
1238
- releases: response?.currentData?.data,
1239
- isError
1240
- }
1241
- ) }),
1242
- /* @__PURE__ */ jsx(TabPanel, { children: /* @__PURE__ */ jsx(
1243
- ReleasesGrid,
1244
- {
1245
- sectionTitle: "done",
1246
- releases: response?.currentData?.data,
1247
- isError
1248
- }
1249
- ) })
1250
- ] })
1251
- ]
1252
- }
1253
- ),
1254
- totalReleases > 0 && /* @__PURE__ */ jsxs(Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1255
- /* @__PURE__ */ jsx(
1256
- PageSizeURLQuery,
1248
+ const handleDeleteRelease = async () => {
1249
+ const response = await deleteRelease({
1250
+ id: releaseId
1251
+ });
1252
+ if ("data" in response) {
1253
+ replace("/plugins/content-releases");
1254
+ } else if (isAxiosError(response.error)) {
1255
+ toggleNotification({
1256
+ type: "warning",
1257
+ message: formatAPIError(response.error)
1258
+ });
1259
+ } else {
1260
+ toggleNotification({
1261
+ type: "warning",
1262
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1263
+ });
1264
+ }
1265
+ };
1266
+ return /* @__PURE__ */ jsxs(
1267
+ ReleaseDetailsLayout,
1268
+ {
1269
+ toggleEditReleaseModal,
1270
+ toggleWarningSubmit,
1271
+ children: [
1272
+ /* @__PURE__ */ jsx(ReleaseDetailsBody, {}),
1273
+ releaseModalShown && /* @__PURE__ */ jsx(
1274
+ ReleaseModal,
1257
1275
  {
1258
- options: ["8", "16", "32", "64"],
1259
- defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
1276
+ handleClose: toggleEditReleaseModal,
1277
+ handleSubmit: handleEditRelease,
1278
+ isLoading: isLoadingDetails || isSubmittingForm,
1279
+ initialValues: {
1280
+ name: title || "",
1281
+ scheduledAt,
1282
+ date,
1283
+ time,
1284
+ isScheduled: Boolean(scheduledAt),
1285
+ timezone
1286
+ }
1260
1287
  }
1261
1288
  ),
1262
1289
  /* @__PURE__ */ jsx(
1263
- PaginationURLQuery,
1290
+ ConfirmDialog,
1264
1291
  {
1265
- pagination: {
1266
- pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
1267
- }
1292
+ bodyText: {
1293
+ id: "content-releases.dialog.confirmation-message",
1294
+ defaultMessage: "Are you sure you want to delete this release?"
1295
+ },
1296
+ isOpen: showWarningSubmit,
1297
+ isConfirmButtonLoading: isDeletingRelease,
1298
+ onToggleDialog: toggleWarningSubmit,
1299
+ onConfirm: handleDeleteRelease
1268
1300
  }
1269
1301
  )
1270
- ] })
1271
- ] }) }),
1272
- releaseModalShown && /* @__PURE__ */ jsx(
1273
- ReleaseModal,
1274
- {
1275
- handleClose: toggleAddReleaseModal,
1276
- handleSubmit: handleAddRelease,
1277
- isLoading: isSubmittingForm,
1278
- initialValues: INITIAL_FORM_VALUES
1279
- }
1280
- )
1281
- ] });
1302
+ ]
1303
+ }
1304
+ );
1282
1305
  };
1283
1306
  const App = () => {
1284
1307
  return /* @__PURE__ */ jsx(CheckPagePermissions, { permissions: PERMISSIONS.main, children: /* @__PURE__ */ jsxs(Switch, { children: [
@@ -1289,4 +1312,4 @@ const App = () => {
1289
1312
  export {
1290
1313
  App
1291
1314
  };
1292
- //# sourceMappingURL=App-xAkiD42p.mjs.map
1315
+ //# sourceMappingURL=App-x6Tjj3HN.mjs.map