@strapi/content-releases 0.0.0-next.f8af92b375dc730ba47ed2117f25df893aae696c → 0.0.0-next.fc231041206e6f3999b094160cfa05db2892ad54

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.
@@ -3,7 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const helperPlugin = require("@strapi/helper-plugin");
5
5
  const reactRouterDom = require("react-router-dom");
6
- const index = require("./index-JvA2_26n.js");
6
+ const index = require("./index-ML_b3php.js");
7
7
  const React = require("react");
8
8
  const strapiAdmin = require("@strapi/admin/strapi-admin");
9
9
  const designSystem = require("@strapi/design-system");
@@ -72,7 +72,6 @@ const ReleaseModal = ({
72
72
  const { formatMessage } = reactIntl.useIntl();
73
73
  const { pathname } = reactRouterDom.useLocation();
74
74
  const isCreatingRelease = pathname === `/plugins/${index.pluginId}`;
75
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
76
75
  const { timezoneList, systemTimezone = { value: "UTC+00:00-Africa/Abidjan " } } = getTimezones(
77
76
  initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : /* @__PURE__ */ new Date()
78
77
  );
@@ -81,12 +80,12 @@ const ReleaseModal = ({
81
80
  if (!date || !time || !timezone)
82
81
  return null;
83
82
  const formattedDate = dateFns.parse(time, "HH:mm", new Date(date));
84
- const timezoneWithoutOffset = timezone.split("_")[1];
83
+ const timezoneWithoutOffset = timezone.split("&")[1];
85
84
  return dateFnsTz.zonedTimeToUtc(formattedDate, timezoneWithoutOffset);
86
85
  };
87
86
  const getTimezoneWithOffset = () => {
88
87
  const currentTimezone = timezoneList.find(
89
- (timezone) => timezone.value.split("_")[1] === initialValues.timezone
88
+ (timezone) => timezone.value.split("&")[1] === initialValues.timezone
90
89
  );
91
90
  return currentTimezone?.value || systemTimezone.value;
92
91
  };
@@ -104,7 +103,7 @@ const ReleaseModal = ({
104
103
  onSubmit: (values) => {
105
104
  handleSubmit({
106
105
  ...values,
107
- timezone: values.timezone ? values.timezone.split("_")[1] : null,
106
+ timezone: values.timezone ? values.timezone.split("&")[1] : null,
108
107
  scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
109
108
  });
110
109
  },
@@ -130,92 +129,88 @@ const ReleaseModal = ({
130
129
  required: true
131
130
  }
132
131
  ),
133
- IsSchedulingEnabled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
134
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "max-content", children: /* @__PURE__ */ jsxRuntime.jsx(
135
- designSystem.Checkbox,
136
- {
137
- name: "isScheduled",
138
- value: values.isScheduled,
139
- onChange: (event) => {
140
- setFieldValue("isScheduled", event.target.checked);
141
- if (!event.target.checked) {
132
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "max-content", children: /* @__PURE__ */ jsxRuntime.jsx(
133
+ designSystem.Checkbox,
134
+ {
135
+ name: "isScheduled",
136
+ value: values.isScheduled,
137
+ onChange: (event) => {
138
+ setFieldValue("isScheduled", event.target.checked);
139
+ if (!event.target.checked) {
140
+ setFieldValue("date", null);
141
+ setFieldValue("time", "");
142
+ setFieldValue("timezone", null);
143
+ } else {
144
+ setFieldValue("date", initialValues.date);
145
+ setFieldValue("time", initialValues.time);
146
+ setFieldValue("timezone", initialValues.timezone ?? systemTimezone?.value);
147
+ }
148
+ },
149
+ children: /* @__PURE__ */ jsxRuntime.jsx(
150
+ designSystem.Typography,
151
+ {
152
+ textColor: values.isScheduled ? "primary600" : "neutral800",
153
+ fontWeight: values.isScheduled ? "semiBold" : "regular",
154
+ children: formatMessage({
155
+ id: "modal.form.input.label.schedule-release",
156
+ defaultMessage: "Schedule release"
157
+ })
158
+ }
159
+ )
160
+ }
161
+ ) }),
162
+ values.isScheduled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
163
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "start", children: [
164
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
165
+ designSystem.DatePicker,
166
+ {
167
+ label: formatMessage({
168
+ id: "content-releases.modal.form.input.label.date",
169
+ defaultMessage: "Date"
170
+ }),
171
+ name: "date",
172
+ error: errors.date,
173
+ onChange: (date) => {
174
+ const isoFormatDate = date ? dateFns.formatISO(date, { representation: "date" }) : null;
175
+ setFieldValue("date", isoFormatDate);
176
+ },
177
+ clearLabel: formatMessage({
178
+ id: "content-releases.modal.form.input.clearLabel",
179
+ defaultMessage: "Clear"
180
+ }),
181
+ onClear: () => {
142
182
  setFieldValue("date", null);
183
+ },
184
+ selectedDate: values.date || void 0,
185
+ required: true,
186
+ minDate: dateFnsTz.utcToZonedTime(/* @__PURE__ */ new Date(), values.timezone.split("&")[1])
187
+ }
188
+ ) }),
189
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
190
+ designSystem.TimePicker,
191
+ {
192
+ label: formatMessage({
193
+ id: "content-releases.modal.form.input.label.time",
194
+ defaultMessage: "Time"
195
+ }),
196
+ name: "time",
197
+ error: errors.time,
198
+ onChange: (time) => {
199
+ setFieldValue("time", time);
200
+ },
201
+ clearLabel: formatMessage({
202
+ id: "content-releases.modal.form.input.clearLabel",
203
+ defaultMessage: "Clear"
204
+ }),
205
+ onClear: () => {
143
206
  setFieldValue("time", "");
144
- setFieldValue("timezone", null);
145
- } else {
146
- setFieldValue("date", initialValues.date);
147
- setFieldValue("time", initialValues.time);
148
- setFieldValue(
149
- "timezone",
150
- initialValues.timezone ?? systemTimezone?.value
151
- );
152
- }
153
- },
154
- children: /* @__PURE__ */ jsxRuntime.jsx(
155
- designSystem.Typography,
156
- {
157
- textColor: values.isScheduled ? "primary600" : "neutral800",
158
- fontWeight: values.isScheduled ? "semiBold" : "regular",
159
- children: formatMessage({
160
- id: "modal.form.input.label.schedule-release",
161
- defaultMessage: "Schedule release"
162
- })
163
- }
164
- )
165
- }
166
- ) }),
167
- values.isScheduled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
168
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "start", children: [
169
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
170
- designSystem.DatePicker,
171
- {
172
- label: formatMessage({
173
- id: "content-releases.modal.form.input.label.date",
174
- defaultMessage: "Date"
175
- }),
176
- name: "date",
177
- error: errors.date,
178
- onChange: (date) => {
179
- const isoFormatDate = date ? dateFns.formatISO(date, { representation: "date" }) : null;
180
- setFieldValue("date", isoFormatDate);
181
- },
182
- clearLabel: formatMessage({
183
- id: "content-releases.modal.form.input.clearLabel",
184
- defaultMessage: "Clear"
185
- }),
186
- onClear: () => {
187
- setFieldValue("date", null);
188
- },
189
- selectedDate: values.date || void 0,
190
- required: true
191
- }
192
- ) }),
193
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(
194
- designSystem.TimePicker,
195
- {
196
- label: formatMessage({
197
- id: "content-releases.modal.form.input.label.time",
198
- defaultMessage: "Time"
199
- }),
200
- name: "time",
201
- error: errors.time,
202
- onChange: (time) => {
203
- setFieldValue("time", time);
204
- },
205
- clearLabel: formatMessage({
206
- id: "content-releases.modal.form.input.clearLabel",
207
- defaultMessage: "Clear"
208
- }),
209
- onClear: () => {
210
- setFieldValue("time", "");
211
- },
212
- value: values.time || void 0,
213
- required: true
214
- }
215
- ) })
216
- ] }),
217
- /* @__PURE__ */ jsxRuntime.jsx(TimezoneComponent, { timezoneOptions: timezoneList })
218
- ] })
207
+ },
208
+ value: values.time || void 0,
209
+ required: true
210
+ }
211
+ ) })
212
+ ] }),
213
+ /* @__PURE__ */ jsxRuntime.jsx(TimezoneComponent, { timezoneOptions: timezoneList })
219
214
  ] })
