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

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 (143) hide show
  1. package/LICENSE +17 -1
  2. package/dist/_chunks/App-CiZCkScI.mjs +1558 -0
  3. package/dist/_chunks/App-CiZCkScI.mjs.map +1 -0
  4. package/dist/_chunks/App-SGjO5UPV.js +1578 -0
  5. package/dist/_chunks/App-SGjO5UPV.js.map +1 -0
  6. package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases--qQepXpP.js} +9 -8
  7. package/dist/_chunks/PurchaseContentReleases--qQepXpP.js.map +1 -0
  8. package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-D-n-w-st.mjs} +10 -9
  9. package/dist/_chunks/PurchaseContentReleases-D-n-w-st.mjs.map +1 -0
  10. package/dist/_chunks/ReleasesSettingsPage-Cto_NLUd.js +178 -0
  11. package/dist/_chunks/ReleasesSettingsPage-Cto_NLUd.js.map +1 -0
  12. package/dist/_chunks/ReleasesSettingsPage-DQT8N3A-.mjs +178 -0
  13. package/dist/_chunks/ReleasesSettingsPage-DQT8N3A-.mjs.map +1 -0
  14. package/dist/_chunks/{en-r0otWaln.js → en-BWPPsSH-.js} +31 -6
  15. package/dist/_chunks/en-BWPPsSH-.js.map +1 -0
  16. package/dist/_chunks/{en-veqvqeEr.mjs → en-D9Q4YW03.mjs} +31 -6
  17. package/dist/_chunks/en-D9Q4YW03.mjs.map +1 -0
  18. package/dist/_chunks/index-BjvFfTtA.mjs +1386 -0
  19. package/dist/_chunks/index-BjvFfTtA.mjs.map +1 -0
  20. package/dist/_chunks/index-CyU534vL.js +1404 -0
  21. package/dist/_chunks/index-CyU534vL.js.map +1 -0
  22. package/dist/_chunks/schemas-DBYv9gK8.js +61 -0
  23. package/dist/_chunks/schemas-DBYv9gK8.js.map +1 -0
  24. package/dist/_chunks/schemas-DdA2ic2U.mjs +44 -0
  25. package/dist/_chunks/schemas-DdA2ic2U.mjs.map +1 -0
  26. package/dist/admin/index.js +1 -15
  27. package/dist/admin/index.js.map +1 -1
  28. package/dist/admin/index.mjs +2 -16
  29. package/dist/admin/index.mjs.map +1 -1
  30. package/dist/admin/src/components/EntryValidationPopover.d.ts +13 -0
  31. package/dist/admin/src/components/RelativeTime.d.ts +28 -0
  32. package/dist/admin/src/components/ReleaseAction.d.ts +3 -0
  33. package/dist/admin/src/components/ReleaseActionMenu.d.ts +26 -0
  34. package/dist/admin/src/components/ReleaseActionModal.d.ts +24 -0
  35. package/dist/admin/src/components/ReleaseActionOptions.d.ts +9 -0
  36. package/dist/admin/src/components/ReleaseListCell.d.ts +28 -0
  37. package/dist/admin/src/components/ReleaseModal.d.ts +17 -0
  38. package/dist/admin/src/components/ReleasesPanel.d.ts +3 -0
  39. package/dist/admin/src/constants.d.ts +76 -0
  40. package/dist/admin/src/index.d.ts +3 -0
  41. package/dist/admin/src/modules/hooks.d.ts +7 -0
  42. package/dist/admin/src/pages/App.d.ts +1 -0
  43. package/dist/admin/src/pages/PurchaseContentReleases.d.ts +2 -0
  44. package/dist/admin/src/pages/ReleaseDetailsPage.d.ts +2 -0
  45. package/dist/admin/src/pages/ReleasesPage.d.ts +8 -0
  46. package/dist/admin/src/pages/ReleasesSettingsPage.d.ts +1 -0
  47. package/dist/admin/src/pages/tests/mockReleaseDetailsPageData.d.ts +181 -0
  48. package/dist/admin/src/pages/tests/mockReleasesPageData.d.ts +39 -0
  49. package/dist/admin/src/pluginId.d.ts +1 -0
  50. package/dist/admin/src/services/release.d.ts +112 -0
  51. package/dist/admin/src/store/hooks.d.ts +7 -0
  52. package/dist/admin/src/utils/api.d.ts +6 -0
  53. package/dist/admin/src/utils/prefixPluginTranslations.d.ts +3 -0
  54. package/dist/admin/src/utils/time.d.ts +10 -0
  55. package/dist/admin/src/validation/schemas.d.ts +6 -0
  56. package/dist/server/index.js +1233 -515
  57. package/dist/server/index.js.map +1 -1
  58. package/dist/server/index.mjs +1232 -513
  59. package/dist/server/index.mjs.map +1 -1
  60. package/dist/server/src/bootstrap.d.ts +5 -0
  61. package/dist/server/src/bootstrap.d.ts.map +1 -0
  62. package/dist/server/src/constants.d.ts +21 -0
  63. package/dist/server/src/constants.d.ts.map +1 -0
  64. package/dist/server/src/content-types/index.d.ts +97 -0
  65. package/dist/server/src/content-types/index.d.ts.map +1 -0
  66. package/dist/server/src/content-types/release/index.d.ts +48 -0
  67. package/dist/server/src/content-types/release/index.d.ts.map +1 -0
  68. package/dist/server/src/content-types/release/schema.d.ts +47 -0
  69. package/dist/server/src/content-types/release/schema.d.ts.map +1 -0
  70. package/dist/server/src/content-types/release-action/index.d.ts +48 -0
  71. package/dist/server/src/content-types/release-action/index.d.ts.map +1 -0
  72. package/dist/server/src/content-types/release-action/schema.d.ts +47 -0
  73. package/dist/server/src/content-types/release-action/schema.d.ts.map +1 -0
  74. package/dist/server/src/controllers/index.d.ts +25 -0
  75. package/dist/server/src/controllers/index.d.ts.map +1 -0
  76. package/dist/server/src/controllers/release-action.d.ts +10 -0
  77. package/dist/server/src/controllers/release-action.d.ts.map +1 -0
  78. package/dist/server/src/controllers/release.d.ts +18 -0
  79. package/dist/server/src/controllers/release.d.ts.map +1 -0
  80. package/dist/server/src/controllers/settings.d.ts +11 -0
  81. package/dist/server/src/controllers/settings.d.ts.map +1 -0
  82. package/dist/server/src/controllers/validation/release-action.d.ts +14 -0
  83. package/dist/server/src/controllers/validation/release-action.d.ts.map +1 -0
  84. package/dist/server/src/controllers/validation/release.d.ts +4 -0
  85. package/dist/server/src/controllers/validation/release.d.ts.map +1 -0
  86. package/dist/server/src/controllers/validation/settings.d.ts +3 -0
  87. package/dist/server/src/controllers/validation/settings.d.ts.map +1 -0
  88. package/dist/server/src/destroy.d.ts +5 -0
  89. package/dist/server/src/destroy.d.ts.map +1 -0
  90. package/dist/server/src/index.d.ts +2111 -0
  91. package/dist/server/src/index.d.ts.map +1 -0
  92. package/dist/server/src/middlewares/documents.d.ts +6 -0
  93. package/dist/server/src/middlewares/documents.d.ts.map +1 -0
  94. package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts +9 -0
  95. package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts.map +1 -0
  96. package/dist/server/src/migrations/index.d.ts +13 -0
  97. package/dist/server/src/migrations/index.d.ts.map +1 -0
  98. package/dist/server/src/register.d.ts +5 -0
  99. package/dist/server/src/register.d.ts.map +1 -0
  100. package/dist/server/src/routes/index.d.ts +51 -0
  101. package/dist/server/src/routes/index.d.ts.map +1 -0
  102. package/dist/server/src/routes/release-action.d.ts +18 -0
  103. package/dist/server/src/routes/release-action.d.ts.map +1 -0
  104. package/dist/server/src/routes/release.d.ts +18 -0
  105. package/dist/server/src/routes/release.d.ts.map +1 -0
  106. package/dist/server/src/routes/settings.d.ts +18 -0
  107. package/dist/server/src/routes/settings.d.ts.map +1 -0
  108. package/dist/server/src/services/index.d.ts +1824 -0
  109. package/dist/server/src/services/index.d.ts.map +1 -0
  110. package/dist/server/src/services/release-action.d.ts +34 -0
  111. package/dist/server/src/services/release-action.d.ts.map +1 -0
  112. package/dist/server/src/services/release.d.ts +31 -0
  113. package/dist/server/src/services/release.d.ts.map +1 -0
  114. package/dist/server/src/services/scheduling.d.ts +18 -0
  115. package/dist/server/src/services/scheduling.d.ts.map +1 -0
  116. package/dist/server/src/services/settings.d.ts +13 -0
  117. package/dist/server/src/services/settings.d.ts.map +1 -0
  118. package/dist/server/src/services/validation.d.ts +18 -0
  119. package/dist/server/src/services/validation.d.ts.map +1 -0
  120. package/dist/server/src/utils/index.d.ts +35 -0
  121. package/dist/server/src/utils/index.d.ts.map +1 -0
  122. package/dist/shared/contracts/release-actions.d.ts +137 -0
  123. package/dist/shared/contracts/release-actions.d.ts.map +1 -0
  124. package/dist/shared/contracts/releases.d.ts +184 -0
  125. package/dist/shared/contracts/releases.d.ts.map +1 -0
  126. package/dist/shared/contracts/settings.d.ts +39 -0
  127. package/dist/shared/contracts/settings.d.ts.map +1 -0
  128. package/dist/shared/types.d.ts +24 -0
  129. package/dist/shared/types.d.ts.map +1 -0
  130. package/package.json +35 -40
  131. package/dist/_chunks/App-OK4Xac-O.js +0 -1315
  132. package/dist/_chunks/App-OK4Xac-O.js.map +0 -1
  133. package/dist/_chunks/App-xAkiD42p.mjs +0 -1292
  134. package/dist/_chunks/App-xAkiD42p.mjs.map +0 -1
  135. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
  136. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
  137. package/dist/_chunks/en-r0otWaln.js.map +0 -1
  138. package/dist/_chunks/en-veqvqeEr.mjs.map +0 -1
  139. package/dist/_chunks/index-JvA2_26n.js +0 -1015
  140. package/dist/_chunks/index-JvA2_26n.js.map +0 -1
  141. package/dist/_chunks/index-exoiSU3V.mjs +0 -994
  142. package/dist/_chunks/index-exoiSU3V.mjs.map +0 -1
  143. package/strapi-server.js +0 -3