220
215
  ] }) }),
221
216
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -239,10 +234,10 @@ const ReleaseModal = ({
239
234
  const getTimezones = (selectedDate) => {
240
235
  const timezoneList = Intl.supportedValuesOf("timeZone").map((timezone) => {
241
236
  const utcOffset = index.getTimezoneOffset(timezone, selectedDate);
242
- return { offset: utcOffset, value: `${utcOffset}_${timezone}` };
237
+ return { offset: utcOffset, value: `${utcOffset}&${timezone}` };
243
238
  });
244
239
  const systemTimezone = timezoneList.find(
245
- (timezone) => timezone.value.split("_")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
240
+ (timezone) => timezone.value.split("&")[1] === Intl.DateTimeFormat().resolvedOptions().timeZone
246
241
  );
247
242
  return { timezoneList, systemTimezone };
248
243
  };
@@ -254,7 +249,7 @@ const TimezoneComponent = ({ timezoneOptions }) => {
254
249
  if (values.date) {
255
250
  const { timezoneList: timezoneList2 } = getTimezones(new Date(values.date));
256
251
  setTimezoneList(timezoneList2);
257
- const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("_")[1] === values.timezone.split("_")[1]);
252
+ const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("&")[1] === values.timezone.split("&")[1]);
258
253
  if (updatedTimezone) {
259
254
  setFieldValue("timezone", updatedTimezone.value);
260
255
  }
@@ -267,137 +262,198 @@ const TimezoneComponent = ({ timezoneOptions }) => {
267
262
  id: "content-releases.modal.form.input.label.timezone",
268
263
  defaultMessage: "Timezone"
269
264
  }),
265
+ autocomplete: { type: "list", filter: "contains" },
270
266
  name: "timezone",
271
267
  value: values.timezone || void 0,
272
- textValue: values.timezone ? values.timezone.replace("_", " ") : void 0,
268
+ textValue: values.timezone ? values.timezone.replace(/&/, " ") : void 0,
273
269
  onChange: (timezone) => {
274
270
  setFieldValue("timezone", timezone);
275
271
  },
272
+ onTextValueChange: (timezone) => {
273
+ setFieldValue("timezone", timezone);
274
+ },
276
275
  onClear: () => {
277
276
  setFieldValue("timezone", "");
278
277
  },
279
278
  error: errors.timezone,
280
279
  required: true,
281
- children: timezoneList.map((timezone) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: timezone.value, children: timezone.value.replace("_", " ") }, timezone.value))
280
+ children: timezoneList.map((timezone) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: timezone.value, children: timezone.value.replace(/&/, " ") }, timezone.value))
282
281
  }
283
282
  );
284
283
  };
285
- const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
286
- align-self: stretch;
287
- border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
288
- border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
289
- border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
290
- `;
291
- const StyledMenuItem = styled__default.default(v2.Menu.Item)`
292
- svg path {
293
- fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
294
- }
295
- span {
296
- color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
297
- }
284
+ const LinkCard = styled__default.default(v2.Link)`
285
+ display: block;
298
286
  `;