@@ -0,0 +1,1578 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const strapiAdmin$1 = require("@strapi/admin/strapi-admin");
5
+ const reactRouterDom = require("react-router-dom");
6
+ const index = require("./index-CyU534vL.js");
7
+ const React = require("react");
8
+ const designSystem = require("@strapi/design-system");
9
+ const icons = require("@strapi/icons");
10
+ const symbols = require("@strapi/icons/symbols");
11
+ const format = require("date-fns/format");
12
+ const dateFnsTz = require("date-fns-tz");
13
+ const reactIntl = require("react-intl");
14
+ const styledComponents = require("styled-components");
15
+ const strapiAdmin = require("@strapi/content-manager/strapi-admin");
16
+ const qs = require("qs");
17
+ const dateFns = require("date-fns");
18
+ const formik = require("formik");
19
+ const schemas = require("./schemas-DBYv9gK8.js");
20
+ const reactRedux = require("react-redux");
21
+ const ee = require("@strapi/admin/strapi-admin/ee");
22
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
23
+ function _interopNamespace(e) {
24
+ if (e && e.__esModule) return e;
25
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
26
+ if (e) {
27
+ for (const k in e) {
28
+ if (k !== "default") {
29
+ const d = Object.getOwnPropertyDescriptor(e, k);
30
+ Object.defineProperty(n, k, d.get ? d : {
31
+ enumerable: true,
32
+ get: () => e[k]
33
+ });
34
+ }
35
+ }
36
+ }
37
+ n.default = e;
38
+ return Object.freeze(n);
39
+ }
40
+ const React__namespace = /* @__PURE__ */ _interopNamespace(React);
41
+ const format__default = /* @__PURE__ */ _interopDefault(format);
42
+ const StyledPopoverFlex = styledComponents.styled(designSystem.Flex)`
43
+ width: 100%;
44
+ max-width: 256px;
45
+
46
+ & > * {
47
+ border-bottom: 1px solid ${({ theme }) => theme.colors.neutral150};
48
+ }
49
+
50
+ & > *:last-child {
51
+ border-bottom: none;
52
+ }
53
+ `;
54
+ const EntryStatusTrigger = ({
55
+ action,
56
+ status,
57
+ hasErrors,
58
+ requiredStage,
59
+ entryStage
60
+ }) => {
61
+ const { formatMessage } = reactIntl.useIntl();
62
+ if (action === "publish") {
63
+ if (hasErrors || requiredStage && requiredStage.id !== entryStage?.id) {
64
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Popover.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(
65
+ designSystem.Button,
66
+ {
67
+ variant: "ghost",
68
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CrossCircle, { fill: "danger600" }),
69
+ endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CaretDown, {}),
70
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "danger600", variant: "omega", fontWeight: "bold", children: formatMessage({
71
+ id: "content-releases.pages.ReleaseDetails.entry-validation.not-ready",
72
+ defaultMessage: "Not ready to publish"
73
+ }) })
74
+ }
75
+ ) });
76
+ }
77
+ if (status === "draft") {
78
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Popover.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(
79
+ designSystem.Button,
80
+ {
81
+ variant: "ghost",
82
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CheckCircle, { fill: "success600" }),
83
+ endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CaretDown, {}),
84
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", variant: "omega", fontWeight: "bold", children: formatMessage({
85
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-publish",
86
+ defaultMessage: "Ready to publish"
87
+ }) })
88
+ }
89
+ ) });
90
+ }
91
+ if (status === "modified") {
92
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Popover.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(
93
+ designSystem.Button,
94
+ {
95
+ variant: "ghost",
96
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowsCounterClockwise, { fill: "alternative600" }),
97
+ endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CaretDown, {}),
98
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", textColor: "alternative600", children: formatMessage({
99
+ id: "content-releases.pages.ReleaseDetails.entry-validation.modified",
100
+ defaultMessage: "Ready to publish changes"
101
+ }) })
102
+ }
103
+ ) });
104
+ }
105
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Popover.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(
106
+ designSystem.Button,
107
+ {
108
+ variant: "ghost",
109
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CheckCircle, { fill: "success600" }),
110
+ endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CaretDown, {}),
111
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", variant: "omega", fontWeight: "bold", children: formatMessage({
112
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-published",
113
+ defaultMessage: "Already published"
114
+ }) })
115
+ }
116
+ ) });
117
+ }
118
+ if (status === "published") {
119
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Popover.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(
120
+ designSystem.Button,
121
+ {
122
+ variant: "ghost",
123
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CheckCircle, { fill: "success600" }),
124
+ endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CaretDown, {}),
125
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", variant: "omega", fontWeight: "bold", children: formatMessage({
126
+ id: "content-releases.pages.ReleaseDetails.entry-validation.ready-to-unpublish",
127
+ defaultMessage: "Ready to unpublish"
128
+ }) })
129
+ }
130
+ ) });
131
+ }
132
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Popover.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CheckCircle, { fill: "success600" }), endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.CaretDown, {}), children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "success600", variant: "omega", fontWeight: "bold", children: formatMessage({
133
+ id: "content-releases.pages.ReleaseDetails.entry-validation.already-unpublished",
134
+ defaultMessage: "Already unpublished"
135
+ }) }) }) });
136
+ };
137
+ const FieldsValidation = ({
138
+ hasErrors,
139
+ errors,
140
+ kind,
141
+ contentTypeUid,
142
+ documentId,
143
+ locale
144
+ }) => {
145
+ const { formatMessage } = reactIntl.useIntl();
146
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 1, width: "100%", padding: 5, children: [
147
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, width: "100%", children: [
148
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: formatMessage({
149
+ id: "content-releases.pages.ReleaseDetails.entry-validation.fields",
150
+ defaultMessage: "Fields"
151
+ }) }),
152
+ hasErrors ? /* @__PURE__ */ jsxRuntime.jsx(icons.CrossCircle, { fill: "danger600" }) : /* @__PURE__ */ jsxRuntime.jsx(icons.CheckCircle, { fill: "success600" })
153
+ ] }),
154
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { width: "100%", textColor: "neutral600", children: hasErrors ? formatMessage(
155
+ {
156
+ id: "content-releases.pages.ReleaseDetails.entry-validation.fields.error",
157
+ defaultMessage: "{errors} errors on fields."
158
+ },
159
+ { errors: errors ? Object.keys(errors).length : 0 }
160
+ ) : formatMessage({
161
+ id: "content-releases.pages.ReleaseDetails.entry-validation.fields.success",
162
+ defaultMessage: "All fields are filled correctly."
163
+ }) }),
164
+ hasErrors && /* @__PURE__ */ jsxRuntime.jsx(
165
+ designSystem.LinkButton,
166
+ {
167
+ tag: reactRouterDom.Link,
168
+ to: {
169
+ pathname: `/content-manager/${kind === "collectionType" ? "collection-types" : "single-types"}/${contentTypeUid}/${documentId}`,
170
+ search: locale ? qs.stringify({
171
+ plugins: {
172
+ i18n: {
173
+ locale
174
+ }
175
+ }
176
+ }) : ""
177
+ },
178
+ variant: "secondary",
179
+ fullWidth: true,
180
+ state: { forceValidation: true },
181
+ children: formatMessage({
182
+ id: "content-releases.pages.ReleaseDetails.entry-validation.fields.see-errors",
183
+ defaultMessage: "See errors"
184
+ })
185
+ }
186
+ )
187
+ ] });
188
+ };
189
+ const getReviewStageIcon = ({
190
+ contentTypeHasReviewWorkflow,
191
+ requiredStage,
192
+ entryStage
193
+ }) => {
194
+ if (!contentTypeHasReviewWorkflow) {
195
+ return /* @__PURE__ */ jsxRuntime.jsx(icons.CheckCircle, { fill: "neutral200" });
196
+ }
197
+ if (requiredStage && requiredStage.id !== entryStage?.id) {
198
+ return /* @__PURE__ */ jsxRuntime.jsx(icons.CrossCircle, { fill: "danger600" });
199
+ }
200
+ return /* @__PURE__ */ jsxRuntime.jsx(icons.CheckCircle, { fill: "success600" });
201
+ };
202
+ const getReviewStageMessage = ({
203
+ contentTypeHasReviewWorkflow,
204
+ requiredStage,
205
+ entryStage,
206
+ formatMessage
207
+ }) => {
208
+ if (!contentTypeHasReviewWorkflow) {
209
+ return formatMessage({
210
+ id: "content-releases.pages.ReleaseDetails.entry-validation.review-stage.not-enabled",
211
+ defaultMessage: "This entry is not associated to any workflow."
212
+ });
213
+ }
214
+ if (requiredStage && requiredStage.id !== entryStage?.id) {
215
+ return formatMessage(
216
+ {
217
+ id: "content-releases.pages.ReleaseDetails.entry-validation.review-stage.not-ready",
218
+ defaultMessage: "This entry is not at the required stage for publishing. ({stageName})"
219
+ },
220
+ {
221
+ stageName: requiredStage?.name ?? ""
222
+ }
223
+ );
224
+ }
225
+ if (requiredStage && requiredStage.id === entryStage?.id) {
226
+ return formatMessage(
227
+ {
228
+ id: "content-releases.pages.ReleaseDetails.entry-validation.review-stage.ready",
229
+ defaultMessage: "This entry is at the required stage for publishing. ({stageName})"
230
+ },
231
+ {
232
+ stageName: requiredStage?.name ?? ""
233
+ }
234
+ );
235
+ }
236
+ return formatMessage({
237
+ id: "content-releases.pages.ReleaseDetails.entry-validation.review-stage.stage-not-required",
238
+ defaultMessage: "No required stage for publication"
239
+ });
240
+ };
241
+ const ReviewStageValidation = ({
242
+ contentTypeHasReviewWorkflow,
243
+ requiredStage,
244
+ entryStage
245
+ }) => {
246
+ const { formatMessage } = reactIntl.useIntl();
247
+ const Icon = getReviewStageIcon({
248
+ contentTypeHasReviewWorkflow,
249
+ requiredStage,
250
+ entryStage
251
+ });
252
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 1, width: "100%", padding: 5, children: [
253
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, width: "100%", children: [
254
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: formatMessage({
255
+ id: "content-releases.pages.ReleaseDetails.entry-validation.review-stage",
256
+ defaultMessage: "Review stage"
257
+ }) }),
258
+ Icon
259
+ ] }),
260
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: getReviewStageMessage({
261
+ contentTypeHasReviewWorkflow,
262
+ requiredStage,
263
+ entryStage,
264
+ formatMessage
265
+ }) })
266
+ ] });
267
+ };
268
+ const EntryValidationPopover = ({
269
+ schema,
270
+ entry,
271
+ status,
272
+ action
273
+ }) => {
274
+ const { validate, isLoading } = strapiAdmin.unstable_useDocument(
275
+ {
276
+ collectionType: schema?.kind ?? "",
277
+ model: schema?.uid ?? ""
278
+ },
279
+ {
280
+ // useDocument makes a request to get more data about the entry, but we only want to have the validation function so we skip the request
281
+ skip: true
282
+ }
283
+ );
284
+ const errors = isLoading ? null : validate(entry);
285
+ const hasErrors = errors ? Object.keys(errors).length > 0 : false;
286
+ const contentTypeHasReviewWorkflow = schema?.hasReviewWorkflow ?? false;
287
+ const requiredStage = schema?.stageRequiredToPublish;
288
+ const entryStage = entry.strapi_stage;
289
+ if (isLoading) {
290
+ return null;
291
+ }
292
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Popover.Root, { children: [
293
+ /* @__PURE__ */ jsxRuntime.jsx(
294
+ EntryStatusTrigger,
295
+ {
296
+ action,
297
+ status,
298
+ hasErrors,
299
+ requiredStage,
300
+ entryStage
301
+ }
302
+ ),
303
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Popover.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(StyledPopoverFlex, { direction: "column", children: [
304
+ /* @__PURE__ */ jsxRuntime.jsx(
305
+ FieldsValidation,
306
+ {
307
+ hasErrors,
308
+ errors,
309
+ contentTypeUid: schema?.uid,
310
+ kind: schema?.kind,
311
+ documentId: entry.documentId,
312
+ locale: entry.locale
313
+ }
314
+ ),
315
+ /* @__PURE__ */ jsxRuntime.jsx(
316
+ ReviewStageValidation,
317
+ {
318
+ contentTypeHasReviewWorkflow,
319
+ requiredStage,
320
+ entryStage
321
+ }
322
+ )
323
+ ] }) })
324
+ ] });
325
+ };
326
+ const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
327
+ const RelativeTime$1 = React__namespace.forwardRef(
328
+ ({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
329
+ const { formatRelativeTime, formatDate, formatTime } = reactIntl.useIntl();
330
+ const interval = dateFns.intervalToDuration({
331
+ start: timestamp,
332
+ end: Date.now()
333
+ // see https://github.com/date-fns/date-fns/issues/2891 – No idea why it's all partial it returns it every time.
334
+ });
335
+ const unit = intervals.find((intervalUnit) => {
336
+ return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
337
+ });
338
+ const relativeTime = dateFns.isPast(timestamp) ? -interval[unit] : interval[unit];
339
+ const customInterval = customIntervals.find(
340
+ (custom) => interval[custom.unit] < custom.threshold
341
+ );
342
+ const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, { numeric: "auto" });
343
+ return /* @__PURE__ */ jsxRuntime.jsx(
344
+ "time",
345
+ {
346
+ ref: forwardedRef,
347
+ dateTime: timestamp.toISOString(),
348
+ role: "time",
349
+ title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
350
+ ...restProps,
351
+ children: displayText
352
+ }
353
+ );
354
+ }
355
+ );
356
+ const ReleaseModal = ({
357
+ handleClose,
358
+ open,
359
+ handleSubmit,
360
+ initialValues,
361
+ isLoading = false
362
+ }) => {
363
+ const { formatMessage } = reactIntl.useIntl();
364
+ const { pathname } = reactRouterDom.useLocation();
365
+ const isCreatingRelease = pathname === `/plugins/${index.pluginId}`;
366
+ const { timezoneList, systemTimezone = { value: "UTC+00:00-Africa/Abidjan " } } = index.getTimezones(
367
+ initialValues.scheduledAt ? new Date(initialValues.scheduledAt) : /* @__PURE__ */ new Date()
368
+ );
369
+ const getScheduledTimestamp = (values) => {
370
+ const { date, time, timezone } = values;
371
+ if (!date || !time || !timezone) return null;
372
+ const timezoneWithoutOffset = timezone.split("&")[1];
373
+ return dateFnsTz.zonedTimeToUtc(`${date} ${time}`, timezoneWithoutOffset);
374
+ };
375
+ const getTimezoneWithOffset = () => {
376
+ const currentTimezone = timezoneList.find(
377
+ (timezone) => timezone.value.split("&")[1] === initialValues.timezone
378
+ );
379
+ return currentTimezone?.value || systemTimezone.value;
380
+ };
381
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
382
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: formatMessage(
383
+ {
384
+ id: "content-releases.modal.title",
385
+ defaultMessage: "{isCreatingRelease, select, true {New release} other {Edit release}}"
386
+ },
387
+ { isCreatingRelease }
388
+ ) }) }),
389
+ /* @__PURE__ */ jsxRuntime.jsx(
390
+ formik.Formik,
391
+ {
392
+ onSubmit: (values) => {
393
+ handleSubmit({
394
+ ...values,
395
+ timezone: values.timezone ? values.timezone.split("&")[1] : null,
396
+ scheduledAt: values.isScheduled ? getScheduledTimestamp(values) : null
397
+ });
398
+ },
399
+ initialValues: {
400
+ ...initialValues,
401
+ timezone: initialValues.timezone ? getTimezoneWithOffset() : systemTimezone.value
402
+ },
403
+ validationSchema: schemas.RELEASE_SCHEMA,
404
+ validateOnChange: false,
405
+ children: ({ values, errors, handleChange, setFieldValue }) => {
406
+ return /* @__PURE__ */ jsxRuntime.jsxs(formik.Form, { children: [
407
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
408
+ /* @__PURE__ */ jsxRuntime.jsxs(
409
+ designSystem.Field.Root,
410
+ {
411
+ name: "name",
412
+ error: errors.name && formatMessage({ id: errors.name, defaultMessage: errors.name }),
413
+ required: true,
414
+ children: [
415
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
416
+ id: "content-releases.modal.form.input.label.release-name",
417
+ defaultMessage: "Name"
418
+ }) }),
419
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.TextInput, { value: values.name, onChange: handleChange }),
420
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
421
+ ]
422
+ }
423
+ ),
424
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "max-content", children: /* @__PURE__ */ jsxRuntime.jsx(
425
+ designSystem.Checkbox,
426
+ {
427
+ name: "isScheduled",
428
+ checked: values.isScheduled,
429
+ onCheckedChange: (checked) => {
430
+ setFieldValue("isScheduled", checked);
431
+ if (!checked) {
432
+ setFieldValue("date", null);
433
+ setFieldValue("time", "");
434
+ setFieldValue("timezone", null);
435
+ } else {
436
+ setFieldValue("date", initialValues.date);
437
+ setFieldValue("time", initialValues.time);
438
+ setFieldValue(
439
+ "timezone",
440
+ initialValues.timezone ?? systemTimezone?.value
441
+ );
442
+ }
443
+ },
444
+ children: /* @__PURE__ */ jsxRuntime.jsx(
445
+ designSystem.Typography,
446
+ {
447
+ textColor: values.isScheduled ? "primary600" : "neutral800",
448
+ fontWeight: values.isScheduled ? "semiBold" : "regular",
449
+ children: formatMessage({
450
+ id: "modal.form.input.label.schedule-release",
451
+ defaultMessage: "Schedule release"
452
+ })
453
+ }
454
+ )
455
+ }
456
+ ) }),
457
+ values.isScheduled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
458
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "start", children: [
459
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
460
+ designSystem.Field.Root,
461
+ {
462
+ name: "date",
463
+ error: errors.date && formatMessage({ id: errors.date, defaultMessage: errors.date }),
464
+ required: true,
465
+ children: [
466
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
467
+ id: "content-releases.modal.form.input.label.date",
468
+ defaultMessage: "Date"
469
+ }) }),
470
+ /* @__PURE__ */ jsxRuntime.jsx(
471
+ designSystem.DatePicker,
472
+ {
473
+ onChange: (date) => {
474
+ const isoFormatDate = date ? dateFns.formatISO(date, { representation: "date" }) : null;
475
+ setFieldValue("date", isoFormatDate);
476
+ },
477
+ clearLabel: formatMessage({
478
+ id: "content-releases.modal.form.input.clearLabel",
479
+ defaultMessage: "Clear"
480
+ }),
481
+ onClear: () => {
482
+ setFieldValue("date", null);
483
+ },
484
+ value: values.date ? new Date(values.date) : /* @__PURE__ */ new Date(),
485
+ minDate: dateFnsTz.utcToZonedTime(/* @__PURE__ */ new Date(), values.timezone.split("&")[1])
486
+ }
487
+ ),
488
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
489
+ ]
490
+ }
491
+ ) }),
492
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
493
+ designSystem.Field.Root,
494
+ {
495
+ name: "time",
496
+ error: errors.time && formatMessage({ id: errors.time, defaultMessage: errors.time }),
497
+ required: true,
498
+ children: [
499
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
500
+ id: "content-releases.modal.form.input.label.time",
501
+ defaultMessage: "Time"
502
+ }) }),
503
+ /* @__PURE__ */ jsxRuntime.jsx(
504
+ designSystem.TimePicker,
505
+ {
506
+ onChange: (time) => {
507
+ setFieldValue("time", time);
508
+ },
509
+ clearLabel: formatMessage({
510
+ id: "content-releases.modal.form.input.clearLabel",
511
+ defaultMessage: "Clear"
512
+ }),
513
+ onClear: () => {
514
+ setFieldValue("time", "");
515
+ },
516
+ value: values.time || void 0
517
+ }
518
+ ),
519
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
520
+ ]
521
+ }
522
+ ) })
523
+ ] }),
524
+ /* @__PURE__ */ jsxRuntime.jsx(TimezoneComponent, { timezoneOptions: timezoneList })
525
+ ] })
526
+ ] }) }),
527
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
528
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Close, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", name: "cancel", children: formatMessage({ id: "cancel", defaultMessage: "Cancel" }) }) }),
529
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { name: "submit", loading: isLoading, type: "submit", children: formatMessage(
530
+ {
531
+ id: "content-releases.modal.form.button.submit",
532
+ defaultMessage: "{isCreatingRelease, select, true {Continue} other {Save}}"
533
+ },
534
+ { isCreatingRelease }
535
+ ) })
536
+ ] })
537
+ ] });
538
+ }
539
+ }
540
+ )
541
+ ] }) });
542
+ };
543
+ const TimezoneComponent = ({ timezoneOptions }) => {
544
+ const { values, errors, setFieldValue } = formik.useFormikContext();
545
+ const { formatMessage } = reactIntl.useIntl();
546
+ const [timezoneList, setTimezoneList] = React__namespace.useState(timezoneOptions);
547
+ React__namespace.useEffect(() => {
548
+ if (values.date) {
549
+ const { timezoneList: timezoneList2 } = index.getTimezones(new Date(values.date));
550
+ setTimezoneList(timezoneList2);
551
+ const updatedTimezone = values.timezone && timezoneList2.find((tz) => tz.value.split("&")[1] === values.timezone.split("&")[1]);
552
+ if (updatedTimezone) {
553
+ setFieldValue("timezone", updatedTimezone.value);
554
+ }
555
+ }
556
+ }, [setFieldValue, values.date, values.timezone]);
557
+ return /* @__PURE__ */ jsxRuntime.jsxs(
558
+ designSystem.Field.Root,
559
+ {
560
+ name: "timezone",
561
+ error: errors.timezone && formatMessage({ id: errors.timezone, defaultMessage: errors.timezone }),
562
+ required: true,
563
+ children: [
564
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
565
+ id: "content-releases.modal.form.input.label.timezone",
566
+ defaultMessage: "Timezone"
567
+ }) }),
568
+ /* @__PURE__ */ jsxRuntime.jsx(
569
+ designSystem.Combobox,
570
+ {
571
+ autocomplete: { type: "list", filter: "contains" },
572
+ value: values.timezone || void 0,
573
+ textValue: values.timezone ? values.timezone.replace(/&/, " ") : void 0,
574
+ onChange: (timezone) => {
575
+ setFieldValue("timezone", timezone);
576
+ },
577
+ onTextValueChange: (timezone) => {
578
+ setFieldValue("timezone", timezone);
579
+ },
580
+ onClear: () => {
581
+ setFieldValue("timezone", "");
582
+ },
583
+ children: timezoneList.map((timezone) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.ComboboxOption, { value: timezone.value, children: timezone.value.replace(/&/, " ") }, timezone.value))
584
+ }
585
+ ),
586
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
587
+ ]
588
+ }
589
+ );
590
+ };
591
+ const useTypedDispatch = reactRedux.useDispatch;
592
+ const isBaseQueryError = (error) => {
593
+ return typeof error !== "undefined" && error.name !== void 0;
594
+ };
595
+ const LinkCard = styledComponents.styled(designSystem.Link)`
596
+ display: block;
597
+ `;
598
+ const RelativeTime = styledComponents.styled(RelativeTime$1)`
599
+ display: inline-block;
600
+ &::first-letter {
601
+ text-transform: uppercase;
602
+ }
603
+ `;
604
+ const getBadgeProps = (status) => {
605
+ let color;
606
+ switch (status) {
607
+ case "ready":
608
+ color = "success";
609
+ break;
610
+ case "blocked":
611
+ color = "warning";
612
+ break;
613
+ case "failed":
614
+ color = "danger";
615
+ break;
616
+ case "done":
617
+ color = "primary";
618
+ break;
619
+ case "empty":
620
+ default:
621
+ color = "neutral";
622
+ }
623
+ return {
624
+ textColor: `${color}600`,
625
+ backgroundColor: `${color}100`,
626
+ borderColor: `${color}200`
627
+ };
628
+ };
629
+ const ReleasesGrid = ({ sectionTitle, releases = [], isError = false }) => {
630
+ const { formatMessage } = reactIntl.useIntl();
631
+ if (isError) {
632
+ return /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Page.Error, {});
633
+ }
634
+ if (releases?.length === 0) {
635
+ return /* @__PURE__ */ jsxRuntime.jsx(
636
+ designSystem.EmptyStateLayout,
637
+ {
638
+ content: formatMessage(
639
+ {
640
+ id: "content-releases.page.Releases.tab.emptyEntries",
641
+ defaultMessage: "No releases"
642
+ },
643
+ {
644
+ target: sectionTitle
645
+ }
646
+ ),
647
+ icon: /* @__PURE__ */ jsxRuntime.jsx(symbols.EmptyDocuments, { width: "16rem" })
648
+ }
649
+ );
650
+ }
651
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Root, { gap: 4, children: releases.map(({ id, name, scheduledAt, status }) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 3, s: 6, xs: 12, direction: "column", alignItems: "stretch", children: /* @__PURE__ */ jsxRuntime.jsx(LinkCard, { tag: reactRouterDom.NavLink, to: `${id}`, isExternal: false, children: /* @__PURE__ */ jsxRuntime.jsxs(
652
+ designSystem.Flex,
653
+ {
654
+ direction: "column",
655
+ justifyContent: "space-between",
656
+ padding: 4,
657
+ hasRadius: true,
658
+ background: "neutral0",
659
+ shadow: "tableShadow",
660
+ height: "100%",
661
+ width: "100%",
662
+ alignItems: "start",
663
+ gap: 4,
664
+ children: [
665
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "start", gap: 1, children: [
666
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", tag: "h3", variant: "delta", fontWeight: "bold", children: name }),
667
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: scheduledAt ? /* @__PURE__ */ jsxRuntime.jsx(RelativeTime, { timestamp: new Date(scheduledAt) }) : formatMessage({
668
+ id: "content-releases.pages.Releases.not-scheduled",
669
+ defaultMessage: "Not scheduled"
670
+ }) })
671
+ ] }),
672
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { ...getBadgeProps(status), children: status })
673
+ ]
674
+ }
675
+ ) }) }, id)) });
676
+ };
677
+ const StyledAlert = styledComponents.styled(designSystem.Alert)`
678
+ button {
679
+ display: none;
680
+ }
681
+ p + div {
682
+ margin-left: auto;
683
+ }
684
+ `;
685
+ const INITIAL_FORM_VALUES = {
686
+ name: "",
687
+ date: dateFns.format(/* @__PURE__ */ new Date(), "yyyy-MM-dd"),
688
+ time: "",
689
+ isScheduled: true,
690
+ scheduledAt: null,
691
+ timezone: null
692
+ };
693
+ const ReleasesPage = () => {
694
+ const location = reactRouterDom.useLocation();
695
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
696
+ const { toggleNotification } = strapiAdmin$1.useNotification();
697
+ const { formatMessage } = reactIntl.useIntl();
698
+ const navigate = reactRouterDom.useNavigate();
699
+ const { formatAPIError } = strapiAdmin$1.useAPIErrorHandler();
700
+ const [{ query }, setQuery] = strapiAdmin$1.useQueryParams();
701
+ const response = index.useGetReleasesQuery(query);
702
+ const { data, isLoading: isLoadingSettings } = index.useGetReleaseSettingsQuery();
703
+ const [createRelease, { isLoading: isSubmittingForm }] = index.useCreateReleaseMutation();
704
+ const { getFeature } = ee.useLicenseLimits();
705
+ const { maximumReleases = 3 } = getFeature("cms-content-releases");
706
+ const { trackUsage } = strapiAdmin$1.useTracking();
707
+ const {
708
+ allowedActions: { canCreate }
709
+ } = strapiAdmin$1.useRBAC(index.PERMISSIONS);
710
+ const { isLoading: isLoadingReleases, isSuccess, isError } = response;
711
+ const activeTab = response?.currentData?.meta?.activeTab || "pending";
712
+ React__namespace.useEffect(() => {
713
+ if (location?.state?.errors) {
714
+ toggleNotification({
715
+ type: "danger",
716
+ title: formatMessage({
717
+ id: "content-releases.pages.Releases.notification.error.title",
718
+ defaultMessage: "Your request could not be processed."
719
+ }),
720
+ message: formatMessage({
721
+ id: "content-releases.pages.Releases.notification.error.message",
722
+ defaultMessage: "Please try again or open another release."
723
+ })
724
+ });
725
+ navigate("", { replace: true, state: null });
726
+ }
727
+ }, [formatMessage, location?.state?.errors, navigate, toggleNotification]);
728
+ const toggleAddReleaseModal = () => {
729
+ setReleaseModalShown((prev) => !prev);
730
+ };
731
+ if (isLoadingReleases || isLoadingSettings) {
732
+ return /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Page.Loading, {});
733
+ }
734
+ const totalPendingReleases = isSuccess && response.currentData?.meta?.pendingReleasesCount || 0;
735
+ const hasReachedMaximumPendingReleases = totalPendingReleases >= maximumReleases;
736
+ const handleTabChange = (tabValue) => {
737
+ setQuery({
738
+ ...query,
739
+ page: 1,
740
+ pageSize: response?.currentData?.meta?.pagination?.pageSize || 16,
741
+ filters: {
742
+ releasedAt: {
743
+ $notNull: tabValue !== "pending"
744
+ }
745
+ }
746
+ });
747
+ };
748
+ const handleAddRelease = async ({ name, scheduledAt, timezone }) => {
749
+ const response2 = await createRelease({
750
+ name,
751
+ scheduledAt,
752
+ timezone
753
+ });
754
+ if ("data" in response2) {
755
+ toggleNotification({
756
+ type: "success",
757
+ message: formatMessage({
758
+ id: "content-releases.modal.release-created-notification-success",
759
+ defaultMessage: "Release created."
760
+ })
761
+ });
762
+ trackUsage("didCreateRelease");
763
+ navigate(response2.data.data.id.toString());
764
+ } else if (strapiAdmin$1.isFetchError(response2.error)) {
765
+ toggleNotification({
766
+ type: "danger",
767
+ message: formatAPIError(response2.error)
768
+ });
769
+ } else {
770
+ toggleNotification({
771
+ type: "danger",
772
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
773
+ });
774
+ }
775
+ };
776
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoadingReleases || isLoadingSettings, children: [
777
+ /* @__PURE__ */ jsxRuntime.jsx(
778
+ strapiAdmin$1.Layouts.Header,
779
+ {
780
+ title: formatMessage({
781
+ id: "content-releases.pages.Releases.title",
782
+ defaultMessage: "Releases"
783
+ }),
784
+ subtitle: formatMessage({
785
+ id: "content-releases.pages.Releases.header-subtitle",
786
+ defaultMessage: "Create and manage content updates"
787
+ }),
788
+ primaryAction: canCreate ? /* @__PURE__ */ jsxRuntime.jsx(
789
+ designSystem.Button,
790
+ {
791
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
792
+ onClick: toggleAddReleaseModal,
793
+ disabled: hasReachedMaximumPendingReleases,
794
+ children: formatMessage({
795
+ id: "content-releases.header.actions.add-release",
796
+ defaultMessage: "New release"
797
+ })
798
+ }
799
+ ) : null
800
+ }
801
+ ),
802
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Layouts.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
803
+ hasReachedMaximumPendingReleases && /* @__PURE__ */ jsxRuntime.jsx(
804
+ StyledAlert,
805
+ {
806
+ marginBottom: 6,
807
+ action: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { href: "https://strapi.io/pricing-cloud", isExternal: true, children: formatMessage({
808
+ id: "content-releases.pages.Releases.max-limit-reached.action",
809
+ defaultMessage: "Explore plans"
810
+ }) }),
811
+ title: formatMessage(
812
+ {
813
+ id: "content-releases.pages.Releases.max-limit-reached.title",
814
+ defaultMessage: "You have reached the {number} pending {number, plural, one {release} other {releases}} limit."
815
+ },
816
+ { number: maximumReleases }
817
+ ),
818
+ onClose: () => {
819
+ },
820
+ closeLabel: "",
821
+ children: formatMessage({
822
+ id: "content-releases.pages.Releases.max-limit-reached.message",
823
+ defaultMessage: "Upgrade to manage an unlimited number of releases."
824
+ })
825
+ }
826
+ ),
827
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.Root, { variant: "simple", onValueChange: handleTabChange, value: activeTab, children: [
828
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingBottom: 8, children: [
829
+ /* @__PURE__ */ jsxRuntime.jsxs(
830
+ designSystem.Tabs.List,
831
+ {
832
+ "aria-label": formatMessage({
833
+ id: "content-releases.pages.Releases.tab-group.label",
834
+ defaultMessage: "Releases list"
835
+ }),
836
+ children: [
837
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "pending", children: formatMessage(
838
+ {
839
+ id: "content-releases.pages.Releases.tab.pending",
840
+ defaultMessage: "Pending ({count})"
841
+ },
842
+ {
843
+ count: totalPendingReleases
844
+ }
845
+ ) }),
846
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "done", children: formatMessage({
847
+ id: "content-releases.pages.Releases.tab.done",
848
+ defaultMessage: "Done"
849
+ }) })
850
+ ]
851
+ }
852
+ ),
853
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {})
854
+ ] }),
855
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "pending", children: /* @__PURE__ */ jsxRuntime.jsx(
856
+ ReleasesGrid,
857
+ {
858
+ sectionTitle: "pending",
859
+ releases: response?.currentData?.data,
860
+ isError
861
+ }
862
+ ) }),
863
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "done", children: /* @__PURE__ */ jsxRuntime.jsx(
864
+ ReleasesGrid,
865
+ {
866
+ sectionTitle: "done",
867
+ releases: response?.currentData?.data,
868
+ isError
869
+ }
870
+ ) })
871
+ ] }),
872
+ /* @__PURE__ */ jsxRuntime.jsxs(
873
+ strapiAdmin$1.Pagination.Root,
874
+ {
875
+ ...response?.currentData?.meta?.pagination,
876
+ defaultPageSize: response?.currentData?.meta?.pagination?.pageSize,
877
+ children: [
878
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Pagination.PageSize, { options: ["8", "16", "32", "64"] }),
879
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Pagination.Links, {})
880
+ ]
881
+ }
882
+ )
883
+ ] }) }),
884
+ /* @__PURE__ */ jsxRuntime.jsx(
885
+ ReleaseModal,
886
+ {
887
+ open: releaseModalShown,
888
+ handleClose: toggleAddReleaseModal,
889
+ handleSubmit: handleAddRelease,
890
+ isLoading: isSubmittingForm,
891
+ initialValues: {
892
+ ...INITIAL_FORM_VALUES,
893
+ timezone: data?.data.defaultTimezone ? data.data.defaultTimezone.split("&")[1] : null
894
+ }
895
+ }
896
+ )
897
+ ] });
898
+ };
899
+ const ReleaseInfoWrapper = styledComponents.styled(designSystem.Flex)`
900
+ align-self: stretch;
901
+ border-bottom-right-radius: ${({ theme }) => theme.borderRadius};
902
+ border-bottom-left-radius: ${({ theme }) => theme.borderRadius};
903
+ border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
904
+ `;
905
+ const StyledMenuItem = styledComponents.styled(designSystem.MenuItem)`
906
+ svg path {
907
+ fill: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
908
+ }
909
+ span {
910
+ color: ${({ theme, disabled }) => disabled && theme.colors.neutral500};
911
+ }
912
+
913
+ &:hover {
914
+ background: ${({ theme, $variant = "neutral" }) => theme.colors[`${$variant}100`]};
915
+ }
916
+ `;
917
+ const PencilIcon = styledComponents.styled(icons.Pencil)`
918
+ width: ${({ theme }) => theme.spaces[4]};
919
+ height: ${({ theme }) => theme.spaces[4]};
920
+ path {
921
+ fill: ${({ theme }) => theme.colors.neutral600};
922
+ }
923
+ `;
924
+ const TrashIcon = styledComponents.styled(icons.Trash)`
925
+ width: ${({ theme }) => theme.spaces[4]};
926
+ height: ${({ theme }) => theme.spaces[4]};
927
+ path {
928
+ fill: ${({ theme }) => theme.colors.danger600};
929
+ }
930
+ `;
931
+ const ReleaseDetailsLayout = ({
932
+ toggleEditReleaseModal,
933
+ toggleWarningSubmit,
934
+ children
935
+ }) => {
936
+ const { formatMessage, formatDate, formatTime } = reactIntl.useIntl();
937
+ const { releaseId } = reactRouterDom.useParams();
938
+ const {
939
+ data,
940
+ isLoading: isLoadingDetails,
941
+ error
942
+ } = index.useGetReleaseQuery(
943
+ { id: releaseId },
944
+ {
945
+ skip: !releaseId
946
+ }
947
+ );
948
+ const [publishRelease, { isLoading: isPublishing }] = index.usePublishReleaseMutation();
949
+ const { toggleNotification } = strapiAdmin$1.useNotification();
950
+ const { formatAPIError } = strapiAdmin$1.useAPIErrorHandler();
951
+ const { allowedActions } = strapiAdmin$1.useRBAC(index.PERMISSIONS);
952
+ const { canUpdate, canDelete, canPublish } = allowedActions;
953
+ const dispatch = useTypedDispatch();
954
+ const { trackUsage } = strapiAdmin$1.useTracking();
955
+ const release = data?.data;
956
+ const handlePublishRelease = (id) => async () => {
957
+ const response = await publishRelease({ id });
958
+ if ("data" in response) {
959
+ toggleNotification({
960
+ type: "success",
961
+ message: formatMessage({
962
+ id: "content-releases.pages.ReleaseDetails.publish-notification-success",
963
+ defaultMessage: "Release was published successfully."
964
+ })
965
+ });
966
+ const { totalEntries: totalEntries2, totalPublishedEntries, totalUnpublishedEntries } = response.data.meta;
967
+ trackUsage("didPublishRelease", {
968
+ totalEntries: totalEntries2,
969
+ totalPublishedEntries,
970
+ totalUnpublishedEntries
971
+ });
972
+ } else if (strapiAdmin$1.isFetchError(response.error)) {
973
+ toggleNotification({
974
+ type: "danger",
975
+ message: formatAPIError(response.error)
976
+ });
977
+ } else {
978
+ toggleNotification({
979
+ type: "danger",
980
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
981
+ });
982
+ }
983
+ };
984
+ const handleRefresh = () => {
985
+ dispatch(
986
+ index.releaseApi.util.invalidateTags([
987
+ { type: "ReleaseAction", id: "LIST" },
988
+ { type: "Release", id: releaseId }
989
+ ])
990
+ );
991
+ };
992
+ const getCreatedByUser = () => {
993
+ if (!release?.createdBy) {
994
+ return null;
995
+ }
996
+ if (release.createdBy.username) {
997
+ return release.createdBy.username;
998
+ }
999
+ if (release.createdBy.firstname) {
1000
+ return `${release.createdBy.firstname} ${release.createdBy.lastname || ""}`.trim();
1001
+ }
1002
+ return release.createdBy.email;
1003
+ };
1004
+ if (isLoadingDetails) {
1005
+ return /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Page.Loading, {});
1006
+ }
1007
+ if (isBaseQueryError(error) && "code" in error || !release) {
1008
+ return /* @__PURE__ */ jsxRuntime.jsx(
1009
+ reactRouterDom.Navigate,
1010
+ {
1011
+ to: "..",
1012
+ state: {
1013
+ errors: [
1014
+ {
1015
+ // @ts-expect-error – TODO: fix this weird error flow
1016
+ code: error?.code
1017
+ }
1018
+ ]
1019
+ }
1020
+ }
1021
+ );
1022
+ }
1023
+ const totalEntries = release.actions.meta.count || 0;
1024
+ const hasCreatedByUser = Boolean(getCreatedByUser());
1025
+ const isScheduled = release.scheduledAt && release.timezone;
1026
+ const numberOfEntriesText = formatMessage(
1027
+ {
1028
+ id: "content-releases.pages.Details.header-subtitle",
1029
+ defaultMessage: "{number, plural, =0 {No entries} one {# entry} other {# entries}}"
1030
+ },
1031
+ { number: totalEntries }
1032
+ );
1033
+ const scheduledText = isScheduled ? formatMessage(
1034
+ {
1035
+ id: "content-releases.pages.ReleaseDetails.header-subtitle.scheduled",
1036
+ defaultMessage: "Scheduled for {date} at {time} ({offset})"
1037
+ },
1038
+ {
1039
+ date: formatDate(new Date(release.scheduledAt), {
1040
+ weekday: "long",
1041
+ day: "numeric",
1042
+ month: "long",
1043
+ year: "numeric",
1044
+ timeZone: release.timezone
1045
+ }),
1046
+ time: formatTime(new Date(release.scheduledAt), {
1047
+ timeZone: release.timezone,
1048
+ hourCycle: "h23"
1049
+ }),
1050
+ offset: index.getTimezoneOffset(release.timezone, new Date(release.scheduledAt))
1051
+ }
1052
+ ) : "";
1053
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "aria-busy": isLoadingDetails, children: [
1054
+ /* @__PURE__ */ jsxRuntime.jsx(
1055
+ strapiAdmin$1.Layouts.Header,
1056
+ {
1057
+ title: release.name,
1058
+ subtitle: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, lineHeight: 6, children: [
1059
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", variant: "epsilon", children: numberOfEntriesText + (isScheduled ? ` - ${scheduledText}` : "") }),
1060
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { ...getBadgeProps(release.status), children: release.status })
1061
+ ] }),
1062
+ navigationAction: /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.BackButton, { fallback: ".." }),
1063
+ primaryAction: !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1064
+ /* @__PURE__ */ jsxRuntime.jsxs(
1065
+ SimpleMenuButton,
1066
+ {
1067
+ label: /* @__PURE__ */ jsxRuntime.jsx(icons.More, {}),
1068
+ variant: "tertiary",
1069
+ endIcon: null,
1070
+ paddingLeft: "7px",
1071
+ paddingRight: "7px",
1072
+ "aria-label": formatMessage({
1073
+ id: "content-releases.header.actions.open-release-actions",
1074
+ defaultMessage: "Release edit and delete menu"
1075
+ }),
1076
+ popoverPlacement: "bottom-end",
1077
+ children: [
1078
+ /* @__PURE__ */ jsxRuntime.jsx(StyledMenuItem, { disabled: !canUpdate, onSelect: toggleEditReleaseModal, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
1079
+ /* @__PURE__ */ jsxRuntime.jsx(PencilIcon, {}),
1080
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: formatMessage({
1081
+ id: "content-releases.header.actions.edit",
1082
+ defaultMessage: "Edit"
1083
+ }) })
1084
+ ] }) }),
1085
+ /* @__PURE__ */ jsxRuntime.jsx(
1086
+ StyledMenuItem,
1087
+ {
1088
+ disabled: !canDelete,
1089
+ onSelect: toggleWarningSubmit,
1090
+ $variant: "danger",
1091
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, hasRadius: true, width: "100%", children: [
1092
+ /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}),
1093
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, textColor: "danger600", children: formatMessage({
1094
+ id: "content-releases.header.actions.delete",
1095
+ defaultMessage: "Delete"
1096
+ }) })
1097
+ ] })
1098
+ }
1099
+ ),
1100
+ /* @__PURE__ */ jsxRuntime.jsxs(
1101
+ ReleaseInfoWrapper,
1102
+ {
1103
+ direction: "column",
1104
+ justifyContent: "center",
1105
+ alignItems: "flex-start",
1106
+ gap: 1,
1107
+ padding: 4,
1108
+ children: [
1109
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: formatMessage({
1110
+ id: "content-releases.header.actions.created",
1111
+ defaultMessage: "Created"
1112
+ }) }),
1113
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", color: "neutral300", children: [
1114
+ /* @__PURE__ */ jsxRuntime.jsx(RelativeTime$1, { timestamp: new Date(release.createdAt) }),
1115
+ formatMessage(
1116
+ {
1117
+ id: "content-releases.header.actions.created.description",
1118
+ defaultMessage: "{hasCreatedByUser, select, true { by {createdBy}} other { by deleted user}}"
1119
+ },
1120
+ { createdBy: getCreatedByUser(), hasCreatedByUser }
1121
+ )
1122
+ ] })
1123
+ ]
1124
+ }
1125
+ )
1126
+ ]
1127
+ }
1128
+ ),
1129
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "S", variant: "tertiary", onClick: handleRefresh, children: formatMessage({
1130
+ id: "content-releases.header.actions.refresh",
1131
+ defaultMessage: "Refresh"
1132
+ }) }),
1133
+ canPublish ? /* @__PURE__ */ jsxRuntime.jsx(
1134
+ designSystem.Button,
1135
+ {
1136
+ size: "S",
1137
+ variant: "default",
1138
+ onClick: handlePublishRelease(release.id.toString()),
1139
+ loading: isPublishing,
1140
+ disabled: release.actions.meta.count === 0,
1141
+ children: formatMessage({
1142
+ id: "content-releases.header.actions.publish",
1143
+ defaultMessage: "Publish"
1144
+ })
1145
+ }
1146
+ ) : null
1147
+ ] })
1148
+ }
1149
+ ),
1150
+ children
1151
+ ] });
1152
+ };
1153
+ const SimpleMenuButton = styledComponents.styled(designSystem.SimpleMenu)`
1154
+ & > span {
1155
+ display: flex;
1156
+ }
1157
+ `;
1158
+ const GROUP_BY_OPTIONS = ["contentType", "locale", "action"];
1159
+ const GROUP_BY_OPTIONS_NO_LOCALE = ["contentType", "action"];
1160
+ const getGroupByOptionLabel = (value) => {
1161
+ if (value === "locale") {
1162
+ return {
1163
+ id: "content-releases.pages.ReleaseDetails.groupBy.option.locales",
1164
+ defaultMessage: "Locales"
1165
+ };
1166
+ }
1167
+ if (value === "action") {
1168
+ return {
1169
+ id: "content-releases.pages.ReleaseDetails.groupBy.option.actions",
1170
+ defaultMessage: "Actions"
1171
+ };
1172
+ }
1173
+ return {
1174
+ id: "content-releases.pages.ReleaseDetails.groupBy.option.content-type",
1175
+ defaultMessage: "Content-Types"
1176
+ };
1177
+ };
1178
+ const ReleaseDetailsBody = ({ releaseId }) => {
1179
+ const { formatMessage } = reactIntl.useIntl();
1180
+ const [{ query }, setQuery] = strapiAdmin$1.useQueryParams();
1181
+ const { toggleNotification } = strapiAdmin$1.useNotification();
1182
+ const { formatAPIError } = strapiAdmin$1.useAPIErrorHandler();
1183
+ const {
1184
+ data: releaseData,
1185
+ isLoading: isReleaseLoading,
1186
+ error: releaseError
1187
+ } = index.useGetReleaseQuery({ id: releaseId });
1188
+ const {
1189
+ allowedActions: { canUpdate }
1190
+ } = strapiAdmin$1.useRBAC(index.PERMISSIONS);
1191
+ const runHookWaterfall = strapiAdmin$1.useStrapiApp("ReleaseDetailsPage", (state) => state.runHookWaterfall);
1192
+ const { displayedHeaders, hasI18nEnabled } = runHookWaterfall("ContentReleases/pages/ReleaseDetails/add-locale-in-releases", {
1193
+ displayedHeaders: [
1194
+ {
1195
+ label: {
1196
+ id: "content-releases.page.ReleaseDetails.table.header.label.name",
1197
+ defaultMessage: "name"
1198
+ },
1199
+ name: "name"
1200
+ }
1201
+ ],
1202
+ hasI18nEnabled: false
1203
+ });
1204
+ const release = releaseData?.data;
1205
+ const selectedGroupBy = query?.groupBy || "contentType";
1206
+ const {
1207
+ isLoading,
1208
+ isFetching,
1209
+ isError,
1210
+ data,
1211
+ error: releaseActionsError
1212
+ } = index.useGetReleaseActionsQuery({
1213
+ ...query,
1214
+ releaseId
1215
+ });
1216
+ const [updateReleaseAction] = index.useUpdateReleaseActionMutation();
1217
+ const handleChangeType = async (e, actionId, actionPath) => {
1218
+ const response = await updateReleaseAction({
1219
+ params: {
1220
+ releaseId,
1221
+ actionId
1222
+ },
1223
+ body: {
1224
+ type: e.target.value
1225
+ },
1226
+ query,
1227
+ // We are passing the query params to make optimistic updates
1228
+ actionPath
1229
+ // We are passing the action path to found the position in the cache of the action for optimistic updates
1230
+ });
1231
+ if ("error" in response) {
1232
+ if (strapiAdmin$1.isFetchError(response.error)) {
1233
+ toggleNotification({
1234
+ type: "danger",
1235
+ message: formatAPIError(response.error)
1236
+ });
1237
+ } else {
1238
+ toggleNotification({
1239
+ type: "danger",
1240
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1241
+ });
1242
+ }
1243
+ }
1244
+ };
1245
+ if (isLoading || isReleaseLoading) {
1246
+ return /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Page.Loading, {});
1247
+ }
1248
+ const releaseActions = data?.data;
1249
+ const releaseMeta = data?.meta;
1250
+ const contentTypes = releaseMeta?.contentTypes || {};
1251
+ releaseMeta?.components || {};
1252
+ if (isBaseQueryError(releaseError) || !release) {
1253
+ const errorsArray = [];
1254
+ if (releaseError && "code" in releaseError) {
1255
+ errorsArray.push({
1256
+ code: releaseError.code
1257
+ });
1258
+ }
1259
+ if (releaseActionsError && "code" in releaseActionsError) {
1260
+ errorsArray.push({
1261
+ code: releaseActionsError.code
1262
+ });
1263
+ }
1264
+ return /* @__PURE__ */ jsxRuntime.jsx(
1265
+ reactRouterDom.Navigate,
1266
+ {
1267
+ to: "..",
1268
+ state: {
1269
+ errors: errorsArray
1270
+ }
1271
+ }
1272
+ );
1273
+ }
1274
+ if (isError || !releaseActions) {
1275
+ return /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Page.Error, {});
1276
+ }
1277
+ if (Object.keys(releaseActions).length === 0) {
1278
+ return /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Layouts.Content, { children: /* @__PURE__ */ jsxRuntime.jsx(
1279
+ designSystem.EmptyStateLayout,
1280
+ {
1281
+ action: /* @__PURE__ */ jsxRuntime.jsx(
1282
+ designSystem.LinkButton,
1283
+ {
1284
+ tag: reactRouterDom.Link,
1285
+ to: {
1286
+ pathname: "/content-manager"
1287
+ },
1288
+ style: { textDecoration: "none" },
1289
+ variant: "secondary",
1290
+ children: formatMessage({
1291
+ id: "content-releases.page.Details.button.openContentManager",
1292
+ defaultMessage: "Open the Content Manager"
1293
+ })
1294
+ }
1295
+ ),
1296
+ icon: /* @__PURE__ */ jsxRuntime.jsx(symbols.EmptyDocuments, { width: "16rem" }),
1297
+ content: formatMessage({
1298
+ id: "content-releases.pages.Details.tab.emptyEntries",
1299
+ defaultMessage: "This release is empty. Open the Content Manager, select an entry and add it to the release."
1300
+ })
1301
+ }
1302
+ ) });
1303
+ }
1304
+ const groupByLabel = formatMessage({
1305
+ id: "content-releases.pages.ReleaseDetails.groupBy.aria-label",
1306
+ defaultMessage: "Group by"
1307
+ });
1308
+ const headers = [
1309
+ ...displayedHeaders,
1310
+ {
1311
+ label: {
1312
+ id: "content-releases.page.ReleaseDetails.table.header.label.content-type",
1313
+ defaultMessage: "content-type"
1314
+ },
1315
+ name: "content-type"
1316
+ },
1317
+ {
1318
+ label: {
1319
+ id: "content-releases.page.ReleaseDetails.table.header.label.action",
1320
+ defaultMessage: "action"
1321
+ },
1322
+ name: "action"
1323
+ },
1324
+ ...!release.releasedAt ? [
1325
+ {
1326
+ label: {
1327
+ id: "content-releases.page.ReleaseDetails.table.header.label.status",
1328
+ defaultMessage: "status"
1329
+ },
1330
+ name: "status"
1331
+ }
1332
+ ] : []
1333
+ ];
1334
+ const options = hasI18nEnabled ? GROUP_BY_OPTIONS : GROUP_BY_OPTIONS_NO_LOCALE;
1335
+ return /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Layouts.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 8, direction: "column", alignItems: "stretch", children: [
1336
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { children: /* @__PURE__ */ jsxRuntime.jsx(
1337
+ designSystem.SingleSelect,
1338
+ {
1339
+ placeholder: groupByLabel,
1340
+ "aria-label": groupByLabel,
1341
+ customizeContent: (value) => formatMessage(
1342
+ {
1343
+ id: `content-releases.pages.ReleaseDetails.groupBy.label`,
1344
+ defaultMessage: `Group by {groupBy}`
1345
+ },
1346
+ {
1347
+ groupBy: value
1348
+ }
1349
+ ),
1350
+ value: formatMessage(getGroupByOptionLabel(selectedGroupBy)),
1351
+ onChange: (value) => setQuery({ groupBy: value }),
1352
+ children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: option, children: formatMessage(getGroupByOptionLabel(option)) }, option))
1353
+ }
1354
+ ) }),
1355
+ Object.keys(releaseActions).map((key) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, direction: "column", alignItems: "stretch", children: [
1356
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { role: "separator", "aria-label": key, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: key }) }),
1357
+ /* @__PURE__ */ jsxRuntime.jsx(
1358
+ strapiAdmin$1.Table.Root,
1359
+ {
1360
+ rows: releaseActions[key].map((item) => ({
1361
+ ...item,
1362
+ id: Number(item.entry.id)
1363
+ })),
1364
+ headers,
1365
+ isLoading: isLoading || isFetching,
1366
+ children: /* @__PURE__ */ jsxRuntime.jsxs(strapiAdmin$1.Table.Content, { children: [
1367
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Table.Head, { children: headers.map(({ label, name }) => /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Table.HeaderCell, { label: formatMessage(label), name }, name)) }),
1368
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Table.Loading, {}),
1369
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Table.Body, { children: releaseActions[key].map(
1370
+ ({ id, contentType, locale, type, entry, status }, actionIndex) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1371
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "25%", maxWidth: "200px", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, children: `${contentType.mainFieldValue || entry.id}` }) }),
1372
+ hasI18nEnabled && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "10%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: `${locale?.name ? locale.name : "-"}` }) }),
1373
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "10%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: contentType.displayName || "" }) }),
1374
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "20%", children: release.releasedAt ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage(
1375
+ {
1376
+ id: "content-releases.page.ReleaseDetails.table.action-published",
1377
+ defaultMessage: "This entry was <b>{isPublish, select, true {published} other {unpublished}}</b>."
1378
+ },
1379
+ {
1380
+ isPublish: type === "publish",
1381
+ b: (children) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children })
1382
+ }
1383
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(
1384
+ index.ReleaseActionOptions,
1385
+ {
1386
+ selected: type,
1387
+ handleChange: (e) => handleChangeType(e, id, [key, actionIndex]),
1388
+ name: `release-action-${id}-type`,
1389
+ disabled: !canUpdate
1390
+ }
1391
+ ) }),
1392
+ !release.releasedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1393
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { width: "20%", minWidth: "200px", children: /* @__PURE__ */ jsxRuntime.jsx(
1394
+ EntryValidationPopover,
1395
+ {
1396
+ action: type,
1397
+ schema: contentTypes?.[contentType.uid],
1398
+ entry,
1399
+ status
1400
+ }
1401
+ ) }),
1402
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxRuntime.jsxs(index.ReleaseActionMenu.Root, { children: [
1403
+ /* @__PURE__ */ jsxRuntime.jsx(
1404
+ index.ReleaseActionMenu.ReleaseActionEntryLinkItem,
1405
+ {
1406
+ contentTypeUid: contentType.uid,
1407
+ documentId: entry.documentId,
1408
+ locale: locale?.code
1409
+ }
1410
+ ),
1411
+ /* @__PURE__ */ jsxRuntime.jsx(
1412
+ index.ReleaseActionMenu.DeleteReleaseActionItem,
1413
+ {
1414
+ releaseId: release.id,
1415
+ actionId: id
1416
+ }
1417
+ )
1418
+ ] }) }) })
1419
+ ] })
1420
+ ] }, id)
1421
+ ) })
1422
+ ] })
1423
+ }
1424
+ )
1425
+ ] }, `releases-group-${key}`)),
1426
+ /* @__PURE__ */ jsxRuntime.jsxs(
1427
+ strapiAdmin$1.Pagination.Root,
1428
+ {
1429
+ ...releaseMeta?.pagination,
1430
+ defaultPageSize: releaseMeta?.pagination?.pageSize,
1431
+ children: [
1432
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Pagination.PageSize, {}),
1433
+ /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Pagination.Links, {})
1434
+ ]
1435
+ }
1436
+ )
1437
+ ] }) });
1438
+ };
1439
+ const ReleaseDetailsPage = () => {
1440
+ const { formatMessage } = reactIntl.useIntl();
1441
+ const { releaseId } = reactRouterDom.useParams();
1442
+ const { toggleNotification } = strapiAdmin$1.useNotification();
1443
+ const { formatAPIError } = strapiAdmin$1.useAPIErrorHandler();
1444
+ const navigate = reactRouterDom.useNavigate();
1445
+ const [releaseModalShown, setReleaseModalShown] = React__namespace.useState(false);
1446
+ const [showWarningSubmit, setWarningSubmit] = React__namespace.useState(false);
1447
+ const {
1448
+ isLoading: isLoadingDetails,
1449
+ data,
1450
+ isSuccess: isSuccessDetails
1451
+ } = index.useGetReleaseQuery(
1452
+ { id: releaseId },
1453
+ {
1454
+ skip: !releaseId
1455
+ }
1456
+ );
1457
+ const { data: dataTimezone, isLoading: isLoadingTimezone } = index.useGetReleaseSettingsQuery();
1458
+ const [updateRelease, { isLoading: isSubmittingForm }] = index.useUpdateReleaseMutation();
1459
+ const [deleteRelease] = index.useDeleteReleaseMutation();
1460
+ const toggleEditReleaseModal = () => {
1461
+ setReleaseModalShown((prev) => !prev);
1462
+ };
1463
+ const getTimezoneValue = () => {
1464
+ if (releaseData?.timezone) {
1465
+ return releaseData.timezone;
1466
+ } else {
1467
+ if (dataTimezone?.data.defaultTimezone) {
1468
+ return dataTimezone.data.defaultTimezone;
1469
+ }
1470
+ return null;
1471
+ }
1472
+ };
1473
+ const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
1474
+ if (isLoadingDetails || isLoadingTimezone) {
1475
+ return /* @__PURE__ */ jsxRuntime.jsx(
1476
+ ReleaseDetailsLayout,
1477
+ {
1478
+ toggleEditReleaseModal,
1479
+ toggleWarningSubmit,
1480
+ children: /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Page.Loading, {})
1481
+ }
1482
+ );
1483
+ }
1484
+ if (!releaseId) {
1485
+ return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: ".." });
1486
+ }
1487
+ const releaseData = isSuccessDetails && data?.data || null;
1488
+ const title = releaseData?.name || "";
1489
+ const timezone = getTimezoneValue();
1490
+ const scheduledAt = releaseData?.scheduledAt && timezone ? dateFnsTz.utcToZonedTime(releaseData.scheduledAt, timezone) : null;
1491
+ const date = scheduledAt ? format__default.default(scheduledAt, "yyyy-MM-dd") : void 0;
1492
+ const time = scheduledAt ? format__default.default(scheduledAt, "HH:mm") : "";
1493
+ const handleEditRelease = async (values) => {
1494
+ const response = await updateRelease({
1495
+ id: releaseId,
1496
+ name: values.name,
1497
+ scheduledAt: values.scheduledAt,
1498
+ timezone: values.timezone
1499
+ });
1500
+ if ("data" in response) {
1501
+ toggleNotification({
1502
+ type: "success",
1503
+ message: formatMessage({
1504
+ id: "content-releases.modal.release-updated-notification-success",
1505
+ defaultMessage: "Release updated."
1506
+ })
1507
+ });
1508
+ toggleEditReleaseModal();
1509
+ } else if (strapiAdmin$1.isFetchError(response.error)) {
1510
+ toggleNotification({
1511
+ type: "danger",
1512
+ message: formatAPIError(response.error)
1513
+ });
1514
+ } else {
1515
+ toggleNotification({
1516
+ type: "danger",
1517
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1518
+ });
1519
+ }
1520
+ };
1521
+ const handleDeleteRelease = async () => {
1522
+ const response = await deleteRelease({
1523
+ id: releaseId
1524
+ });
1525
+ if ("data" in response) {
1526
+ navigate("..");
1527
+ } else if (strapiAdmin$1.isFetchError(response.error)) {
1528
+ toggleNotification({
1529
+ type: "danger",
1530
+ message: formatAPIError(response.error)
1531
+ });
1532
+ } else {
1533
+ toggleNotification({
1534
+ type: "danger",
1535
+ message: formatMessage({ id: "notification.error", defaultMessage: "An error occurred" })
1536
+ });
1537
+ }
1538
+ };
1539
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1540
+ ReleaseDetailsLayout,
1541
+ {
1542
+ toggleEditReleaseModal,
1543
+ toggleWarningSubmit,
1544
+ children: [
1545
+ /* @__PURE__ */ jsxRuntime.jsx(ReleaseDetailsBody, { releaseId }),
1546
+ /* @__PURE__ */ jsxRuntime.jsx(
1547
+ ReleaseModal,
1548
+ {
1549
+ open: releaseModalShown,
1550
+ handleClose: toggleEditReleaseModal,
1551
+ handleSubmit: handleEditRelease,
1552
+ isLoading: isLoadingDetails || isSubmittingForm,
1553
+ initialValues: {
1554
+ name: title || "",
1555
+ scheduledAt,
1556
+ date,
1557
+ time,
1558
+ isScheduled: Boolean(scheduledAt),
1559
+ timezone
1560
+ }
1561
+ }
1562
+ ),
1563
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Dialog.Root, { open: showWarningSubmit, onOpenChange: toggleWarningSubmit, children: /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.ConfirmDialog, { onConfirm: handleDeleteRelease, children: formatMessage({
1564
+ id: "content-releases.dialog.confirmation-message",
1565
+ defaultMessage: "Are you sure you want to delete this release?"
1566
+ }) }) })
1567
+ ]
1568
+ }
1569
+ );
1570
+ };
1571
+ const App = () => {
1572
+ return /* @__PURE__ */ jsxRuntime.jsx(strapiAdmin$1.Page.Protect, { permissions: index.PERMISSIONS.main, children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
1573
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(ReleasesPage, {}) }),
1574
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: ":releaseId", element: /* @__PURE__ */ jsxRuntime.jsx(ReleaseDetailsPage, {}) })
1575
+ ] }) });
1576
+ };
1577
+ exports.App = App;
1578
+ //# sourceMappingURL=App-SGjO5UPV.js.map