299
- const PencilIcon = styled__default.default(icons.Pencil)`
300
- width: ${({ theme }) => theme.spaces[3]};
301
- height: ${({ theme }) => theme.spaces[3]};
302
- path {
303
- fill: ${({ theme }) => theme.colors.neutral600};
304
- }
287
+ const CapitalizeRelativeTime = styled__default.default(helperPlugin.RelativeTime)`
288
+ text-transform: capitalize;
305
289
  `;
306
- const TrashIcon = styled__default.default(icons.Trash)`
307
- width: ${({ theme }) => theme.spaces[3]};
308
- height: ${({ theme }) => theme.spaces[3]};
309
- path {
310
- fill: ${({ theme }) => theme.colors.danger600};
290
+ const getBadgeProps = (status) => {
291
+ let color;
292
+ switch (status) {
293
+ case "ready":
294
+ color = "success";
295
+ break;
296
+ case "blocked":
297
+ color = "warning";
298
+ break;
299
+ case "failed":
300
+ color = "danger";
301
+ break;
302
+ case "done":
303
+ color = "primary";
304
+ break;
305
+ case "empty":
306
+ default:
307
+ color = "neutral";
311
308
  }
312
- `;
313
- const TypographyMaxWidth = styled__default.default(designSystem.Typography)`
314
- max-width: 300px;
315
- `;
316
- const EntryValidationText = ({ action, schema, components, entry }) => {
309
+ return {
310
+ textColor: `${color}600`,
311
+ backgroundColor: `${color}100`,
312
+ borderColor: `${color}200`
313
+ };
314
+ };
315
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
317
316
  const { formatMessage } = reactIntl.useIntl();
318
- const { validate } = strapiAdmin.unstable_useDocument();
319
- const { errors } = validate(entry, {
320
- contentType: schema,
321
- components,
322
- isCreatingEntry: false
323
- });
324
- if (Object.keys(errors).length > 0) {
325
- const validationErrorsMessages = Object.entries(errors).map(
326
- ([key, value]) => formatMessage(
327
- { id: `${value.id}.withField`, defaultMessage: value.defaultMessage },
328
- { field: key }
329
- )
330
- ).join(" ");
331
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
332
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "danger600", as: icons.CrossCircle }),
333
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsxRuntime.jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
334
- ] });
317
+ if (isError) {
318
+ return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
335
319
  }
336
- if (action == "publish") {
337
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
338
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
339
- entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
340
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
341
- defaultMessage: "Already published"
342
- }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
343
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
344
- defaultMessage: "Ready to publish"
345
- }) })
346
- ] });
320
+ if (releases?.length === 0) {
321
+ return /* @__PURE__ */ jsxRuntime.jsx(
322
+ designSystem.EmptyStateLayout,
323
+ {
324
+ content: formatMessage(
325
+ {
326
+ id: "content-releases.page.Releases.tab.emptyEntries",
327
+ defaultMessage: "No releases"
328
+ },
329
+ {
330
+ target: sectionTitle
331
+ }
332
+ ),
333
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.EmptyDocuments, { width: "10rem" })
334
+ }
335
+ );
347
336
  }
348
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
349
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
350
- !entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
351
- id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
352
- defaultMessage: "Already unpublished"
353
- }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
354
- id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
355
- defaultMessage: "Ready to unpublish"
356
- }) })
357
- ] });
337
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid, { gap: 4, children: releases.map(({ id, name, scheduledAt, status }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxRuntime.jsxs(
338
+ designSystem.Flex,
339
+ {
340
+ direction: "column",
341
+ justifyContent: "space-between",
342
+ padding: 4,
343
+ hasRadius: true,
344
+ background: "neutral0",
345
+ shadow: "tableShadow",
346
+ height: "100%",
347
+ width: "100%",
348
+ alignItems: "start",
349
+ gap: 4,
350
+ children: [
351
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "start", gap: 1, children: [
352
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
353
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: scheduledAt ? /* @__PURE__ */ jsxRuntime.jsx(CapitalizeRelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
354
+ id: "content-releases.pages.Releases.not-scheduled",
355
+ defaultMessage: "Not scheduled"
356
+ }) })
357
+ ] }),
358
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { ...getBadgeProps(status), children: status })
359
+ ]
360
+ }
361
+ ) }) }, id)) });
358
362
  };
359
- const ReleaseDetailsLayout = ({
360
- toggleEditReleaseModal,
361
- toggleWarningSubmit,
362
- children
363
- }) => {
364
- const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
365
- const { releaseId } = reactRouterDom.useParams();
366
- const {
367
- data,
368
- isLoading: isLoadingDetails,
369
- isError,
370
- error
371
- } = index.useGetReleaseQuery({ id: releaseId });
372
- const [publishRelease, { isLoading: isPublishing }] = index.usePublishReleaseMutation();
363
+ const StyledAlert = styled__default.default(designSystem.Alert)`
364
+ button {
365
+ display: none;
366
+ }
367
+ p + div {
368
+ margin-left: auto;
369
+ }
370
+ `;
371
+ const INITIAL_FORM_VALUES = {
372
+ name: "",
373
+ date: null,
374
+ time: "",
375
+ isScheduled: true,
376
+ scheduledAt: null,
377
+ timezone: null
378
+ };
379
+ const ReleasesPage = () => {
380
+ const tabRef = React__namespace.useRef(null);
381
+ const location = reactRouterDom.useLocation();
382
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
373
383
  const toggleNotification = helperPlugin.useNotification();
384
+ const { formatMessage } = reactIntl.useIntl();
385
+ const { push, replace } = reactRouterDom.useHistory();
374
386
  const { formatAPIError } = helperPlugin.useAPIErrorHandler();
375
- const {
376
- allowedActions: { canUpdate, canDelete }
377
- } = helperPlugin.useRBAC(index.PERMISSIONS);
378
- const dispatch = index.useTypedDispatch();
387
+ const [{ query }, setQuery] = helperPlugin.useQueryParams();
388
+ const response = index.useGetReleasesQuery(query);
389
+ const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
390
+ const { getFeature } = strapiAdmin.useLicenseLimits();
391
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
379
392
  const { trackUsage } = helperPlugin.useTracking();
380
- const release = data?.data;
381
- const handlePublishRelease = async () => {
382
- const response = await publishRelease({ id: releaseId });
383
- if ("data" in response) {
393
+ const { isLoading, isSuccess, isError } = response;
394
+ const activeTab = response?.currentData?.meta?.activeTab || "pending";
395
+ const activeTabIndex = ["pending", "done"].indexOf(activeTab);
396
+ React__namespace.useEffect(() => {
397
+ if (location?.state?.errors) {
384
398
  toggleNotification({
385
- type: "success",
399
+ type: "warning",
400
+ title: formatMessage({
401
+ id: "content-releases.pages.Releases.notification.error.title",
402
+ defaultMessage: "Your request could not be processed."
403
+ }),
386
404
  message: formatMessage({
387
- id: "content-releases.pages.ReleaseDetails.publish-notification-success",
388
- defaultMessage: "Release was published successfully."
405
+ id: "content-releases.pages.Releases.notification.error.message",
406
+ defaultMessage: "Please try again or open another release."
389
407
  })
390
408
  });
391
- const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
392
- trackUsage("didPublishRelease", {
393
- totalEntries: totalEntries2,
394
- totalPublishedEntries,
395
- totalUnpublishedEntries
409
+ replace({ state: null });
410
+ }
411
+ }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
412
+ React__namespace.useEffect(() => {
413
+ if (tabRef.current) {
414
+ tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
415
+ }
416
+ }, [activeTabIndex]);
417
+ const toggleAddReleaseModal = () => {
418
+ setReleaseModalShown((prev) => !prev);
419
+ };
420
+ if (isLoading) {
421
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
422
+ }
423
+ const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
424
+ const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
425
+ const handleTabChange = (index2) => {
426
+ setQuery({
427
+ ...query,
428
+ page: 1,
429
+ pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
430
+ filters: {
431
+ releasedAt: {
432
+ $notNull: index2 === 0 ? false : true
433
+ }
434
+ }
435
+ });
436
+ };
437
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
438
+ const response2 = await createRelease({
439
+ name,
440
+ scheduledAt,
441
+ timezone
442
+ });
443
+ if ("data" in response2) {
444
+ toggleNotification({
445
+ type: "success",
446
+ message: formatMessage({
447
+ id: "content-releases.modal.release-created-notification-success",
448
+ defaultMessage: "Release created."
449
+ })
396
450
  });
397
- } else if (index.isAxiosError(response.error)) {
451
+ trackUsage("didCreateRelease");
452
+ push(`/plugins/content-releases/${response2.data.data.id}`);
453
+ } else if (index.isAxiosError(response2.error)) {
398
454
  toggleNotification({
399
455
  type: "warning",
400
- message: formatAPIError(response.error)
456
+ message: formatAPIError(response2.error)
401
457
  });
402
458
  } else {
403
459
  toggleNotification({
@@ -406,18 +462,282 @@ const ReleaseDetailsLayout = ({
406
462
  });
407
463
  }
408
464
  };
409
- const handleRefresh = () => {
410
- dispatch(index.releaseApi.util.invalidateTags([{ type: "ReleaseAction", id: "LIST" }]));
411
- };
412
- const getCreatedByUser = () => {
413
- if (!release?.createdBy) {
414
- return null;
415
- }
416
- if (release.createdBy.username) {
417
- return release.createdBy.username;
418
- }
419
- if (release.createdBy.firstname) {
420
- return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
465
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
466
+ /* @__PURE__ */ jsxRuntime.jsx(
467
+ designSystem.HeaderLayout,
468
+ {
469
+ title: formatMessage({
470
+ id: "content-releases.pages.Releases.title",
471
+ defaultMessage: "Releases"
472
+ }),
473
+ subtitle: formatMessage({
474
+ id: "content-releases.pages.Releases.header-subtitle",
475
+ defaultMessage: "Create and manage content updates"
476
+ }),
477
+ primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
478
+ designSystem.Button,
479
+ {
480
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
481
+ onClick: toggleAddReleaseModal,
482
+ disabled: hasReachedMaximumPendingReleases,
483
+ children: formatMessage({
484
+ id: "content-releases.header.actions.add-release",
485
+ defaultMessage: "New release"
486
+ })
487
+ }
488
+ ) })
489
+ }
490
+ ),
491
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
492
+ hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
493
+ StyledAlert,
494
+ {
495
+ marginBottom: 6,
496
+ action: /* @__PURE__ */ jsxRuntime.jsx(v2.Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
497
+ id: "content-releases.pages.Releases.max-limit-reached.action",
498
+ defaultMessage: "Explore plans"
499
+ }) }),
500
+ title: formatMessage(
501
+ {
502
+ id: "content-releases.pages.Releases.max-limit-reached.title",
503
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
504
+ },
505
+ { number: maximumReleases }
506
+ ),
507
+ onClose: () => {
508
+ },
509
+ closeLabel: "",
510
+ children: formatMessage({
511
+ id: "content-releases.pages.Releases.max-limit-reached.message",
512
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
513
+ })
514
+ }
515
+ ),
516
+ /* @__PURE__ */ jsxRuntime.jsxs(
517
+ designSystem.TabGroup,
518
+ {
519
+ label: formatMessage({
520
+ id: "content-releases.pages.Releases.tab-group.label",
521
+ defaultMessage: "Releases list"
522
+ }),
523
+ variant: "simple",
524
+ initialSelectedTabIndex: activeTabIndex,
525
+ onTabChange: handleTabChange,
526
+ ref: tabRef,
527
+ children: [
528
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingBottom: 8, children: [
529
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs, { children: [
530
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage(
531
+ {
532
+ id: "content-releases.pages.Releases.tab.pending",
533
+ defaultMessage: "Pending ({count})"
534
+ },
535
+ {
536
+ count: totalPendingReleases
537
+ }
538
+ ) }),
539
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
540
+ id: "content-releases.pages.Releases.tab.done",
541
+ defaultMessage: "Done"
542
+ }) })
543
+ ] }),
544
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {})
545
+ ] }),
546
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.TabPanels, { children: [
547
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
548
+ ReleasesGrid,
549
+ {
550
+ sectionTitle: "pending",
551
+ releases: response?.currentData?.data,
552
+ isError
553
+ }
554
+ ) }),
555
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
556
+ ReleasesGrid,
557
+ {
558
+ sectionTitle: "done",
559
+ releases: response?.currentData?.data,
560
+ isError
561
+ }
562
+ ) })
563
+ ] })
564
+ ]
565
+ }
566
+ ),
567
+ response.currentData?.meta?.pagination?.total ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
568
+ /* @__PURE__ */ jsxRuntime.jsx(
569
+ helperPlugin.PageSizeURLQuery,
570
+ {
571
+ options: ["8", "16", "32", "64"],
572
+ defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
573
+ }
574
+ ),
575
+ /* @__PURE__ */ jsxRuntime.jsx(
576
+ helperPlugin.PaginationURLQuery,
577
+ {
578
+ pagination: {
579
+ pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
580
+ }
581
+ }
582
+ )
583
+ ] }) : null
584
+ ] }) }),
585
+ releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
586
+ ReleaseModal,
587
+ {
588
+ handleClose: toggleAddReleaseModal,
589
+ handleSubmit: handleAddRelease,
590
+ isLoading: isSubmittingForm,
591
+ initialValues: INITIAL_FORM_VALUES
592
+ }
593
+ )
594
+ ] });
595
+ };
596
+ const ReleaseInfoWrapper = styled__default.default(designSystem.Flex)`
597
+ align-self: stretch;
598
+ border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
599
+ border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
600
+ border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
601
+ `;
602
+ const StyledMenuItem = styled__default.default(v2.Menu.Item)`
603
+ svg path {
604
+ fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
605
+ }
606
+ span {
607
+ color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
608
+ }
609
+
610
+ &:hover {
611
+ background: ${({ theme, variant = "neutral" }) => theme.colors[`${variant}100`]};
612
+ }
613
+ `;
614
+ const PencilIcon = styled__default.default(icons.Pencil)`
615
+ width: ${({ theme }) => theme.spaces[3]};
616
+ height: ${({ theme }) => theme.spaces[3]};
617
+ path {
618
+ fill: ${({ theme }) => theme.colors.neutral600};
619
+ }
620
+ `;
621
+ const TrashIcon = styled__default.default(icons.Trash)`
622
+ width: ${({ theme }) => theme.spaces[3]};
623
+ height: ${({ theme }) => theme.spaces[3]};
624
+ path {
625
+ fill: ${({ theme }) => theme.colors.danger600};
626
+ }
627
+ `;
628
+ const TypographyMaxWidth = styled__default.default(designSystem.Typography)`
629
+ max-width: 300px;
630
+ `;
631
+ const EntryValidationText = ({ action, schema, components, entry }) => {
632
+ const { formatMessage } = reactIntl.useIntl();
633
+ const { validate } = strapiAdmin.unstable_useDocument();
634
+ const { errors } = validate(entry, {
635
+ contentType: schema,
636
+ components,
637
+ isCreatingEntry: false
638
+ });
639
+ if (Object.keys(errors).length > 0) {
640
+ const validationErrorsMessages = Object.entries(errors).map(
641
+ ([key, value]) => formatMessage(
642
+ { id: `${value.id}.withField`, defaultMessage: value.defaultMessage },
643
+ { field: key }
644
+ )
645
+ ).join(" ");
646
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
647
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "danger600", as: icons.CrossCircle }),
648
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { description: validationErrorsMessages, children: /* @__PURE__ */ jsxRuntime.jsx(TypographyMaxWidth, { textColor: "danger600", variant: "omega", fontWeight: "semiBold", ellipsis: true, children: validationErrorsMessages }) })
649
+ ] });
650
+ }
651
+ if (action == "publish") {
652
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
653
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
654
+ entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
655
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
656
+ defaultMessage: "Already published"
657
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
658
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
659
+ defaultMessage: "Ready to publish"
660
+ }) })
661
+ ] });
662
+ }
663
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
664
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Icon, { color: "success600", as: icons.CheckCircle }),
665
+ !entry.publishedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", fontWeight: "bold", children: formatMessage({
666
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
667
+ defaultMessage: "Already unpublished"
668
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
669
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
670
+ defaultMessage: "Ready to unpublish"
671
+ }) })
672
+ ] });
673
+ };
674
+ const ReleaseDetailsLayout = ({
675
+ toggleEditReleaseModal,
676
+ toggleWarningSubmit,
677
+ children
678
+ }) => {
679
+ const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
680
+ const { releaseId } = reactRouterDom.useParams();
681
+ const {
682
+ data,
683
+ isLoading: isLoadingDetails,
684
+ isError,
685
+ error
686
+ } = index.useGetReleaseQuery({ id: releaseId });
687
+ const [publishRelease, { isLoading: isPublishing }] = index.usePublishReleaseMutation();
688
+ const toggleNotification = helperPlugin.useNotification();
689
+ const { formatAPIError } = helperPlugin.useAPIErrorHandler();
690
+ const {
691
+ allowedActions: { canUpdate, canDelete }
692
+ } = helperPlugin.useRBAC(index.PERMISSIONS);
693
+ const dispatch = index.useTypedDispatch();
694
+ const { trackUsage } = helperPlugin.useTracking();
695
+ const release = data?.data;
696
+ const handlePublishRelease = async () => {
697
+ const response = await publishRelease({ id: releaseId });
698
+ if ("data" in response) {
699
+ toggleNotification({
700
+ type: "success",
701
+ message: formatMessage({
702
+ id: "content-releases.pages.ReleaseDetails.publish-notification-success",
703
+ defaultMessage: "Release was published successfully."
704
+ })
705
+ });
706
+ const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
707
+ trackUsage("didPublishRelease", {
708
+ totalEntries: totalEntries2,
709
+ totalPublishedEntries,
710
+ totalUnpublishedEntries
711
+ });
712
+ } else if (index.isAxiosError(response.error)) {
713
+ toggleNotification({
714
+ type: "warning",
715
+ message: formatAPIError(response.error)
716
+ });
717
+ } else {
718
+ toggleNotification({
719
+ type: "warning",
720
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
721
+ });
722
+ }
723
+ };
724
+ const handleRefresh = () => {
725
+ dispatch(
726
+ index.releaseApi.util.invalidateTags([
727
+ { type: "ReleaseAction", id: "LIST" },
728
+ { type: "Release", id: releaseId }
729
+ ])
730
+ );
731
+ };
732
+ const getCreatedByUser = () => {
733
+ if (!release?.createdBy) {
734
+ return null;
735
+ }
736
+ if (release.createdBy.username) {
737
+ return release.createdBy.username;
738
+ }
739
+ if (release.createdBy.firstname) {
740
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
421
741
  }
422
742
  return release.createdBy.email;
423
743
  };
@@ -443,7 +763,6 @@ const ReleaseDetailsLayout = ({
443
763
  }
444
764
  const totalEntries = release.actions.meta.count || 0;
445
765
  const hasCreatedByUser = Boolean(getCreatedByUser());
446
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
447
766
  const isScheduled = release.scheduledAt && release.timezone;
448
767
  const numberOfEntriesText = formatMessage(
449
768
  {
@@ -477,7 +796,10 @@ const ReleaseDetailsLayout = ({
477
796
  designSystem.HeaderLayout,
478
797
  {
479
798
  title: release.name,
480
- subtitle: numberOfEntriesText + (IsSchedulingEnabled && isScheduled ? ` - ${scheduledText}` : ""),
799
+ subtitle: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, lineHeight: 6, children: [
800
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", variant: "epsilon", children: numberOfEntriesText + (isScheduled ? ` - ${scheduledText}` : "") }),
801
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { ...getBadgeProps(release.status), children: release.status })
802
+ ] }),
481
803
  navigationAction: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {}), to: "/plugins/content-releases", children: formatMessage({
482
804
  id: "global.back",
483
805
  defaultMessage: "Back"
@@ -508,42 +830,28 @@ const ReleaseDetailsLayout = ({
508
830
  padding: 1,
509
831
  width: "100%",
510
832
  children: [
511
- /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(
512
- designSystem.Flex,
513
- {
514
- paddingTop: 2,
515
- paddingBottom: 2,
516
- alignItems: "center",
517
- gap: 2,
518
- hasRadius: true,
519
- width: "100%",
520
- children: [
521
- /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
522
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
523
- id: "content-releases.header.actions.edit",
524
- defaultMessage: "Edit"
525
- }) })
526
- ]
527
- }
528
- ) }),
529
- /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canDelete, onSelect: toggleWarningSubmit, children: /* @__PURE__ */ jsxRuntime.jsxs(
530
- designSystem.Flex,
833
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
834
+ /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
835
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
836
+ id: "content-releases.header.actions.edit",
837
+ defaultMessage: "Edit"
838
+ }) })
839
+ ] }) }),
840
+ /* @__PURE__ */ jsxRuntime.jsx(
841
+ StyledMenuItem,
531
842
  {
532
- paddingTop: 2,
533
- paddingBottom: 2,
534
- alignItems: "center",
535
- gap: 2,
536
- hasRadius: true,
537
- width: "100%",
538
- children: [
843
+ disabled: !canDelete,
844
+ onSelect: toggleWarningSubmit,
845
+ variant: "danger",
846
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
539
847
  /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
540
848
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
541
849
  id: "content-releases.header.actions.delete",
542
850
  defaultMessage: "Delete"
543
851
  }) })
544
- ]
852
+ ] })
545
853
  }
546
- ) })
854
+ )
547
855
  ]
548
856
  }
549
857
  ),
@@ -900,274 +1208,57 @@ const ReleaseDetailsBody = () => {
900
1208
  const ReleaseDetailsPage = () => {
901
1209
  const { formatMessage } = reactIntl.useIntl();
902
1210
  const { releaseId } = reactRouterDom.useParams();
903
- const toggleNotification = helperPlugin.useNotification();
904
- const { formatAPIError } = helperPlugin.useAPIErrorHandler();
905
- const { push } = reactRouterDom.useHistory();
906
- const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
907
- const [showWarningSubmit, setWarningSubmit] = React__namespace.useState(false);
908
- const {
909
- isLoading: isLoadingDetails,
910
- data,
911
- isSuccess: isSuccessDetails
912
- } = index.useGetReleaseQuery({ id: releaseId });
913
- const [updateRelease, { isLoading: isSubmittingForm }] = index.useUpdateReleaseMutation();
914
- const [deleteRelease, { isLoading: isDeletingRelease }] = index.useDeleteReleaseMutation();
915
- const toggleEditReleaseModal = () => {
916
- setReleaseModalShown((prev) => !prev);
917
- };
918
- const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
919
- if (isLoadingDetails) {
920
- return /* @__PURE__ */ jsxRuntime.jsx(
921
- ReleaseDetailsLayout,
922
- {
923
- toggleEditReleaseModal,
924
- toggleWarningSubmit,
925
- children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) })
926
- }
927
- );
928
- }
929
- const releaseData = isSuccessDetails && data?.data || null;
930
- const title = releaseData?.name || "";
931
- const timezone = releaseData?.timezone ?? null;
932
- const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
933
- const date = scheduledAt ? new Date(format__default.default(scheduledAt, "yyyy-MM-dd")) : null;
934
- const time = scheduledAt ? format__default.default(scheduledAt, "HH:mm") : "";
935
- const handleEditRelease = async (values) => {
936
- const response = await updateRelease({
937
- id: releaseId,
938
- name: values.name,
939
- scheduledAt: values.scheduledAt,
940
- timezone: values.timezone
941
- });
942
- if ("data" in response) {
943
- toggleNotification({
944
- type: "success",
945
- message: formatMessage({
946
- id: "content-releases.modal.release-updated-notification-success",
947
- defaultMessage: "Release updated."
948
- })
949
- });
950
- } else if (index.isAxiosError(response.error)) {
951
- toggleNotification({
952
- type: "warning",
953
- message: formatAPIError(response.error)
954
- });
955
- } else {
956
- toggleNotification({
957
- type: "warning",
958
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
959
- });
960
- }
961
- toggleEditReleaseModal();
962
- };
963
- const handleDeleteRelease = async () => {
964
- const response = await deleteRelease({
965
- id: releaseId
966
- });
967
- if ("data" in response) {
968
- push("/plugins/content-releases");
969
- } else if (index.isAxiosError(response.error)) {
970
- toggleNotification({
971
- type: "warning",
972
- message: formatAPIError(response.error)
973
- });
974
- } else {
975
- toggleNotification({
976
- type: "warning",
977
- message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
978
- });
979
- }
980
- };
981
- return /* @__PURE__ */ jsxRuntime.jsxs(
982
- ReleaseDetailsLayout,
983
- {
984
- toggleEditReleaseModal,
985
- toggleWarningSubmit,
986
- children: [
987
- /* @__PURE__ */ jsxRuntime.jsx(ReleaseDetailsBody, {}),
988
- releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
989
- ReleaseModal,
990
- {
991
- handleClose: toggleEditReleaseModal,
992
- handleSubmit: handleEditRelease,
993
- isLoading: isLoadingDetails || isSubmittingForm,
994
- initialValues: {
995
- name: title || "",
996
- scheduledAt,
997
- date,
998
- time,
999
- isScheduled: Boolean(scheduledAt),
1000
- timezone
1001
- }
1002
- }
1003
- ),
1004
- /* @__PURE__ */ jsxRuntime.jsx(
1005
- helperPlugin.ConfirmDialog,
1006
- {
1007
- bodyText: {
1008
- id: "content-releases.dialog.confirmation-message",
1009
- defaultMessage: "Are you sure you want to delete this release?"
1010
- },
1011
- isOpen: showWarningSubmit,
1012
- isConfirmButtonLoading: isDeletingRelease,
1013
- onToggleDialog: toggleWarningSubmit,
1014
- onConfirm: handleDeleteRelease
1015
- }
1016
- )
1017
- ]
1018
- }
1019
- );
1020
- };
1021
- const LinkCard = styled__default.default(v2.Link)`
1022
- display: block;
1023
- `;
1024
- const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
1025
- const { formatMessage } = reactIntl.useIntl();
1026
- const IsSchedulingEnabled = window.strapi.future.isEnabled("contentReleasesScheduling");
1027
- if (isError) {
1028
- return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.AnErrorOccurred, {});
1029
- }
1030
- if (releases?.length === 0) {
1031
- return /* @__PURE__ */ jsxRuntime.jsx(
1032
- designSystem.EmptyStateLayout,
1033
- {
1034
- content: formatMessage(
1035
- {
1036
- id: "content-releases.page.Releases.tab.emptyEntries",
1037
- defaultMessage: "No releases"
1038
- },
1039
- {
1040
- target: sectionTitle
1041
- }
1042
- ),
1043
- icon: /* @__PURE__ */ jsxRuntime.jsx(icons.EmptyDocuments, { width: "10rem" })
1044
- }
1045
- );
1046
- }
1047
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid, { gap: 4, children: releases.map(({ id, name, actions, scheduledAt }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.GridItem, { col: 3, s: 6, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsx(LinkCard, { href: `content-releases/${id}`, isExternal: false, children: /* @__PURE__ */ jsxRuntime.jsxs(
1048
- designSystem.Flex,
1049
- {
1050
- direction: "column",
1051
- justifyContent: "space-between",
1052
- padding: 4,
1053
- hasRadius: true,
1054
- background: "neutral0",
1055
- shadow: "tableShadow",
1056
- height: "100%",
1057
- width: "100%",
1058
- alignItems: "start",
1059
- gap: 2,
1060
- children: [
1061
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { as: "h3", variant: "delta", fontWeight: "bold", children: name }),
1062
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: IsSchedulingEnabled ? scheduledAt ? /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
1063
- id: "content-releases.pages.Releases.not-scheduled",
1064
- defaultMessage: "Not scheduled"
1065
- }) : formatMessage(
1066
- {
1067
- id: "content-releases.page.Releases.release-item.entries",
1068
- defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
1069
- },
1070
- { number: actions.meta.count }
1071
- ) })
1072
- ]
1073
- }
1074
- ) }) }, id)) });
1075
- };
1076
- const StyledAlert = styled__default.default(designSystem.Alert)`
1077
- button {
1078
- display: none;
1079
- }
1080
- p + div {
1081
- margin-left: auto;
1082
- }
1083
- `;
1084
- const INITIAL_FORM_VALUES = {
1085
- name: "",
1086
- date: null,
1087
- time: "",
1088
- // Remove future flag check after Scheduling Beta release and replace with true as creating new release should include scheduling by default
1089
- isScheduled: window.strapi.future.isEnabled("contentReleasesScheduling"),
1090
- scheduledAt: null,
1091
- timezone: null
1092
- };
1093
- const ReleasesPage = () => {
1094
- const tabRef = React__namespace.useRef(null);
1095
- const location = reactRouterDom.useLocation();
1096
- const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
1097
- const toggleNotification = helperPlugin.useNotification();
1098
- const { formatMessage } = reactIntl.useIntl();
1099
- const { push, replace } = reactRouterDom.useHistory();
1211
+ const toggleNotification = helperPlugin.useNotification();
1100
1212
  const { formatAPIError } = helperPlugin.useAPIErrorHandler();
1101
- const [{ query }, setQuery] = helperPlugin.useQueryParams();
1102
- const response = index.useGetReleasesQuery(query);
1103
- const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
1104
- const { getFeature } = strapiAdmin.useLicenseLimits();
1105
- const { maximumReleases = 3 } = getFeature("cms-content-releases");
1106
- const { trackUsage } = helperPlugin.useTracking();
1107
- const { isLoading, isSuccess, isError } = response;
1108
- const activeTab = response?.currentData?.meta?.activeTab || "pending";
1109
- const activeTabIndex = ["pending", "done"].indexOf(activeTab);
1110
- React__namespace.useEffect(() => {
1111
- if (location?.state?.errors) {
1112
- toggleNotification({
1113
- type: "warning",
1114
- title: formatMessage({
1115
- id: "content-releases.pages.Releases.notification.error.title",
1116
- defaultMessage: "Your request could not be processed."
1117
- }),
1118
- message: formatMessage({
1119
- id: "content-releases.pages.Releases.notification.error.message",
1120
- defaultMessage: "Please try again or open another release."
1121
- })
1122
- });
1123
- replace({ state: null });
1124
- }
1125
- }, [formatMessage, location?.state?.errors, replace, toggleNotification]);
1126
- React__namespace.useEffect(() => {
1127
- if (tabRef.current) {
1128
- tabRef.current._handlers.setSelectedTabIndex(activeTabIndex);
1129
- }
1130
- }, [activeTabIndex]);
1131
- const toggleAddReleaseModal = () => {
1213
+ const { replace } = reactRouterDom.useHistory();
1214
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
1215
+ const [showWarningSubmit, setWarningSubmit] = React__namespace.useState(false);
1216
+ const {
1217
+ isLoading: isLoadingDetails,
1218
+ data,
1219
+ isSuccess: isSuccessDetails
1220
+ } = index.useGetReleaseQuery({ id: releaseId });
1221
+ const [updateRelease, { isLoading: isSubmittingForm }] = index.useUpdateReleaseMutation();
1222
+ const [deleteRelease, { isLoading: isDeletingRelease }] = index.useDeleteReleaseMutation();
1223
+ const toggleEditReleaseModal = () => {
1132
1224
  setReleaseModalShown((prev) => !prev);
1133
1225
  };
1134
- if (isLoading) {
1135
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { "aria-busy": isLoading, children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) });
1136
- }
1137
- const totalReleases = isSuccess && response.currentData?.meta?.pagination?.total || 0;
1138
- const hasReachedMaximumPendingReleases = totalReleases >= maximumReleases;
1139
- const handleTabChange = (index2) => {
1140
- setQuery({
1141
- ...query,
1142
- page: 1,
1143
- pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
1144
- filters: {
1145
- releasedAt: {
1146
- $notNull: index2 === 0 ? false : true
1147
- }
1226
+ const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
1227
+ if (isLoadingDetails) {
1228
+ return /* @__PURE__ */ jsxRuntime.jsx(
1229
+ ReleaseDetailsLayout,
1230
+ {
1231
+ toggleEditReleaseModal,
1232
+ toggleWarningSubmit,
1233
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.LoadingIndicatorPage, {}) })
1148
1234
  }
1235
+ );
1236
+ }
1237
+ const releaseData = isSuccessDetails && data?.data || null;
1238
+ const title = releaseData?.name || "";
1239
+ const timezone = releaseData?.timezone ?? null;
1240
+ const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
1241
+ const date = scheduledAt ? new Date(format__default.default(scheduledAt, "yyyy-MM-dd")) : null;
1242
+ const time = scheduledAt ? format__default.default(scheduledAt, "HH:mm") : "";
1243
+ const handleEditRelease = async (values) => {
1244
+ const response = await updateRelease({
1245
+ id: releaseId,
1246
+ name: values.name,
1247
+ scheduledAt: values.scheduledAt,
1248
+ timezone: values.timezone
1149
1249
  });
1150
- };
1151
- const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
1152
- const response2 = await createRelease({
1153
- name,
1154
- scheduledAt,
1155
- timezone
1156
- });
1157
- if ("data" in response2) {
1250
+ if ("data" in response) {
1158
1251
  toggleNotification({
1159
1252
  type: "success",
1160
1253
  message: formatMessage({
1161
- id: "content-releases.modal.release-created-notification-success",
1162
- defaultMessage: "Release created."
1254
+ id: "content-releases.modal.release-updated-notification-success",
1255
+ defaultMessage: "Release updated."
1163
1256
  })
1164
1257
  });
1165
- trackUsage("didCreateRelease");
1166
- push(`/plugins/content-releases/${response2.data.data.id}`);
1167
- } else if (index.isAxiosError(response2.error)) {
1258
+ } else if (index.isAxiosError(response.error)) {
1168
1259
  toggleNotification({
1169
1260
  type: "warning",
1170
- message: formatAPIError(response2.error)
1261
+ message: formatAPIError(response.error)
1171
1262
  });
1172
1263
  } else {
1173
1264
  toggleNotification({
@@ -1175,135 +1266,65 @@ const ReleasesPage = () => {
1175
1266
  message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1176
1267
  });
1177
1268
  }
1269
+ toggleEditReleaseModal();
1178
1270
  };
1179
- return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoading, children: [
1180
- /* @__PURE__ */ jsxRuntime.jsx(
1181
- designSystem.HeaderLayout,
1182
- {
1183
- title: formatMessage({
1184
- id: "content-releases.pages.Releases.title",
1185
- defaultMessage: "Releases"
1186
- }),
1187
- subtitle: formatMessage(
1188
- {
1189
- id: "content-releases.pages.Releases.header-subtitle",
1190
- defaultMessage: "{number, plural, =0 {No releases} one {# release} other {# releases}}"
1191
- },
1192
- { number: totalReleases }
1193
- ),
1194
- primaryAction: /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPermissions, { permissions: index.PERMISSIONS.create, children: /* @__PURE__ */ jsxRuntime.jsx(
1195
- designSystem.Button,
1196
- {
1197
- startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1198
- onClick: toggleAddReleaseModal,
1199
- disabled: hasReachedMaximumPendingReleases,
1200
- children: formatMessage({
1201
- id: "content-releases.header.actions.add-release",
1202
- defaultMessage: "New release"
1203
- })
1204
- }
1205
- ) })
1206
- }
1207
- ),
1208
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.ContentLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1209
- activeTab === "pending" && hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
1210
- StyledAlert,
1211
- {
1212
- marginBottom: 6,
1213
- action: /* @__PURE__ */ jsxRuntime.jsx(v2.Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
1214
- id: "content-releases.pages.Releases.max-limit-reached.action",
1215
- defaultMessage: "Explore plans"
1216
- }) }),
1217
- title: formatMessage(
1218
- {
1219
- id: "content-releases.pages.Releases.max-limit-reached.title",
1220
- defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
1221
- },
1222
- { number: maximumReleases }
1223
- ),
1224
- onClose: () => {
1225
- },
1226
- closeLabel: "",
1227
- children: formatMessage({
1228
- id: "content-releases.pages.Releases.max-limit-reached.message",
1229
- defaultMessage: "Upgrade to manage an unlimited number of releases."
1230
- })
1231
- }
1232
- ),
1233
- /* @__PURE__ */ jsxRuntime.jsxs(
1234
- designSystem.TabGroup,
1235
- {
1236
- label: formatMessage({
1237
- id: "content-releases.pages.Releases.tab-group.label",
1238
- defaultMessage: "Releases list"
1239
- }),
1240
- variant: "simple",
1241
- initialSelectedTabIndex: activeTabIndex,
1242
- onTabChange: handleTabChange,
1243
- ref: tabRef,
1244
- children: [
1245
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingBottom: 8, children: [
1246
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs, { children: [
1247
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1248
- id: "content-releases.pages.Releases.tab.pending",
1249
- defaultMessage: "Pending"
1250
- }) }),
1251
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tab, { children: formatMessage({
1252
- id: "content-releases.pages.Releases.tab.done",
1253
- defaultMessage: "Done"
1254
- }) })
1255
- ] }),
1256
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {})
1257
- ] }),
1258
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.TabPanels, { children: [
1259
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
1260
- ReleasesGrid,
1261
- {
1262
- sectionTitle: "pending",
1263
- releases: response?.currentData?.data,
1264
- isError
1265
- }
1266
- ) }),
1267
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.TabPanel, { children: /* @__PURE__ */ jsxRuntime.jsx(
1268
- ReleasesGrid,
1269
- {
1270
- sectionTitle: "done",
1271
- releases: response?.currentData?.data,
1272
- isError
1273
- }
1274
- ) })
1275
- ] })
1276
- ]
1277
- }
1278
- ),
1279
- totalReleases > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { paddingTop: 4, alignItems: "flex-end", justifyContent: "space-between", children: [
1280
- /* @__PURE__ */ jsxRuntime.jsx(
1281
- helperPlugin.PageSizeURLQuery,
1271
+ const handleDeleteRelease = async () => {
1272
+ const response = await deleteRelease({
1273
+ id: releaseId
1274
+ });
1275
+ if ("data" in response) {
1276
+ replace("/plugins/content-releases");
1277
+ } else if (index.isAxiosError(response.error)) {
1278
+ toggleNotification({
1279
+ type: "warning",
1280
+ message: formatAPIError(response.error)
1281
+ });
1282
+ } else {
1283
+ toggleNotification({
1284
+ type: "warning",
1285
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1286
+ });
1287
+ }
1288
+ };
1289
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1290
+ ReleaseDetailsLayout,
1291
+ {
1292
+ toggleEditReleaseModal,
1293
+ toggleWarningSubmit,
1294
+ children: [
1295
+ /* @__PURE__ */ jsxRuntime.jsx(ReleaseDetailsBody, {}),
1296
+ releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
1297
+ ReleaseModal,
1282
1298
  {
1283
- options: ["8", "16", "32", "64"],
1284
- defaultValue: response?.currentData?.meta?.pagination?.pageSize.toString()
1299
+ handleClose: toggleEditReleaseModal,
1300
+ handleSubmit: handleEditRelease,
1301
+ isLoading: isLoadingDetails || isSubmittingForm,
1302
+ initialValues: {
1303
+ name: title || "",
1304
+ scheduledAt,
1305
+ date,
1306
+ time,
1307
+ isScheduled: Boolean(scheduledAt),
1308
+ timezone
1309
+ }
1285
1310
  }
1286
1311
  ),
1287
1312
  /* @__PURE__ */ jsxRuntime.jsx(
1288
- helperPlugin.PaginationURLQuery,
1313
+ helperPlugin.ConfirmDialog,
1289
1314
  {
1290
- pagination: {
1291
- pageCount: response?.currentData?.meta?.pagination?.pageCount || 0
1292
- }
1315
+ bodyText: {
1316
+ id: "content-releases.dialog.confirmation-message",
1317
+ defaultMessage: "Are you sure you want to delete this release?"
1318
+ },
1319
+ isOpen: showWarningSubmit,
1320
+ isConfirmButtonLoading: isDeletingRelease,
1321
+ onToggleDialog: toggleWarningSubmit,
1322
+ onConfirm: handleDeleteRelease
1293
1323
  }
1294
1324
  )
1295
- ] })
1296
- ] }) }),
1297
- releaseModalShown && /* @__PURE__ */ jsxRuntime.jsx(
1298
- ReleaseModal,
1299
- {
1300
- handleClose: toggleAddReleaseModal,
1301
- handleSubmit: handleAddRelease,
1302
- isLoading: isSubmittingForm,
1303
- initialValues: INITIAL_FORM_VALUES
1304
- }
1305
- )
1306
- ] });
1325
+ ]
1326
+ }
1327
+ );
1307
1328
  };
1308
1329
  const App = () => {
1309
1330
  return /* @__PURE__ */ jsxRuntime.jsx(helperPlugin.CheckPagePermissions, { permissions: index.PERMISSIONS.main, children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Switch, { children: [
@@ -1312,4 +1333,4 @@ const App = () => {
1312
1333
  ] }) });
1313
1334
  };
1314
1335
  exports.App = App;
1315
- //# sourceMappingURL=App-OK4Xac-O.js.map
1336
+ //# sourceMappingURL=App-l62gIUTX.js.map