@strapi/content-manager 0.0.0-experimental.edc24aaa3bb5a90fa5fd4fee208167dd4e2e38d4 → 0.0.0-experimental.f0d4afee92a0d386f80385590c87955656f995ce
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.
- package/dist/_chunks/{ComponentConfigurationPage-BAgyHiMm.mjs → ComponentConfigurationPage-CIjXcRAB.mjs} +4 -4
- package/dist/_chunks/{ComponentConfigurationPage-BAgyHiMm.mjs.map → ComponentConfigurationPage-CIjXcRAB.mjs.map} +1 -1
- package/dist/_chunks/{ComponentConfigurationPage-5ukroXAh.js → ComponentConfigurationPage-gsCd80MU.js} +4 -4
- package/dist/_chunks/{ComponentConfigurationPage-5ukroXAh.js.map → ComponentConfigurationPage-gsCd80MU.js.map} +1 -1
- package/dist/_chunks/{EditConfigurationPage-DmoXawIh.mjs → EditConfigurationPage-BglmD_BF.mjs} +4 -4
- package/dist/_chunks/{EditConfigurationPage-DmoXawIh.mjs.map → EditConfigurationPage-BglmD_BF.mjs.map} +1 -1
- package/dist/_chunks/{EditConfigurationPage-Xp7lun0f.js → EditConfigurationPage-DHDQKBzw.js} +4 -4
- package/dist/_chunks/{EditConfigurationPage-Xp7lun0f.js.map → EditConfigurationPage-DHDQKBzw.js.map} +1 -1
- package/dist/_chunks/{EditViewPage-C-ukDOB7.js → EditViewPage-C4iTxUPU.js} +30 -9
- package/dist/_chunks/EditViewPage-C4iTxUPU.js.map +1 -0
- package/dist/_chunks/{EditViewPage-BLsjc5F-.mjs → EditViewPage-CiwVPMaK.mjs} +30 -9
- package/dist/_chunks/EditViewPage-CiwVPMaK.mjs.map +1 -0
- package/dist/_chunks/{Field-Cs7duwWd.mjs → Field-DIjL1b5d.mjs} +173 -101
- package/dist/_chunks/Field-DIjL1b5d.mjs.map +1 -0
- package/dist/_chunks/{Field-Bfph5SOd.js → Field-DhXEK8y1.js} +175 -103
- package/dist/_chunks/Field-DhXEK8y1.js.map +1 -0
- package/dist/_chunks/{Form-Dg_GS5TQ.mjs → Form-CmNesrvR.mjs} +36 -17
- package/dist/_chunks/Form-CmNesrvR.mjs.map +1 -0
- package/dist/_chunks/{Form-CPYqIWDG.js → Form-CwmJ4sWe.js} +36 -17
- package/dist/_chunks/Form-CwmJ4sWe.js.map +1 -0
- package/dist/_chunks/{History-DNQkXANT.js → History-BLCCNgCt.js} +24 -11
- package/dist/_chunks/History-BLCCNgCt.js.map +1 -0
- package/dist/_chunks/{History-wrnHqf09.mjs → History-D-99Wh30.mjs} +25 -12
- package/dist/_chunks/History-D-99Wh30.mjs.map +1 -0
- package/dist/_chunks/{ListConfigurationPage-CUQxfpjT.js → ListConfigurationPage-DxWpeZrO.js} +15 -5
- package/dist/_chunks/ListConfigurationPage-DxWpeZrO.js.map +1 -0
- package/dist/_chunks/{ListConfigurationPage-DScmJVkW.mjs → ListConfigurationPage-JPWZz7Kg.mjs} +15 -5
- package/dist/_chunks/ListConfigurationPage-JPWZz7Kg.mjs.map +1 -0
- package/dist/_chunks/{ListViewPage-BsLiH2-2.js → ListViewPage-CIQekSFz.js} +61 -41
- package/dist/_chunks/ListViewPage-CIQekSFz.js.map +1 -0
- package/dist/_chunks/{ListViewPage-C4IvrMgY.mjs → ListViewPage-DSK3f0ST.mjs} +59 -39
- package/dist/_chunks/ListViewPage-DSK3f0ST.mjs.map +1 -0
- package/dist/_chunks/{NoContentTypePage-BZ-PnGAf.js → NoContentTypePage-C5cxKvC2.js} +2 -2
- package/dist/_chunks/{NoContentTypePage-BZ-PnGAf.js.map → NoContentTypePage-C5cxKvC2.js.map} +1 -1
- package/dist/_chunks/{NoContentTypePage-Djg8nPlj.mjs → NoContentTypePage-D99LU1YP.mjs} +2 -2
- package/dist/_chunks/{NoContentTypePage-Djg8nPlj.mjs.map → NoContentTypePage-D99LU1YP.mjs.map} +1 -1
- package/dist/_chunks/{NoPermissionsPage-DSP7R-hv.mjs → NoPermissionsPage-DBrBw-0y.mjs} +2 -2
- package/dist/_chunks/{NoPermissionsPage-DSP7R-hv.mjs.map → NoPermissionsPage-DBrBw-0y.mjs.map} +1 -1
- package/dist/_chunks/{NoPermissionsPage-_lUqjGW3.js → NoPermissionsPage-Oy4tmUrW.js} +2 -2
- package/dist/_chunks/{NoPermissionsPage-_lUqjGW3.js.map → NoPermissionsPage-Oy4tmUrW.js.map} +1 -1
- package/dist/_chunks/{Relations-BZr8tL0R.mjs → Relations-BBmhcWFV.mjs} +70 -37
- package/dist/_chunks/Relations-BBmhcWFV.mjs.map +1 -0
- package/dist/_chunks/{Relations-CtELXYIK.js → Relations-eG-9p_qS.js} +69 -36
- package/dist/_chunks/Relations-eG-9p_qS.js.map +1 -0
- package/dist/_chunks/{en-uOUIxfcQ.js → en-Bm0D0IWz.js} +13 -12
- package/dist/_chunks/{en-uOUIxfcQ.js.map → en-Bm0D0IWz.js.map} +1 -1
- package/dist/_chunks/{en-BrCTWlZv.mjs → en-DKV44jRb.mjs} +13 -12
- package/dist/_chunks/{en-BrCTWlZv.mjs.map → en-DKV44jRb.mjs.map} +1 -1
- package/dist/_chunks/{index-OerGjbAN.js → index-BIWDoFLK.js} +1968 -1781
- package/dist/_chunks/index-BIWDoFLK.js.map +1 -0
- package/dist/_chunks/{index-c_5DdJi-.mjs → index-BrUzbQ30.mjs} +1987 -1801
- package/dist/_chunks/index-BrUzbQ30.mjs.map +1 -0
- package/dist/_chunks/{layout-oPBiO7RY.mjs → layout-_5-cXs34.mjs} +22 -9
- package/dist/_chunks/layout-_5-cXs34.mjs.map +1 -0
- package/dist/_chunks/{layout-Ci7qHlFb.js → layout-lMc9i1-Z.js} +21 -8
- package/dist/_chunks/layout-lMc9i1-Z.js.map +1 -0
- package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
- package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
- package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
- package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
- package/dist/_chunks/{relations-COBpStiF.js → relations-BRHithi8.js} +3 -7
- package/dist/_chunks/relations-BRHithi8.js.map +1 -0
- package/dist/_chunks/{relations-BIdWFjdq.mjs → relations-B_VLk-DD.mjs} +3 -7
- package/dist/_chunks/relations-B_VLk-DD.mjs.map +1 -0
- package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
- package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
- package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
- package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
- package/dist/admin/index.js +2 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +5 -4
- package/dist/admin/src/exports.d.ts +1 -1
- package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
- package/dist/admin/src/hooks/useDocument.d.ts +32 -1
- package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
- package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
- package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
- package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
- package/dist/admin/src/preview/constants.d.ts +1 -0
- package/dist/admin/src/preview/index.d.ts +4 -0
- package/dist/admin/src/services/api.d.ts +1 -1
- package/dist/admin/src/services/components.d.ts +2 -2
- package/dist/admin/src/services/contentTypes.d.ts +3 -3
- package/dist/admin/src/services/documents.d.ts +19 -17
- package/dist/admin/src/services/init.d.ts +1 -1
- package/dist/admin/src/services/relations.d.ts +2 -2
- package/dist/admin/src/services/uid.d.ts +3 -3
- package/dist/admin/src/utils/validation.d.ts +4 -1
- package/dist/server/index.js +513 -235
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +514 -236
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts.map +1 -1
- package/dist/server/src/controllers/relations.d.ts.map +1 -1
- package/dist/server/src/controllers/utils/metadata.d.ts +15 -1
- package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
- package/dist/server/src/history/services/history.d.ts.map +1 -1
- package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
- package/dist/server/src/history/services/utils.d.ts +4 -4
- package/dist/server/src/history/services/utils.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +4 -4
- package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
- package/dist/server/src/preview/constants.d.ts +2 -0
- package/dist/server/src/preview/constants.d.ts.map +1 -0
- package/dist/server/src/preview/controllers/index.d.ts +2 -0
- package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
- package/dist/server/src/preview/controllers/preview.d.ts +13 -0
- package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
- package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
- package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
- package/dist/server/src/preview/index.d.ts +4 -0
- package/dist/server/src/preview/index.d.ts.map +1 -0
- package/dist/server/src/preview/routes/index.d.ts +8 -0
- package/dist/server/src/preview/routes/index.d.ts.map +1 -0
- package/dist/server/src/preview/routes/preview.d.ts +4 -0
- package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
- package/dist/server/src/preview/services/index.d.ts +15 -0
- package/dist/server/src/preview/services/index.d.ts.map +1 -0
- package/dist/server/src/preview/services/preview-config.d.ts +30 -0
- package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
- package/dist/server/src/preview/services/preview.d.ts +12 -0
- package/dist/server/src/preview/services/preview.d.ts.map +1 -0
- package/dist/server/src/preview/utils.d.ts +18 -0
- package/dist/server/src/preview/utils.d.ts.map +1 -0
- package/dist/server/src/routes/index.d.ts.map +1 -1
- package/dist/server/src/services/document-metadata.d.ts +8 -8
- package/dist/server/src/services/document-metadata.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +4 -4
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/permission-checker.d.ts.map +1 -1
- package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
- package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
- package/dist/server/src/utils/index.d.ts +2 -0
- package/dist/server/src/utils/index.d.ts.map +1 -1
- package/dist/shared/contracts/collection-types.d.ts +3 -1
- package/dist/shared/contracts/collection-types.d.ts.map +1 -1
- package/dist/shared/contracts/index.d.ts +1 -0
- package/dist/shared/contracts/index.d.ts.map +1 -1
- package/dist/shared/contracts/preview.d.ts +27 -0
- package/dist/shared/contracts/preview.d.ts.map +1 -0
- package/dist/shared/index.js +4 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +4 -0
- package/dist/shared/index.mjs.map +1 -1
- package/package.json +13 -13
- package/dist/_chunks/EditViewPage-BLsjc5F-.mjs.map +0 -1
- package/dist/_chunks/EditViewPage-C-ukDOB7.js.map +0 -1
- package/dist/_chunks/Field-Bfph5SOd.js.map +0 -1
- package/dist/_chunks/Field-Cs7duwWd.mjs.map +0 -1
- package/dist/_chunks/Form-CPYqIWDG.js.map +0 -1
- package/dist/_chunks/Form-Dg_GS5TQ.mjs.map +0 -1
- package/dist/_chunks/History-DNQkXANT.js.map +0 -1
- package/dist/_chunks/History-wrnHqf09.mjs.map +0 -1
- package/dist/_chunks/ListConfigurationPage-CUQxfpjT.js.map +0 -1
- package/dist/_chunks/ListConfigurationPage-DScmJVkW.mjs.map +0 -1
- package/dist/_chunks/ListViewPage-BsLiH2-2.js.map +0 -1
- package/dist/_chunks/ListViewPage-C4IvrMgY.mjs.map +0 -1
- package/dist/_chunks/Relations-BZr8tL0R.mjs.map +0 -1
- package/dist/_chunks/Relations-CtELXYIK.js.map +0 -1
- package/dist/_chunks/index-OerGjbAN.js.map +0 -1
- package/dist/_chunks/index-c_5DdJi-.mjs.map +0 -1
- package/dist/_chunks/layout-Ci7qHlFb.js.map +0 -1
- package/dist/_chunks/layout-oPBiO7RY.mjs.map +0 -1
- package/dist/_chunks/relations-BIdWFjdq.mjs.map +0 -1
- package/dist/_chunks/relations-COBpStiF.js.map +0 -1
- package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
- package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
- package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
- package/strapi-server.js +0 -3
@@ -1,16 +1,17 @@
|
|
1
|
-
import {
|
1
|
+
import { More, Cross, WarningCircle, ListPlus, Pencil, Trash, Check, CrossCircle, CheckCircle, ArrowsCounterClockwise, ChevronRight, Duplicate, ClockCounterClockwise, Feather } from "@strapi/icons";
|
2
2
|
import { jsx, Fragment, jsxs } from "react/jsx-runtime";
|
3
|
-
import { useStrapiApp, createContext, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors,
|
3
|
+
import { useStrapiApp, createContext, useQueryParams, useAuth, useRBAC, Page, adminApi, translatedErrors, useNotification, useAPIErrorHandler, getYupValidationErrors, useForm, useTracking, useGuidedTour, BackButton, DescriptionComponentRenderer, useTable, Table } from "@strapi/admin/strapi-admin";
|
4
4
|
import * as React from "react";
|
5
5
|
import { lazy } from "react";
|
6
|
-
import { Button, Menu, VisuallyHidden, Flex,
|
6
|
+
import { Button, Menu, VisuallyHidden, Flex, Typography, Dialog, Modal, Radio, Status, Box, SingleSelect, SingleSelectOption, IconButton, Loader, Tooltip, LinkButton } from "@strapi/design-system";
|
7
|
+
import mapValues from "lodash/fp/mapValues";
|
7
8
|
import { useIntl } from "react-intl";
|
8
|
-
import { useParams,
|
9
|
-
import { styled } from "styled-components";
|
9
|
+
import { useParams, useNavigate, Navigate, useMatch, useLocation, Link, NavLink } from "react-router-dom";
|
10
10
|
import * as yup from "yup";
|
11
11
|
import { ValidationError } from "yup";
|
12
12
|
import pipe from "lodash/fp/pipe";
|
13
13
|
import { intervalToDuration, isPast } from "date-fns";
|
14
|
+
import { styled } from "styled-components";
|
14
15
|
import { stringify } from "qs";
|
15
16
|
import { createSlice, combineReducers } from "@reduxjs/toolkit";
|
16
17
|
const __variableDynamicImportRuntimeHelper = (glob, path) => {
|
@@ -100,6 +101,7 @@ const DocumentRBAC = ({ children, permissions }) => {
|
|
100
101
|
if (!slug) {
|
101
102
|
throw new Error("Cannot find the slug param in the URL");
|
102
103
|
}
|
104
|
+
const [{ rawQuery }] = useQueryParams();
|
103
105
|
const userPermissions = useAuth("DocumentRBAC", (state) => state.permissions);
|
104
106
|
const contentTypePermissions = React.useMemo(() => {
|
105
107
|
const contentTypePermissions2 = userPermissions.filter(
|
@@ -110,7 +112,14 @@ const DocumentRBAC = ({ children, permissions }) => {
|
|
110
112
|
return { ...acc, [action]: [permission] };
|
111
113
|
}, {});
|
112
114
|
}, [slug, userPermissions]);
|
113
|
-
const { isLoading, allowedActions } = useRBAC(
|
115
|
+
const { isLoading, allowedActions } = useRBAC(
|
116
|
+
contentTypePermissions,
|
117
|
+
permissions ?? void 0,
|
118
|
+
// TODO: useRBAC context should be typed and built differently
|
119
|
+
// We are passing raw query as context to the hook so that it can
|
120
|
+
// rely on the locale provided from DocumentRBAC for its permission calculations.
|
121
|
+
rawQuery
|
122
|
+
);
|
114
123
|
const canCreateFields = !isLoading && allowedActions.canCreate ? extractAndDedupeFields(contentTypePermissions.create) : [];
|
115
124
|
const canReadFields = !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];
|
116
125
|
const canUpdateFields = !isLoading && allowedActions.canUpdate ? extractAndDedupeFields(contentTypePermissions.update) : [];
|
@@ -158,7 +167,8 @@ const contentManagerApi = adminApi.enhanceEndpoints({
|
|
158
167
|
"Document",
|
159
168
|
"InitialData",
|
160
169
|
"HistoryVersion",
|
161
|
-
"Relations"
|
170
|
+
"Relations",
|
171
|
+
"UidAvailability"
|
162
172
|
]
|
163
173
|
});
|
164
174
|
const documentApi = contentManagerApi.injectEndpoints({
|
@@ -188,7 +198,10 @@ const documentApi = contentManagerApi.injectEndpoints({
|
|
188
198
|
params
|
189
199
|
}
|
190
200
|
}),
|
191
|
-
invalidatesTags: (_result, _error, { model }) => [
|
201
|
+
invalidatesTags: (_result, _error, { model }) => [
|
202
|
+
{ type: "Document", id: `${model}_LIST` },
|
203
|
+
{ type: "UidAvailability", id: model }
|
204
|
+
]
|
192
205
|
}),
|
193
206
|
/**
|
194
207
|
* Creates a new collection-type document. This should ONLY be used for collection-types.
|
@@ -205,7 +218,8 @@ const documentApi = contentManagerApi.injectEndpoints({
|
|
205
218
|
}),
|
206
219
|
invalidatesTags: (result, _error, { model }) => [
|
207
220
|
{ type: "Document", id: `${model}_LIST` },
|
208
|
-
"Relations"
|
221
|
+
"Relations",
|
222
|
+
{ type: "UidAvailability", id: model }
|
209
223
|
]
|
210
224
|
}),
|
211
225
|
deleteDocument: builder.mutation({
|
@@ -246,7 +260,8 @@ const documentApi = contentManagerApi.injectEndpoints({
|
|
246
260
|
id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
|
247
261
|
},
|
248
262
|
{ type: "Document", id: `${model}_LIST` },
|
249
|
-
"Relations"
|
263
|
+
"Relations",
|
264
|
+
{ type: "UidAvailability", id: model }
|
250
265
|
];
|
251
266
|
}
|
252
267
|
}),
|
@@ -256,7 +271,7 @@ const documentApi = contentManagerApi.injectEndpoints({
|
|
256
271
|
*/
|
257
272
|
getAllDocuments: builder.query({
|
258
273
|
query: ({ model, params }) => ({
|
259
|
-
url: `/content-manager/collection-types/${model}`,
|
274
|
+
url: `/content-manager/collection-types/${model}${params ? `?${params}` : ""}`,
|
260
275
|
method: "GET",
|
261
276
|
config: {
|
262
277
|
params
|
@@ -264,6 +279,7 @@ const documentApi = contentManagerApi.injectEndpoints({
|
|
264
279
|
}),
|
265
280
|
providesTags: (result, _error, arg) => {
|
266
281
|
return [
|
282
|
+
{ type: "Document", id: `ALL_LIST` },
|
267
283
|
{ type: "Document", id: `${arg.model}_LIST` },
|
268
284
|
...result?.results.map(({ documentId }) => ({
|
269
285
|
type: "Document",
|
@@ -302,6 +318,11 @@ const documentApi = contentManagerApi.injectEndpoints({
|
|
302
318
|
{
|
303
319
|
type: "Document",
|
304
320
|
id: collectionType !== SINGLE_TYPES ? `${model}_${result && "documentId" in result ? result.documentId : documentId}` : model
|
321
|
+
},
|
322
|
+
// Make it easy to invalidate all individual documents queries for a model
|
323
|
+
{
|
324
|
+
type: "Document",
|
325
|
+
id: `${model}_ALL_ITEMS`
|
305
326
|
}
|
306
327
|
];
|
307
328
|
}
|
@@ -365,7 +386,8 @@ const documentApi = contentManagerApi.injectEndpoints({
|
|
365
386
|
type: "Document",
|
366
387
|
id: collectionType !== SINGLE_TYPES ? `${model}_${documentId}` : model
|
367
388
|
},
|
368
|
-
"Relations"
|
389
|
+
"Relations",
|
390
|
+
{ type: "UidAvailability", id: model }
|
369
391
|
];
|
370
392
|
},
|
371
393
|
async onQueryStarted({ data, ...patch }, { dispatch, queryFulfilled }) {
|
@@ -448,20 +470,39 @@ const buildValidParams = (query) => {
|
|
448
470
|
const isBaseQueryError = (error) => {
|
449
471
|
return error.name !== void 0;
|
450
472
|
};
|
451
|
-
const
|
473
|
+
const arrayValidator = (attribute, options) => ({
|
474
|
+
message: translatedErrors.required,
|
475
|
+
test(value) {
|
476
|
+
if (options.status === "draft") {
|
477
|
+
return true;
|
478
|
+
}
|
479
|
+
if (!attribute.required) {
|
480
|
+
return true;
|
481
|
+
}
|
482
|
+
if (!value) {
|
483
|
+
return false;
|
484
|
+
}
|
485
|
+
if (Array.isArray(value) && value.length === 0) {
|
486
|
+
return false;
|
487
|
+
}
|
488
|
+
return true;
|
489
|
+
}
|
490
|
+
});
|
491
|
+
const createYupSchema = (attributes = {}, components = {}, options = { status: null }) => {
|
452
492
|
const createModelSchema = (attributes2) => yup.object().shape(
|
453
493
|
Object.entries(attributes2).reduce((acc, [name, attribute]) => {
|
454
494
|
if (DOCUMENT_META_FIELDS.includes(name)) {
|
455
495
|
return acc;
|
456
496
|
}
|
457
497
|
const validations = [
|
498
|
+
addNullableValidation,
|
458
499
|
addRequiredValidation,
|
459
500
|
addMinLengthValidation,
|
460
501
|
addMaxLengthValidation,
|
461
502
|
addMinValidation,
|
462
503
|
addMaxValidation,
|
463
504
|
addRegexValidation
|
464
|
-
].map((fn) => fn(attribute));
|
505
|
+
].map((fn) => fn(attribute, options));
|
465
506
|
const transformSchema = pipe(...validations);
|
466
507
|
switch (attribute.type) {
|
467
508
|
case "component": {
|
@@ -471,12 +512,12 @@ const createYupSchema = (attributes = {}, components = {}) => {
|
|
471
512
|
...acc,
|
472
513
|
[name]: transformSchema(
|
473
514
|
yup.array().of(createModelSchema(attributes3).nullable(false))
|
474
|
-
)
|
515
|
+
).test(arrayValidator(attribute, options))
|
475
516
|
};
|
476
517
|
} else {
|
477
518
|
return {
|
478
519
|
...acc,
|
479
|
-
[name]: transformSchema(createModelSchema(attributes3))
|
520
|
+
[name]: transformSchema(createModelSchema(attributes3).nullable())
|
480
521
|
};
|
481
522
|
}
|
482
523
|
}
|
@@ -498,7 +539,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
|
|
498
539
|
}
|
499
540
|
)
|
500
541
|
)
|
501
|
-
)
|
542
|
+
).test(arrayValidator(attribute, options))
|
502
543
|
};
|
503
544
|
case "relation":
|
504
545
|
return {
|
@@ -510,7 +551,7 @@ const createYupSchema = (attributes = {}, components = {}) => {
|
|
510
551
|
} else if (Array.isArray(value)) {
|
511
552
|
return yup.array().of(
|
512
553
|
yup.object().shape({
|
513
|
-
id: yup.
|
554
|
+
id: yup.number().required()
|
514
555
|
})
|
515
556
|
);
|
516
557
|
} else if (typeof value === "object") {
|
@@ -562,6 +603,14 @@ const createAttributeSchema = (attribute) => {
|
|
562
603
|
if (!value || typeof value === "string" && value.length === 0) {
|
563
604
|
return true;
|
564
605
|
}
|
606
|
+
if (typeof value === "object") {
|
607
|
+
try {
|
608
|
+
JSON.stringify(value);
|
609
|
+
return true;
|
610
|
+
} catch (err) {
|
611
|
+
return false;
|
612
|
+
}
|
613
|
+
}
|
565
614
|
try {
|
566
615
|
JSON.parse(value);
|
567
616
|
return true;
|
@@ -580,13 +629,7 @@ const createAttributeSchema = (attribute) => {
|
|
580
629
|
return yup.mixed();
|
581
630
|
}
|
582
631
|
};
|
583
|
-
const
|
584
|
-
if ((attribute.type === "component" && attribute.repeatable || attribute.type === "dynamiczone") && attribute.required && "min" in schema) {
|
585
|
-
return schema.min(1, translatedErrors.required);
|
586
|
-
}
|
587
|
-
if (attribute.required && attribute.type !== "relation") {
|
588
|
-
return schema.required(translatedErrors.required);
|
589
|
-
}
|
632
|
+
const nullableSchema = (schema) => {
|
590
633
|
return schema?.nullable ? schema.nullable() : (
|
591
634
|
// In some cases '.nullable' will not be available on the schema.
|
592
635
|
// e.g. when the schema has been built using yup.lazy (e.g. for relations).
|
@@ -594,7 +637,22 @@ const addRequiredValidation = (attribute) => (schema) => {
|
|
594
637
|
schema
|
595
638
|
);
|
596
639
|
};
|
597
|
-
const
|
640
|
+
const addNullableValidation = () => (schema) => {
|
641
|
+
return nullableSchema(schema);
|
642
|
+
};
|
643
|
+
const addRequiredValidation = (attribute, options) => (schema) => {
|
644
|
+
if (options.status === "draft" || !attribute.required) {
|
645
|
+
return schema;
|
646
|
+
}
|
647
|
+
if (attribute.required && "required" in schema) {
|
648
|
+
return schema.required(translatedErrors.required);
|
649
|
+
}
|
650
|
+
return schema;
|
651
|
+
};
|
652
|
+
const addMinLengthValidation = (attribute, options) => (schema) => {
|
653
|
+
if (options.status === "draft") {
|
654
|
+
return schema;
|
655
|
+
}
|
598
656
|
if ("minLength" in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && "min" in schema) {
|
599
657
|
return schema.min(attribute.minLength, {
|
600
658
|
...translatedErrors.minLength,
|
@@ -616,32 +674,13 @@ const addMaxLengthValidation = (attribute) => (schema) => {
|
|
616
674
|
}
|
617
675
|
return schema;
|
618
676
|
};
|
619
|
-
const addMinValidation = (attribute) => (schema) => {
|
620
|
-
if ("
|
677
|
+
const addMinValidation = (attribute, options) => (schema) => {
|
678
|
+
if (options.status === "draft") {
|
679
|
+
return schema;
|
680
|
+
}
|
681
|
+
if ("min" in attribute && "min" in schema) {
|
621
682
|
const min = toInteger(attribute.min);
|
622
|
-
if (
|
623
|
-
if (!attribute.required && "test" in schema && min) {
|
624
|
-
return schema.test(
|
625
|
-
"custom-min",
|
626
|
-
{
|
627
|
-
...translatedErrors.min,
|
628
|
-
values: {
|
629
|
-
min: attribute.min
|
630
|
-
}
|
631
|
-
},
|
632
|
-
(value) => {
|
633
|
-
if (!value) {
|
634
|
-
return true;
|
635
|
-
}
|
636
|
-
if (Array.isArray(value) && value.length === 0) {
|
637
|
-
return true;
|
638
|
-
}
|
639
|
-
return value.length >= min;
|
640
|
-
}
|
641
|
-
);
|
642
|
-
}
|
643
|
-
}
|
644
|
-
if ("min" in schema && min) {
|
683
|
+
if (min) {
|
645
684
|
return schema.min(min, {
|
646
685
|
...translatedErrors.min,
|
647
686
|
values: {
|
@@ -759,19 +798,115 @@ const extractContentTypeComponents = (attributes = {}, allComponents = {}) => {
|
|
759
798
|
}, {});
|
760
799
|
return componentsByKey;
|
761
800
|
};
|
762
|
-
const
|
801
|
+
const HOOKS = {
|
802
|
+
/**
|
803
|
+
* Hook that allows to mutate the displayed headers of the list view table
|
804
|
+
* @constant
|
805
|
+
* @type {string}
|
806
|
+
*/
|
807
|
+
INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
|
808
|
+
/**
|
809
|
+
* Hook that allows to mutate the CM's collection types links pre-set filters
|
810
|
+
* @constant
|
811
|
+
* @type {string}
|
812
|
+
*/
|
813
|
+
MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
|
814
|
+
/**
|
815
|
+
* Hook that allows to mutate the CM's edit view layout
|
816
|
+
* @constant
|
817
|
+
* @type {string}
|
818
|
+
*/
|
819
|
+
MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
|
820
|
+
/**
|
821
|
+
* Hook that allows to mutate the CM's single types links pre-set filters
|
822
|
+
* @constant
|
823
|
+
* @type {string}
|
824
|
+
*/
|
825
|
+
MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
|
826
|
+
};
|
827
|
+
const contentTypesApi = contentManagerApi.injectEndpoints({
|
828
|
+
endpoints: (builder) => ({
|
829
|
+
getContentTypeConfiguration: builder.query({
|
830
|
+
query: (uid) => ({
|
831
|
+
url: `/content-manager/content-types/${uid}/configuration`,
|
832
|
+
method: "GET"
|
833
|
+
}),
|
834
|
+
transformResponse: (response) => response.data,
|
835
|
+
providesTags: (_result, _error, uid) => [
|
836
|
+
{ type: "ContentTypesConfiguration", id: uid },
|
837
|
+
{ type: "ContentTypeSettings", id: "LIST" }
|
838
|
+
]
|
839
|
+
}),
|
840
|
+
getAllContentTypeSettings: builder.query({
|
841
|
+
query: () => "/content-manager/content-types-settings",
|
842
|
+
transformResponse: (response) => response.data,
|
843
|
+
providesTags: [{ type: "ContentTypeSettings", id: "LIST" }]
|
844
|
+
}),
|
845
|
+
updateContentTypeConfiguration: builder.mutation({
|
846
|
+
query: ({ uid, ...body }) => ({
|
847
|
+
url: `/content-manager/content-types/${uid}/configuration`,
|
848
|
+
method: "PUT",
|
849
|
+
data: body
|
850
|
+
}),
|
851
|
+
transformResponse: (response) => response.data,
|
852
|
+
invalidatesTags: (_result, _error, { uid }) => [
|
853
|
+
{ type: "ContentTypesConfiguration", id: uid },
|
854
|
+
{ type: "ContentTypeSettings", id: "LIST" },
|
855
|
+
// Is this necessary?
|
856
|
+
{ type: "InitialData" }
|
857
|
+
]
|
858
|
+
})
|
859
|
+
})
|
860
|
+
});
|
861
|
+
const {
|
862
|
+
useGetContentTypeConfigurationQuery,
|
863
|
+
useGetAllContentTypeSettingsQuery,
|
864
|
+
useUpdateContentTypeConfigurationMutation
|
865
|
+
} = contentTypesApi;
|
866
|
+
const checkIfAttributeIsDisplayable = (attribute) => {
|
867
|
+
const { type } = attribute;
|
868
|
+
if (type === "relation") {
|
869
|
+
return !attribute.relation.toLowerCase().includes("morph");
|
870
|
+
}
|
871
|
+
return !["json", "dynamiczone", "richtext", "password", "blocks"].includes(type) && !!type;
|
872
|
+
};
|
873
|
+
const getMainField = (attribute, mainFieldName, { schemas, components }) => {
|
874
|
+
if (!mainFieldName) {
|
875
|
+
return void 0;
|
876
|
+
}
|
877
|
+
const mainFieldType = attribute.type === "component" ? components[attribute.component].attributes[mainFieldName].type : (
|
878
|
+
// @ts-expect-error – `targetModel` does exist on the attribute for a relation.
|
879
|
+
schemas.find((schema) => schema.uid === attribute.targetModel)?.attributes[mainFieldName].type
|
880
|
+
);
|
881
|
+
return {
|
882
|
+
name: mainFieldName,
|
883
|
+
type: mainFieldType ?? "string"
|
884
|
+
};
|
885
|
+
};
|
886
|
+
const DEFAULT_SETTINGS = {
|
887
|
+
bulkable: false,
|
888
|
+
filterable: false,
|
889
|
+
searchable: false,
|
890
|
+
pagination: false,
|
891
|
+
defaultSortBy: "",
|
892
|
+
defaultSortOrder: "asc",
|
893
|
+
mainField: "id",
|
894
|
+
pageSize: 10
|
895
|
+
};
|
896
|
+
const useDocumentLayout = (model) => {
|
897
|
+
const { schema, components } = useDocument({ model, collectionType: "" }, { skip: true });
|
898
|
+
const [{ query }] = useQueryParams();
|
899
|
+
const runHookWaterfall = useStrapiApp("useDocumentLayout", (state) => state.runHookWaterfall);
|
763
900
|
const { toggleNotification } = useNotification();
|
764
901
|
const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
|
902
|
+
const { isLoading: isLoadingSchemas, schemas } = useContentTypeSchema();
|
765
903
|
const {
|
766
|
-
|
767
|
-
isLoading:
|
768
|
-
|
769
|
-
|
770
|
-
} =
|
771
|
-
|
772
|
-
skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
|
773
|
-
});
|
774
|
-
const { components, schema, isLoading: isLoadingSchema } = useContentTypeSchema(args.model);
|
904
|
+
data,
|
905
|
+
isLoading: isLoadingConfigs,
|
906
|
+
error,
|
907
|
+
isFetching: isFetchingConfigs
|
908
|
+
} = useGetContentTypeConfigurationQuery(model);
|
909
|
+
const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
|
775
910
|
React.useEffect(() => {
|
776
911
|
if (error) {
|
777
912
|
toggleNotification({
|
@@ -779,390 +914,646 @@ const useDocument = (args, opts) => {
|
|
779
914
|
message: formatAPIError(error)
|
780
915
|
});
|
781
916
|
}
|
782
|
-
}, [
|
783
|
-
const
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
(document) => {
|
791
|
-
if (!validationSchema) {
|
792
|
-
throw new Error(
|
793
|
-
"There is no validation schema generated, this is likely due to the schema not being loaded yet."
|
794
|
-
);
|
795
|
-
}
|
796
|
-
try {
|
797
|
-
validationSchema.validateSync(document, { abortEarly: false, strict: true });
|
798
|
-
return null;
|
799
|
-
} catch (error2) {
|
800
|
-
if (error2 instanceof ValidationError) {
|
801
|
-
return getYupValidationErrors(error2);
|
802
|
-
}
|
803
|
-
throw error2;
|
804
|
-
}
|
917
|
+
}, [error, formatAPIError, toggleNotification]);
|
918
|
+
const editLayout = React.useMemo(
|
919
|
+
() => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
|
920
|
+
layout: [],
|
921
|
+
components: {},
|
922
|
+
metadatas: {},
|
923
|
+
options: {},
|
924
|
+
settings: DEFAULT_SETTINGS
|
805
925
|
},
|
806
|
-
[
|
926
|
+
[data, isLoading, schemas, schema, components]
|
927
|
+
);
|
928
|
+
const listLayout = React.useMemo(() => {
|
929
|
+
return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
|
930
|
+
layout: [],
|
931
|
+
metadatas: {},
|
932
|
+
options: {},
|
933
|
+
settings: DEFAULT_SETTINGS
|
934
|
+
};
|
935
|
+
}, [data, isLoading, schemas, schema, components]);
|
936
|
+
const { layout: edit } = React.useMemo(
|
937
|
+
() => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
|
938
|
+
layout: editLayout,
|
939
|
+
query
|
940
|
+
}),
|
941
|
+
[editLayout, query, runHookWaterfall]
|
807
942
|
);
|
808
|
-
const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
|
809
943
|
return {
|
810
|
-
|
811
|
-
document: data?.data,
|
812
|
-
meta: data?.meta,
|
944
|
+
error,
|
813
945
|
isLoading,
|
814
|
-
|
815
|
-
|
816
|
-
};
|
817
|
-
};
|
818
|
-
const useDoc = () => {
|
819
|
-
const { id, slug, collectionType, origin } = useParams();
|
820
|
-
const [{ query }] = useQueryParams();
|
821
|
-
const params = React.useMemo(() => buildValidParams(query), [query]);
|
822
|
-
if (!collectionType) {
|
823
|
-
throw new Error("Could not find collectionType in url params");
|
824
|
-
}
|
825
|
-
if (!slug) {
|
826
|
-
throw new Error("Could not find model in url params");
|
827
|
-
}
|
828
|
-
return {
|
829
|
-
collectionType,
|
830
|
-
model: slug,
|
831
|
-
id: origin || id === "create" ? void 0 : id,
|
832
|
-
...useDocument(
|
833
|
-
{ documentId: origin || id, model: slug, collectionType, params },
|
834
|
-
{
|
835
|
-
skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
|
836
|
-
}
|
837
|
-
)
|
946
|
+
edit,
|
947
|
+
list: listLayout
|
838
948
|
};
|
839
949
|
};
|
840
|
-
const
|
841
|
-
|
842
|
-
|
843
|
-
}
|
844
|
-
return Object.keys(trad).reduce((acc, current) => {
|
845
|
-
acc[`${pluginId}.${current}`] = trad[current];
|
846
|
-
return acc;
|
847
|
-
}, {});
|
848
|
-
};
|
849
|
-
const getTranslation = (id) => `content-manager.${id}`;
|
850
|
-
const DEFAULT_UNEXPECTED_ERROR_MSG = {
|
851
|
-
id: "notification.error",
|
852
|
-
defaultMessage: "An error occurred, please try again"
|
950
|
+
const useDocLayout = () => {
|
951
|
+
const { model } = useDoc();
|
952
|
+
return useDocumentLayout(model);
|
853
953
|
};
|
854
|
-
const
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
const
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
return { error: res.error };
|
876
|
-
}
|
877
|
-
toggleNotification({
|
878
|
-
type: "success",
|
879
|
-
message: formatMessage({
|
880
|
-
id: getTranslation("success.record.delete"),
|
881
|
-
defaultMessage: "Deleted document"
|
882
|
-
})
|
883
|
-
});
|
884
|
-
trackUsage("didDeleteEntry", trackerProperty);
|
885
|
-
return res.data;
|
886
|
-
} catch (err) {
|
887
|
-
toggleNotification({
|
888
|
-
type: "danger",
|
889
|
-
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
890
|
-
});
|
891
|
-
trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
|
892
|
-
throw err;
|
954
|
+
const formatEditLayout = (data, {
|
955
|
+
schemas,
|
956
|
+
schema,
|
957
|
+
components
|
958
|
+
}) => {
|
959
|
+
let currentPanelIndex = 0;
|
960
|
+
const panelledEditAttributes = convertEditLayoutToFieldLayouts(
|
961
|
+
data.contentType.layouts.edit,
|
962
|
+
schema?.attributes,
|
963
|
+
data.contentType.metadatas,
|
964
|
+
{ configurations: data.components, schemas: components },
|
965
|
+
schemas
|
966
|
+
).reduce((panels, row) => {
|
967
|
+
if (row.some((field) => field.type === "dynamiczone")) {
|
968
|
+
panels.push([row]);
|
969
|
+
currentPanelIndex += 2;
|
970
|
+
} else {
|
971
|
+
if (!panels[currentPanelIndex]) {
|
972
|
+
panels.push([row]);
|
973
|
+
} else {
|
974
|
+
panels[currentPanelIndex].push(row);
|
893
975
|
}
|
894
|
-
}
|
895
|
-
|
896
|
-
);
|
897
|
-
const
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
message: formatAPIError(res.error)
|
911
|
-
});
|
912
|
-
return { error: res.error };
|
976
|
+
}
|
977
|
+
return panels;
|
978
|
+
}, []);
|
979
|
+
const componentEditAttributes = Object.entries(data.components).reduce(
|
980
|
+
(acc, [uid, configuration]) => {
|
981
|
+
acc[uid] = {
|
982
|
+
layout: convertEditLayoutToFieldLayouts(
|
983
|
+
configuration.layouts.edit,
|
984
|
+
components[uid].attributes,
|
985
|
+
configuration.metadatas,
|
986
|
+
{ configurations: data.components, schemas: components }
|
987
|
+
),
|
988
|
+
settings: {
|
989
|
+
...configuration.settings,
|
990
|
+
icon: components[uid].info.icon,
|
991
|
+
displayName: components[uid].info.displayName
|
913
992
|
}
|
914
|
-
|
915
|
-
|
916
|
-
title: formatMessage({
|
917
|
-
id: getTranslation("success.records.delete"),
|
918
|
-
defaultMessage: "Successfully deleted."
|
919
|
-
}),
|
920
|
-
message: ""
|
921
|
-
});
|
922
|
-
trackUsage("didBulkDeleteEntries");
|
923
|
-
return res.data;
|
924
|
-
} catch (err) {
|
925
|
-
toggleNotification({
|
926
|
-
type: "danger",
|
927
|
-
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
928
|
-
});
|
929
|
-
trackUsage("didNotBulkDeleteEntries");
|
930
|
-
throw err;
|
931
|
-
}
|
993
|
+
};
|
994
|
+
return acc;
|
932
995
|
},
|
933
|
-
|
996
|
+
{}
|
934
997
|
);
|
935
|
-
const
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
model,
|
942
|
-
documentId,
|
943
|
-
params
|
944
|
-
});
|
945
|
-
if ("error" in res) {
|
946
|
-
toggleNotification({
|
947
|
-
type: "danger",
|
948
|
-
message: formatAPIError(res.error)
|
949
|
-
});
|
950
|
-
return { error: res.error };
|
951
|
-
}
|
952
|
-
toggleNotification({
|
953
|
-
type: "success",
|
954
|
-
message: formatMessage({
|
955
|
-
id: "content-manager.success.record.discard",
|
956
|
-
defaultMessage: "Changes discarded"
|
957
|
-
})
|
958
|
-
});
|
959
|
-
return res.data;
|
960
|
-
} catch (err) {
|
961
|
-
toggleNotification({
|
962
|
-
type: "danger",
|
963
|
-
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
964
|
-
});
|
965
|
-
throw err;
|
966
|
-
}
|
998
|
+
const editMetadatas = Object.entries(data.contentType.metadatas).reduce(
|
999
|
+
(acc, [attribute, metadata]) => {
|
1000
|
+
return {
|
1001
|
+
...acc,
|
1002
|
+
[attribute]: metadata.edit
|
1003
|
+
};
|
967
1004
|
},
|
968
|
-
|
1005
|
+
{}
|
969
1006
|
);
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
model,
|
978
|
-
documentId,
|
979
|
-
data,
|
980
|
-
params
|
981
|
-
});
|
982
|
-
if ("error" in res) {
|
983
|
-
toggleNotification({ type: "danger", message: formatAPIError(res.error) });
|
984
|
-
return { error: res.error };
|
985
|
-
}
|
986
|
-
trackUsage("didPublishEntry");
|
987
|
-
toggleNotification({
|
988
|
-
type: "success",
|
989
|
-
message: formatMessage({
|
990
|
-
id: getTranslation("success.record.publish"),
|
991
|
-
defaultMessage: "Published document"
|
992
|
-
})
|
993
|
-
});
|
994
|
-
return res.data;
|
995
|
-
} catch (err) {
|
996
|
-
toggleNotification({
|
997
|
-
type: "danger",
|
998
|
-
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
999
|
-
});
|
1000
|
-
throw err;
|
1001
|
-
}
|
1007
|
+
return {
|
1008
|
+
layout: panelledEditAttributes,
|
1009
|
+
components: componentEditAttributes,
|
1010
|
+
metadatas: editMetadatas,
|
1011
|
+
settings: {
|
1012
|
+
...data.contentType.settings,
|
1013
|
+
displayName: schema?.info.displayName
|
1002
1014
|
},
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
return { error: res.error };
|
1017
|
-
}
|
1018
|
-
toggleNotification({
|
1019
|
-
type: "success",
|
1020
|
-
message: formatMessage({
|
1021
|
-
id: getTranslation("success.record.publish"),
|
1022
|
-
defaultMessage: "Published document"
|
1023
|
-
})
|
1024
|
-
});
|
1025
|
-
return res.data;
|
1026
|
-
} catch (err) {
|
1027
|
-
toggleNotification({
|
1028
|
-
type: "danger",
|
1029
|
-
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
1030
|
-
});
|
1031
|
-
throw err;
|
1015
|
+
options: {
|
1016
|
+
...schema?.options,
|
1017
|
+
...schema?.pluginOptions,
|
1018
|
+
...data.contentType.options
|
1019
|
+
}
|
1020
|
+
};
|
1021
|
+
};
|
1022
|
+
const convertEditLayoutToFieldLayouts = (rows, attributes = {}, metadatas, components, schemas = []) => {
|
1023
|
+
return rows.map(
|
1024
|
+
(row) => row.map((field) => {
|
1025
|
+
const attribute = attributes[field.name];
|
1026
|
+
if (!attribute) {
|
1027
|
+
return null;
|
1032
1028
|
}
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1029
|
+
const { edit: metadata } = metadatas[field.name];
|
1030
|
+
const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
|
1031
|
+
return {
|
1032
|
+
attribute,
|
1033
|
+
disabled: !metadata.editable,
|
1034
|
+
hint: metadata.description,
|
1035
|
+
label: metadata.label ?? "",
|
1036
|
+
name: field.name,
|
1037
|
+
// @ts-expect-error – mainField does exist on the metadata for a relation.
|
1038
|
+
mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
|
1039
|
+
schemas,
|
1040
|
+
components: components?.schemas ?? {}
|
1041
|
+
}),
|
1042
|
+
placeholder: metadata.placeholder ?? "",
|
1043
|
+
required: attribute.required ?? false,
|
1044
|
+
size: field.size,
|
1045
|
+
unique: "unique" in attribute ? attribute.unique : false,
|
1046
|
+
visible: metadata.visible ?? true,
|
1047
|
+
type: attribute.type
|
1048
|
+
};
|
1049
|
+
}).filter((field) => field !== null)
|
1041
1050
|
);
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1051
|
+
};
|
1052
|
+
const formatListLayout = (data, {
|
1053
|
+
schemas,
|
1054
|
+
schema,
|
1055
|
+
components
|
1056
|
+
}) => {
|
1057
|
+
const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
|
1058
|
+
(acc, [attribute, metadata]) => {
|
1059
|
+
return {
|
1060
|
+
...acc,
|
1061
|
+
[attribute]: metadata.list
|
1062
|
+
};
|
1063
|
+
},
|
1064
|
+
{}
|
1065
|
+
);
|
1066
|
+
const listAttributes = convertListLayoutToFieldLayouts(
|
1067
|
+
data.contentType.layouts.list,
|
1068
|
+
schema?.attributes,
|
1069
|
+
listMetadatas,
|
1070
|
+
{ configurations: data.components, schemas: components },
|
1071
|
+
schemas
|
1072
|
+
);
|
1073
|
+
return {
|
1074
|
+
layout: listAttributes,
|
1075
|
+
settings: { ...data.contentType.settings, displayName: schema?.info.displayName },
|
1076
|
+
metadatas: listMetadatas,
|
1077
|
+
options: {
|
1078
|
+
...schema?.options,
|
1079
|
+
...schema?.pluginOptions,
|
1080
|
+
...data.contentType.options
|
1081
|
+
}
|
1082
|
+
};
|
1083
|
+
};
|
1084
|
+
const convertListLayoutToFieldLayouts = (columns, attributes = {}, metadatas, components, schemas = []) => {
|
1085
|
+
return columns.map((name) => {
|
1086
|
+
const attribute = attributes[name];
|
1087
|
+
if (!attribute) {
|
1088
|
+
return null;
|
1089
|
+
}
|
1090
|
+
const metadata = metadatas[name];
|
1091
|
+
const settings = attribute.type === "component" && components ? components.configurations[attribute.component].settings : {};
|
1092
|
+
return {
|
1093
|
+
attribute,
|
1094
|
+
label: metadata.label ?? "",
|
1095
|
+
mainField: getMainField(attribute, metadata.mainField || settings.mainField, {
|
1096
|
+
schemas,
|
1097
|
+
components: components?.schemas ?? {}
|
1098
|
+
}),
|
1099
|
+
name,
|
1100
|
+
searchable: metadata.searchable ?? true,
|
1101
|
+
sortable: metadata.sortable ?? true
|
1102
|
+
};
|
1103
|
+
}).filter((field) => field !== null);
|
1104
|
+
};
|
1105
|
+
const useDocument = (args, opts) => {
|
1106
|
+
const { toggleNotification } = useNotification();
|
1107
|
+
const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
|
1108
|
+
const {
|
1109
|
+
currentData: data,
|
1110
|
+
isLoading: isLoadingDocument,
|
1111
|
+
isFetching: isFetchingDocument,
|
1112
|
+
error
|
1113
|
+
} = useGetDocumentQuery(args, {
|
1114
|
+
...opts,
|
1115
|
+
skip: !args.documentId && args.collectionType !== SINGLE_TYPES || opts?.skip
|
1116
|
+
});
|
1117
|
+
const {
|
1118
|
+
components,
|
1119
|
+
schema,
|
1120
|
+
schemas,
|
1121
|
+
isLoading: isLoadingSchema
|
1122
|
+
} = useContentTypeSchema(args.model);
|
1123
|
+
React.useEffect(() => {
|
1124
|
+
if (error) {
|
1125
|
+
toggleNotification({
|
1126
|
+
type: "danger",
|
1127
|
+
message: formatAPIError(error)
|
1128
|
+
});
|
1129
|
+
}
|
1130
|
+
}, [toggleNotification, error, formatAPIError, args.collectionType]);
|
1131
|
+
const validationSchema = React.useMemo(() => {
|
1132
|
+
if (!schema) {
|
1133
|
+
return null;
|
1134
|
+
}
|
1135
|
+
return createYupSchema(schema.attributes, components);
|
1136
|
+
}, [schema, components]);
|
1137
|
+
const validate = React.useCallback(
|
1138
|
+
(document) => {
|
1139
|
+
if (!validationSchema) {
|
1140
|
+
throw new Error(
|
1141
|
+
"There is no validation schema generated, this is likely due to the schema not being loaded yet."
|
1142
|
+
);
|
1143
|
+
}
|
1045
1144
|
try {
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
data,
|
1052
|
-
params
|
1053
|
-
});
|
1054
|
-
if ("error" in res) {
|
1055
|
-
toggleNotification({ type: "danger", message: formatAPIError(res.error) });
|
1056
|
-
trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
|
1057
|
-
return { error: res.error };
|
1145
|
+
validationSchema.validateSync(document, { abortEarly: false, strict: true });
|
1146
|
+
return null;
|
1147
|
+
} catch (error2) {
|
1148
|
+
if (error2 instanceof ValidationError) {
|
1149
|
+
return getYupValidationErrors(error2);
|
1058
1150
|
}
|
1059
|
-
|
1060
|
-
toggleNotification({
|
1061
|
-
type: "success",
|
1062
|
-
message: formatMessage({
|
1063
|
-
id: getTranslation("success.record.save"),
|
1064
|
-
defaultMessage: "Saved document"
|
1065
|
-
})
|
1066
|
-
});
|
1067
|
-
return res.data;
|
1068
|
-
} catch (err) {
|
1069
|
-
trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
|
1070
|
-
toggleNotification({
|
1071
|
-
type: "danger",
|
1072
|
-
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
1073
|
-
});
|
1074
|
-
throw err;
|
1151
|
+
throw error2;
|
1075
1152
|
}
|
1076
1153
|
},
|
1077
|
-
[
|
1154
|
+
[validationSchema]
|
1078
1155
|
);
|
1079
|
-
const
|
1080
|
-
const
|
1081
|
-
|
1156
|
+
const isLoading = isLoadingDocument || isFetchingDocument || isLoadingSchema;
|
1157
|
+
const hasError = !!error;
|
1158
|
+
return {
|
1159
|
+
components,
|
1160
|
+
document: data?.data,
|
1161
|
+
meta: data?.meta,
|
1162
|
+
isLoading,
|
1163
|
+
hasError,
|
1164
|
+
schema,
|
1165
|
+
schemas,
|
1166
|
+
validate
|
1167
|
+
};
|
1168
|
+
};
|
1169
|
+
const useDoc = () => {
|
1170
|
+
const { id, slug, collectionType, origin } = useParams();
|
1171
|
+
const [{ query }] = useQueryParams();
|
1172
|
+
const params = React.useMemo(() => buildValidParams(query), [query]);
|
1173
|
+
if (!collectionType) {
|
1174
|
+
throw new Error("Could not find collectionType in url params");
|
1175
|
+
}
|
1176
|
+
if (!slug) {
|
1177
|
+
throw new Error("Could not find model in url params");
|
1178
|
+
}
|
1179
|
+
const document = useDocument(
|
1180
|
+
{ documentId: origin || id, model: slug, collectionType, params },
|
1181
|
+
{
|
1182
|
+
skip: id === "create" || !origin && !id && collectionType !== SINGLE_TYPES
|
1183
|
+
}
|
1184
|
+
);
|
1185
|
+
const returnId = origin || id === "create" ? void 0 : id;
|
1186
|
+
return {
|
1187
|
+
collectionType,
|
1188
|
+
model: slug,
|
1189
|
+
id: returnId,
|
1190
|
+
...document
|
1191
|
+
};
|
1192
|
+
};
|
1193
|
+
const useContentManagerContext = () => {
|
1194
|
+
const {
|
1195
|
+
collectionType,
|
1196
|
+
model,
|
1197
|
+
id,
|
1198
|
+
components,
|
1199
|
+
isLoading: isLoadingDoc,
|
1200
|
+
schema,
|
1201
|
+
schemas
|
1202
|
+
} = useDoc();
|
1203
|
+
const layout = useDocumentLayout(model);
|
1204
|
+
const form = useForm("useContentManagerContext", (state) => state);
|
1205
|
+
const isSingleType = collectionType === SINGLE_TYPES;
|
1206
|
+
const slug = model;
|
1207
|
+
const isCreatingEntry = id === "create";
|
1208
|
+
useContentTypeSchema();
|
1209
|
+
const isLoading = isLoadingDoc || layout.isLoading;
|
1210
|
+
const error = layout.error;
|
1211
|
+
return {
|
1212
|
+
error,
|
1213
|
+
isLoading,
|
1214
|
+
// Base metadata
|
1215
|
+
model,
|
1216
|
+
collectionType,
|
1217
|
+
id,
|
1218
|
+
slug,
|
1219
|
+
isCreatingEntry,
|
1220
|
+
isSingleType,
|
1221
|
+
hasDraftAndPublish: schema?.options?.draftAndPublish ?? false,
|
1222
|
+
// All schema infos
|
1223
|
+
components,
|
1224
|
+
contentType: schema,
|
1225
|
+
contentTypes: schemas,
|
1226
|
+
// Form state
|
1227
|
+
form,
|
1228
|
+
// layout infos
|
1229
|
+
layout
|
1230
|
+
};
|
1231
|
+
};
|
1232
|
+
const prefixPluginTranslations = (trad, pluginId) => {
|
1233
|
+
return Object.keys(trad).reduce((acc, current) => {
|
1234
|
+
acc[`${pluginId}.${current}`] = trad[current];
|
1235
|
+
return acc;
|
1236
|
+
}, {});
|
1237
|
+
};
|
1238
|
+
const getTranslation = (id) => `content-manager.${id}`;
|
1239
|
+
const DEFAULT_UNEXPECTED_ERROR_MSG = {
|
1240
|
+
id: "notification.error",
|
1241
|
+
defaultMessage: "An error occurred, please try again"
|
1242
|
+
};
|
1243
|
+
const useDocumentActions = () => {
|
1244
|
+
const { toggleNotification } = useNotification();
|
1245
|
+
const { formatMessage } = useIntl();
|
1246
|
+
const { trackUsage } = useTracking();
|
1247
|
+
const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
|
1248
|
+
const navigate = useNavigate();
|
1249
|
+
const setCurrentStep = useGuidedTour("useDocumentActions", (state) => state.setCurrentStep);
|
1250
|
+
const [deleteDocument] = useDeleteDocumentMutation();
|
1251
|
+
const _delete = React.useCallback(
|
1252
|
+
async ({ collectionType, model, documentId, params }, trackerProperty) => {
|
1082
1253
|
try {
|
1083
|
-
trackUsage("
|
1084
|
-
const res = await
|
1254
|
+
trackUsage("willDeleteEntry", trackerProperty);
|
1255
|
+
const res = await deleteDocument({
|
1085
1256
|
collectionType,
|
1086
1257
|
model,
|
1087
1258
|
documentId,
|
1088
|
-
params
|
1089
|
-
data: {
|
1090
|
-
discardDraft
|
1091
|
-
}
|
1259
|
+
params
|
1092
1260
|
});
|
1093
1261
|
if ("error" in res) {
|
1094
|
-
toggleNotification({
|
1262
|
+
toggleNotification({
|
1263
|
+
type: "danger",
|
1264
|
+
message: formatAPIError(res.error)
|
1265
|
+
});
|
1095
1266
|
return { error: res.error };
|
1096
1267
|
}
|
1097
|
-
trackUsage("didUnpublishEntry");
|
1098
1268
|
toggleNotification({
|
1099
1269
|
type: "success",
|
1100
1270
|
message: formatMessage({
|
1101
|
-
id: getTranslation("success.record.
|
1102
|
-
defaultMessage: "
|
1271
|
+
id: getTranslation("success.record.delete"),
|
1272
|
+
defaultMessage: "Deleted document"
|
1103
1273
|
})
|
1104
1274
|
});
|
1275
|
+
trackUsage("didDeleteEntry", trackerProperty);
|
1105
1276
|
return res.data;
|
1106
1277
|
} catch (err) {
|
1107
1278
|
toggleNotification({
|
1108
1279
|
type: "danger",
|
1109
1280
|
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
1110
1281
|
});
|
1282
|
+
trackUsage("didNotDeleteEntry", { error: err, ...trackerProperty });
|
1111
1283
|
throw err;
|
1112
1284
|
}
|
1113
1285
|
},
|
1114
|
-
[trackUsage,
|
1286
|
+
[trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError]
|
1115
1287
|
);
|
1116
|
-
const [
|
1117
|
-
const
|
1288
|
+
const [deleteManyDocuments] = useDeleteManyDocumentsMutation();
|
1289
|
+
const deleteMany = React.useCallback(
|
1118
1290
|
async ({ model, documentIds, params }) => {
|
1119
1291
|
try {
|
1120
|
-
trackUsage("
|
1121
|
-
const res = await
|
1292
|
+
trackUsage("willBulkDeleteEntries");
|
1293
|
+
const res = await deleteManyDocuments({
|
1122
1294
|
model,
|
1123
1295
|
documentIds,
|
1124
1296
|
params
|
1125
1297
|
});
|
1126
1298
|
if ("error" in res) {
|
1127
|
-
toggleNotification({
|
1299
|
+
toggleNotification({
|
1300
|
+
type: "danger",
|
1301
|
+
message: formatAPIError(res.error)
|
1302
|
+
});
|
1128
1303
|
return { error: res.error };
|
1129
1304
|
}
|
1130
|
-
trackUsage("didBulkUnpublishEntries");
|
1131
1305
|
toggleNotification({
|
1132
1306
|
type: "success",
|
1133
1307
|
title: formatMessage({
|
1134
|
-
id: getTranslation("success.records.
|
1135
|
-
defaultMessage: "Successfully
|
1308
|
+
id: getTranslation("success.records.delete"),
|
1309
|
+
defaultMessage: "Successfully deleted."
|
1136
1310
|
}),
|
1137
1311
|
message: ""
|
1138
1312
|
});
|
1313
|
+
trackUsage("didBulkDeleteEntries");
|
1139
1314
|
return res.data;
|
1140
1315
|
} catch (err) {
|
1141
1316
|
toggleNotification({
|
1142
1317
|
type: "danger",
|
1143
1318
|
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
1144
1319
|
});
|
1145
|
-
trackUsage("
|
1320
|
+
trackUsage("didNotBulkDeleteEntries");
|
1146
1321
|
throw err;
|
1147
1322
|
}
|
1148
1323
|
},
|
1149
|
-
[trackUsage,
|
1324
|
+
[trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError]
|
1150
1325
|
);
|
1151
|
-
const [
|
1152
|
-
const
|
1153
|
-
async ({ model, params }
|
1326
|
+
const [discardDocument] = useDiscardDocumentMutation();
|
1327
|
+
const discard = React.useCallback(
|
1328
|
+
async ({ collectionType, model, documentId, params }) => {
|
1154
1329
|
try {
|
1155
|
-
const res = await
|
1330
|
+
const res = await discardDocument({
|
1331
|
+
collectionType,
|
1156
1332
|
model,
|
1157
|
-
|
1333
|
+
documentId,
|
1158
1334
|
params
|
1159
1335
|
});
|
1160
1336
|
if ("error" in res) {
|
1161
|
-
toggleNotification({
|
1162
|
-
|
1337
|
+
toggleNotification({
|
1338
|
+
type: "danger",
|
1339
|
+
message: formatAPIError(res.error)
|
1340
|
+
});
|
1163
1341
|
return { error: res.error };
|
1164
1342
|
}
|
1165
|
-
|
1343
|
+
toggleNotification({
|
1344
|
+
type: "success",
|
1345
|
+
message: formatMessage({
|
1346
|
+
id: "content-manager.success.record.discard",
|
1347
|
+
defaultMessage: "Changes discarded"
|
1348
|
+
})
|
1349
|
+
});
|
1350
|
+
return res.data;
|
1351
|
+
} catch (err) {
|
1352
|
+
toggleNotification({
|
1353
|
+
type: "danger",
|
1354
|
+
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
1355
|
+
});
|
1356
|
+
throw err;
|
1357
|
+
}
|
1358
|
+
},
|
1359
|
+
[discardDocument, formatAPIError, formatMessage, toggleNotification]
|
1360
|
+
);
|
1361
|
+
const [publishDocument] = usePublishDocumentMutation();
|
1362
|
+
const publish = React.useCallback(
|
1363
|
+
async ({ collectionType, model, documentId, params }, data) => {
|
1364
|
+
try {
|
1365
|
+
trackUsage("willPublishEntry");
|
1366
|
+
const res = await publishDocument({
|
1367
|
+
collectionType,
|
1368
|
+
model,
|
1369
|
+
documentId,
|
1370
|
+
data,
|
1371
|
+
params
|
1372
|
+
});
|
1373
|
+
if ("error" in res) {
|
1374
|
+
toggleNotification({ type: "danger", message: formatAPIError(res.error) });
|
1375
|
+
return { error: res.error };
|
1376
|
+
}
|
1377
|
+
trackUsage("didPublishEntry");
|
1378
|
+
toggleNotification({
|
1379
|
+
type: "success",
|
1380
|
+
message: formatMessage({
|
1381
|
+
id: getTranslation("success.record.publish"),
|
1382
|
+
defaultMessage: "Published document"
|
1383
|
+
})
|
1384
|
+
});
|
1385
|
+
return res.data;
|
1386
|
+
} catch (err) {
|
1387
|
+
toggleNotification({
|
1388
|
+
type: "danger",
|
1389
|
+
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
1390
|
+
});
|
1391
|
+
throw err;
|
1392
|
+
}
|
1393
|
+
},
|
1394
|
+
[trackUsage, publishDocument, toggleNotification, formatMessage, formatAPIError]
|
1395
|
+
);
|
1396
|
+
const [publishManyDocuments] = usePublishManyDocumentsMutation();
|
1397
|
+
const publishMany = React.useCallback(
|
1398
|
+
async ({ model, documentIds, params }) => {
|
1399
|
+
try {
|
1400
|
+
const res = await publishManyDocuments({
|
1401
|
+
model,
|
1402
|
+
documentIds,
|
1403
|
+
params
|
1404
|
+
});
|
1405
|
+
if ("error" in res) {
|
1406
|
+
toggleNotification({ type: "danger", message: formatAPIError(res.error) });
|
1407
|
+
return { error: res.error };
|
1408
|
+
}
|
1409
|
+
toggleNotification({
|
1410
|
+
type: "success",
|
1411
|
+
message: formatMessage({
|
1412
|
+
id: getTranslation("success.record.publish"),
|
1413
|
+
defaultMessage: "Published document"
|
1414
|
+
})
|
1415
|
+
});
|
1416
|
+
return res.data;
|
1417
|
+
} catch (err) {
|
1418
|
+
toggleNotification({
|
1419
|
+
type: "danger",
|
1420
|
+
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
1421
|
+
});
|
1422
|
+
throw err;
|
1423
|
+
}
|
1424
|
+
},
|
1425
|
+
[
|
1426
|
+
// trackUsage,
|
1427
|
+
publishManyDocuments,
|
1428
|
+
toggleNotification,
|
1429
|
+
formatMessage,
|
1430
|
+
formatAPIError
|
1431
|
+
]
|
1432
|
+
);
|
1433
|
+
const [updateDocument] = useUpdateDocumentMutation();
|
1434
|
+
const update = React.useCallback(
|
1435
|
+
async ({ collectionType, model, documentId, params }, data, trackerProperty) => {
|
1436
|
+
try {
|
1437
|
+
trackUsage("willEditEntry", trackerProperty);
|
1438
|
+
const res = await updateDocument({
|
1439
|
+
collectionType,
|
1440
|
+
model,
|
1441
|
+
documentId,
|
1442
|
+
data,
|
1443
|
+
params
|
1444
|
+
});
|
1445
|
+
if ("error" in res) {
|
1446
|
+
toggleNotification({ type: "danger", message: formatAPIError(res.error) });
|
1447
|
+
trackUsage("didNotEditEntry", { error: res.error, ...trackerProperty });
|
1448
|
+
return { error: res.error };
|
1449
|
+
}
|
1450
|
+
trackUsage("didEditEntry", trackerProperty);
|
1451
|
+
toggleNotification({
|
1452
|
+
type: "success",
|
1453
|
+
message: formatMessage({
|
1454
|
+
id: getTranslation("success.record.save"),
|
1455
|
+
defaultMessage: "Saved document"
|
1456
|
+
})
|
1457
|
+
});
|
1458
|
+
return res.data;
|
1459
|
+
} catch (err) {
|
1460
|
+
trackUsage("didNotEditEntry", { error: err, ...trackerProperty });
|
1461
|
+
toggleNotification({
|
1462
|
+
type: "danger",
|
1463
|
+
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
1464
|
+
});
|
1465
|
+
throw err;
|
1466
|
+
}
|
1467
|
+
},
|
1468
|
+
[trackUsage, updateDocument, toggleNotification, formatMessage, formatAPIError]
|
1469
|
+
);
|
1470
|
+
const [unpublishDocument] = useUnpublishDocumentMutation();
|
1471
|
+
const unpublish = React.useCallback(
|
1472
|
+
async ({ collectionType, model, documentId, params }, discardDraft = false) => {
|
1473
|
+
try {
|
1474
|
+
trackUsage("willUnpublishEntry");
|
1475
|
+
const res = await unpublishDocument({
|
1476
|
+
collectionType,
|
1477
|
+
model,
|
1478
|
+
documentId,
|
1479
|
+
params,
|
1480
|
+
data: {
|
1481
|
+
discardDraft
|
1482
|
+
}
|
1483
|
+
});
|
1484
|
+
if ("error" in res) {
|
1485
|
+
toggleNotification({ type: "danger", message: formatAPIError(res.error) });
|
1486
|
+
return { error: res.error };
|
1487
|
+
}
|
1488
|
+
trackUsage("didUnpublishEntry");
|
1489
|
+
toggleNotification({
|
1490
|
+
type: "success",
|
1491
|
+
message: formatMessage({
|
1492
|
+
id: getTranslation("success.record.unpublish"),
|
1493
|
+
defaultMessage: "Unpublished document"
|
1494
|
+
})
|
1495
|
+
});
|
1496
|
+
return res.data;
|
1497
|
+
} catch (err) {
|
1498
|
+
toggleNotification({
|
1499
|
+
type: "danger",
|
1500
|
+
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
1501
|
+
});
|
1502
|
+
throw err;
|
1503
|
+
}
|
1504
|
+
},
|
1505
|
+
[trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError]
|
1506
|
+
);
|
1507
|
+
const [unpublishManyDocuments] = useUnpublishManyDocumentsMutation();
|
1508
|
+
const unpublishMany = React.useCallback(
|
1509
|
+
async ({ model, documentIds, params }) => {
|
1510
|
+
try {
|
1511
|
+
trackUsage("willBulkUnpublishEntries");
|
1512
|
+
const res = await unpublishManyDocuments({
|
1513
|
+
model,
|
1514
|
+
documentIds,
|
1515
|
+
params
|
1516
|
+
});
|
1517
|
+
if ("error" in res) {
|
1518
|
+
toggleNotification({ type: "danger", message: formatAPIError(res.error) });
|
1519
|
+
return { error: res.error };
|
1520
|
+
}
|
1521
|
+
trackUsage("didBulkUnpublishEntries");
|
1522
|
+
toggleNotification({
|
1523
|
+
type: "success",
|
1524
|
+
title: formatMessage({
|
1525
|
+
id: getTranslation("success.records.unpublish"),
|
1526
|
+
defaultMessage: "Successfully unpublished."
|
1527
|
+
}),
|
1528
|
+
message: ""
|
1529
|
+
});
|
1530
|
+
return res.data;
|
1531
|
+
} catch (err) {
|
1532
|
+
toggleNotification({
|
1533
|
+
type: "danger",
|
1534
|
+
message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG)
|
1535
|
+
});
|
1536
|
+
trackUsage("didNotBulkUnpublishEntries");
|
1537
|
+
throw err;
|
1538
|
+
}
|
1539
|
+
},
|
1540
|
+
[trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError]
|
1541
|
+
);
|
1542
|
+
const [createDocument] = useCreateDocumentMutation();
|
1543
|
+
const create = React.useCallback(
|
1544
|
+
async ({ model, params }, data, trackerProperty) => {
|
1545
|
+
try {
|
1546
|
+
const res = await createDocument({
|
1547
|
+
model,
|
1548
|
+
data,
|
1549
|
+
params
|
1550
|
+
});
|
1551
|
+
if ("error" in res) {
|
1552
|
+
toggleNotification({ type: "danger", message: formatAPIError(res.error) });
|
1553
|
+
trackUsage("didNotCreateEntry", { error: res.error, ...trackerProperty });
|
1554
|
+
return { error: res.error };
|
1555
|
+
}
|
1556
|
+
trackUsage("didCreateEntry", trackerProperty);
|
1166
1557
|
toggleNotification({
|
1167
1558
|
type: "success",
|
1168
1559
|
message: formatMessage({
|
@@ -1170,6 +1561,7 @@ const useDocumentActions = () => {
|
|
1170
1561
|
defaultMessage: "Saved document"
|
1171
1562
|
})
|
1172
1563
|
});
|
1564
|
+
setCurrentStep("contentManager.success");
|
1173
1565
|
return res.data;
|
1174
1566
|
} catch (err) {
|
1175
1567
|
toggleNotification({
|
@@ -1209,7 +1601,7 @@ const useDocumentActions = () => {
|
|
1209
1601
|
throw err;
|
1210
1602
|
}
|
1211
1603
|
},
|
1212
|
-
[autoCloneDocument,
|
1604
|
+
[autoCloneDocument, formatMessage, toggleNotification]
|
1213
1605
|
);
|
1214
1606
|
const [cloneDocument] = useCloneDocumentMutation();
|
1215
1607
|
const clone = React.useCallback(
|
@@ -1235,6 +1627,7 @@ const useDocumentActions = () => {
|
|
1235
1627
|
defaultMessage: "Cloned document"
|
1236
1628
|
})
|
1237
1629
|
});
|
1630
|
+
navigate(`../../${res.data.data.documentId}`, { relative: "path" });
|
1238
1631
|
return res.data;
|
1239
1632
|
} catch (err) {
|
1240
1633
|
toggleNotification({
|
@@ -1245,7 +1638,7 @@ const useDocumentActions = () => {
|
|
1245
1638
|
throw err;
|
1246
1639
|
}
|
1247
1640
|
},
|
1248
|
-
[cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError]
|
1641
|
+
[cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate]
|
1249
1642
|
);
|
1250
1643
|
const [getDoc] = useLazyGetDocumentQuery();
|
1251
1644
|
const getDocument = React.useCallback(
|
@@ -1271,7 +1664,7 @@ const useDocumentActions = () => {
|
|
1271
1664
|
};
|
1272
1665
|
};
|
1273
1666
|
const ProtectedHistoryPage = lazy(
|
1274
|
-
() => import("./History-
|
1667
|
+
() => import("./History-D-99Wh30.mjs").then((mod) => ({ default: mod.ProtectedHistoryPage }))
|
1275
1668
|
);
|
1276
1669
|
const routes$1 = [
|
1277
1670
|
{
|
@@ -1284,31 +1677,31 @@ const routes$1 = [
|
|
1284
1677
|
}
|
1285
1678
|
];
|
1286
1679
|
const ProtectedEditViewPage = lazy(
|
1287
|
-
() => import("./EditViewPage-
|
1680
|
+
() => import("./EditViewPage-CiwVPMaK.mjs").then((mod) => ({ default: mod.ProtectedEditViewPage }))
|
1288
1681
|
);
|
1289
1682
|
const ProtectedListViewPage = lazy(
|
1290
|
-
() => import("./ListViewPage-
|
1683
|
+
() => import("./ListViewPage-DSK3f0ST.mjs").then((mod) => ({ default: mod.ProtectedListViewPage }))
|
1291
1684
|
);
|
1292
1685
|
const ProtectedListConfiguration = lazy(
|
1293
|
-
() => import("./ListConfigurationPage-
|
1686
|
+
() => import("./ListConfigurationPage-JPWZz7Kg.mjs").then((mod) => ({
|
1294
1687
|
default: mod.ProtectedListConfiguration
|
1295
1688
|
}))
|
1296
1689
|
);
|
1297
1690
|
const ProtectedEditConfigurationPage = lazy(
|
1298
|
-
() => import("./EditConfigurationPage-
|
1691
|
+
() => import("./EditConfigurationPage-BglmD_BF.mjs").then((mod) => ({
|
1299
1692
|
default: mod.ProtectedEditConfigurationPage
|
1300
1693
|
}))
|
1301
1694
|
);
|
1302
1695
|
const ProtectedComponentConfigurationPage = lazy(
|
1303
|
-
() => import("./ComponentConfigurationPage-
|
1696
|
+
() => import("./ComponentConfigurationPage-CIjXcRAB.mjs").then((mod) => ({
|
1304
1697
|
default: mod.ProtectedComponentConfigurationPage
|
1305
1698
|
}))
|
1306
1699
|
);
|
1307
1700
|
const NoPermissions = lazy(
|
1308
|
-
() => import("./NoPermissionsPage-
|
1701
|
+
() => import("./NoPermissionsPage-DBrBw-0y.mjs").then((mod) => ({ default: mod.NoPermissions }))
|
1309
1702
|
);
|
1310
1703
|
const NoContentType = lazy(
|
1311
|
-
() => import("./NoContentTypePage-
|
1704
|
+
() => import("./NoContentTypePage-D99LU1YP.mjs").then((mod) => ({ default: mod.NoContentType }))
|
1312
1705
|
);
|
1313
1706
|
const CollectionTypePages = () => {
|
1314
1707
|
const { collectionType } = useParams();
|
@@ -1346,1069 +1739,765 @@ const routes = [
|
|
1346
1739
|
path: ":collectionType/:slug/configurations/edit",
|
1347
1740
|
Component: ProtectedEditConfigurationPage
|
1348
1741
|
},
|
1349
|
-
{
|
1350
|
-
path: "403",
|
1351
|
-
Component: NoPermissions
|
1352
|
-
},
|
1353
|
-
{
|
1354
|
-
path: "no-content-types",
|
1355
|
-
Component: NoContentType
|
1356
|
-
},
|
1357
|
-
...routes$1
|
1358
|
-
];
|
1359
|
-
const DocumentActions = ({ actions: actions2 }) => {
|
1360
|
-
const { formatMessage } = useIntl();
|
1361
|
-
const [primaryAction, secondaryAction, ...restActions] = actions2.filter((action) => {
|
1362
|
-
if (action.position === void 0) {
|
1363
|
-
return true;
|
1364
|
-
}
|
1365
|
-
const positions = Array.isArray(action.position) ? action.position : [action.position];
|
1366
|
-
return positions.includes("panel");
|
1367
|
-
});
|
1368
|
-
if (!primaryAction) {
|
1369
|
-
return null;
|
1370
|
-
}
|
1371
|
-
return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, alignItems: "stretch", width: "100%", children: [
|
1372
|
-
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
1373
|
-
/* @__PURE__ */ jsx(DocumentActionButton, { ...primaryAction, variant: primaryAction.variant || "default" }),
|
1374
|
-
restActions.length > 0 ? /* @__PURE__ */ jsx(
|
1375
|
-
DocumentActionsMenu,
|
1376
|
-
{
|
1377
|
-
actions: restActions,
|
1378
|
-
label: formatMessage({
|
1379
|
-
id: "content-manager.containers.edit.panels.default.more-actions",
|
1380
|
-
defaultMessage: "More document actions"
|
1381
|
-
})
|
1382
|
-
}
|
1383
|
-
) : null
|
1384
|
-
] }),
|
1385
|
-
secondaryAction ? /* @__PURE__ */ jsx(
|
1386
|
-
DocumentActionButton,
|
1387
|
-
{
|
1388
|
-
...secondaryAction,
|
1389
|
-
variant: secondaryAction.variant || "secondary"
|
1390
|
-
}
|
1391
|
-
) : null
|
1392
|
-
] });
|
1393
|
-
};
|
1394
|
-
const DocumentActionButton = (action) => {
|
1395
|
-
const [dialogId, setDialogId] = React.useState(null);
|
1396
|
-
const { toggleNotification } = useNotification();
|
1397
|
-
const handleClick = (action2) => async (e) => {
|
1398
|
-
const { onClick = () => false, dialog, id } = action2;
|
1399
|
-
const muteDialog = await onClick(e);
|
1400
|
-
if (dialog && !muteDialog) {
|
1401
|
-
switch (dialog.type) {
|
1402
|
-
case "notification":
|
1403
|
-
toggleNotification({
|
1404
|
-
title: dialog.title,
|
1405
|
-
message: dialog.content,
|
1406
|
-
type: dialog.status,
|
1407
|
-
timeout: dialog.timeout,
|
1408
|
-
onClose: dialog.onClose
|
1409
|
-
});
|
1410
|
-
break;
|
1411
|
-
case "dialog":
|
1412
|
-
case "modal":
|
1413
|
-
e.preventDefault();
|
1414
|
-
setDialogId(id);
|
1415
|
-
}
|
1416
|
-
}
|
1417
|
-
};
|
1418
|
-
const handleClose = () => {
|
1419
|
-
setDialogId(null);
|
1420
|
-
};
|
1421
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
1422
|
-
/* @__PURE__ */ jsx(
|
1423
|
-
Button,
|
1424
|
-
{
|
1425
|
-
flex: 1,
|
1426
|
-
startIcon: action.icon,
|
1427
|
-
disabled: action.disabled,
|
1428
|
-
onClick: handleClick(action),
|
1429
|
-
justifyContent: "center",
|
1430
|
-
variant: action.variant || "default",
|
1431
|
-
children: action.label
|
1432
|
-
}
|
1433
|
-
),
|
1434
|
-
action.dialog?.type === "dialog" ? /* @__PURE__ */ jsx(
|
1435
|
-
DocumentActionConfirmDialog,
|
1436
|
-
{
|
1437
|
-
...action.dialog,
|
1438
|
-
variant: action.dialog?.variant ?? action.variant,
|
1439
|
-
isOpen: dialogId === action.id,
|
1440
|
-
onClose: handleClose
|
1441
|
-
}
|
1442
|
-
) : null,
|
1443
|
-
action.dialog?.type === "modal" ? /* @__PURE__ */ jsx(
|
1444
|
-
DocumentActionModal,
|
1445
|
-
{
|
1446
|
-
...action.dialog,
|
1447
|
-
onModalClose: handleClose,
|
1448
|
-
isOpen: dialogId === action.id
|
1449
|
-
}
|
1450
|
-
) : null
|
1451
|
-
] });
|
1452
|
-
};
|
1453
|
-
const DocumentActionsMenu = ({
|
1454
|
-
actions: actions2,
|
1455
|
-
children,
|
1456
|
-
label,
|
1457
|
-
variant = "tertiary"
|
1458
|
-
}) => {
|
1459
|
-
const [isOpen, setIsOpen] = React.useState(false);
|
1460
|
-
const [dialogId, setDialogId] = React.useState(null);
|
1461
|
-
const { formatMessage } = useIntl();
|
1462
|
-
const { toggleNotification } = useNotification();
|
1463
|
-
const isDisabled = actions2.every((action) => action.disabled) || actions2.length === 0;
|
1464
|
-
const handleClick = (action) => async (e) => {
|
1465
|
-
const { onClick = () => false, dialog, id } = action;
|
1466
|
-
const muteDialog = await onClick(e);
|
1467
|
-
if (dialog && !muteDialog) {
|
1468
|
-
switch (dialog.type) {
|
1469
|
-
case "notification":
|
1470
|
-
toggleNotification({
|
1471
|
-
title: dialog.title,
|
1472
|
-
message: dialog.content,
|
1473
|
-
type: dialog.status,
|
1474
|
-
timeout: dialog.timeout,
|
1475
|
-
onClose: dialog.onClose
|
1476
|
-
});
|
1477
|
-
break;
|
1478
|
-
case "dialog":
|
1479
|
-
case "modal":
|
1480
|
-
setDialogId(id);
|
1481
|
-
}
|
1482
|
-
}
|
1483
|
-
};
|
1484
|
-
const handleClose = () => {
|
1485
|
-
setDialogId(null);
|
1486
|
-
setIsOpen(false);
|
1487
|
-
};
|
1488
|
-
return /* @__PURE__ */ jsxs(Menu.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
|
1489
|
-
/* @__PURE__ */ jsxs(
|
1490
|
-
Menu.Trigger,
|
1491
|
-
{
|
1492
|
-
disabled: isDisabled,
|
1493
|
-
size: "S",
|
1494
|
-
endIcon: null,
|
1495
|
-
paddingTop: "7px",
|
1496
|
-
paddingLeft: "9px",
|
1497
|
-
paddingRight: "9px",
|
1498
|
-
variant,
|
1499
|
-
children: [
|
1500
|
-
/* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
|
1501
|
-
/* @__PURE__ */ jsx(VisuallyHidden, { tag: "span", children: label || formatMessage({
|
1502
|
-
id: "content-manager.containers.edit.panels.default.more-actions",
|
1503
|
-
defaultMessage: "More document actions"
|
1504
|
-
}) })
|
1505
|
-
]
|
1506
|
-
}
|
1507
|
-
),
|
1508
|
-
/* @__PURE__ */ jsxs(Menu.Content, { top: "4px", maxHeight: void 0, popoverPlacement: "bottom-end", children: [
|
1509
|
-
actions2.map((action) => {
|
1510
|
-
return /* @__PURE__ */ jsx(
|
1511
|
-
Menu.Item,
|
1512
|
-
{
|
1513
|
-
disabled: action.disabled,
|
1514
|
-
onSelect: handleClick(action),
|
1515
|
-
display: "block",
|
1516
|
-
children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
|
1517
|
-
/* @__PURE__ */ jsxs(Flex, { color: convertActionVariantToColor(action.variant), gap: 2, tag: "span", children: [
|
1518
|
-
/* @__PURE__ */ jsx(Box, { tag: "span", color: convertActionVariantToIconColor(action.variant), children: action.icon }),
|
1519
|
-
action.label
|
1520
|
-
] }),
|
1521
|
-
action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
|
1522
|
-
Flex,
|
1523
|
-
{
|
1524
|
-
alignItems: "center",
|
1525
|
-
background: "alternative100",
|
1526
|
-
borderStyle: "solid",
|
1527
|
-
borderColor: "alternative200",
|
1528
|
-
borderWidth: "1px",
|
1529
|
-
height: 5,
|
1530
|
-
paddingLeft: 2,
|
1531
|
-
paddingRight: 2,
|
1532
|
-
hasRadius: true,
|
1533
|
-
color: "alternative600",
|
1534
|
-
children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
|
1535
|
-
}
|
1536
|
-
)
|
1537
|
-
] })
|
1538
|
-
},
|
1539
|
-
action.id
|
1540
|
-
);
|
1541
|
-
}),
|
1542
|
-
children
|
1543
|
-
] }),
|
1544
|
-
actions2.map((action) => {
|
1545
|
-
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
1546
|
-
action.dialog?.type === "dialog" ? /* @__PURE__ */ jsx(
|
1547
|
-
DocumentActionConfirmDialog,
|
1548
|
-
{
|
1549
|
-
...action.dialog,
|
1550
|
-
variant: action.variant,
|
1551
|
-
isOpen: dialogId === action.id,
|
1552
|
-
onClose: handleClose
|
1553
|
-
}
|
1554
|
-
) : null,
|
1555
|
-
action.dialog?.type === "modal" ? /* @__PURE__ */ jsx(
|
1556
|
-
DocumentActionModal,
|
1557
|
-
{
|
1558
|
-
...action.dialog,
|
1559
|
-
onModalClose: handleClose,
|
1560
|
-
isOpen: dialogId === action.id
|
1561
|
-
}
|
1562
|
-
) : null
|
1563
|
-
] }, action.id);
|
1564
|
-
})
|
1565
|
-
] });
|
1566
|
-
};
|
1567
|
-
const convertActionVariantToColor = (variant = "secondary") => {
|
1568
|
-
switch (variant) {
|
1569
|
-
case "danger":
|
1570
|
-
return "danger600";
|
1571
|
-
case "secondary":
|
1572
|
-
return void 0;
|
1573
|
-
case "success":
|
1574
|
-
return "success600";
|
1575
|
-
default:
|
1576
|
-
return "primary600";
|
1577
|
-
}
|
1578
|
-
};
|
1579
|
-
const convertActionVariantToIconColor = (variant = "secondary") => {
|
1580
|
-
switch (variant) {
|
1581
|
-
case "danger":
|
1582
|
-
return "danger600";
|
1583
|
-
case "secondary":
|
1584
|
-
return "neutral500";
|
1585
|
-
case "success":
|
1586
|
-
return "success600";
|
1587
|
-
default:
|
1588
|
-
return "primary600";
|
1589
|
-
}
|
1590
|
-
};
|
1591
|
-
const DocumentActionConfirmDialog = ({
|
1592
|
-
onClose,
|
1593
|
-
onCancel,
|
1594
|
-
onConfirm,
|
1595
|
-
title,
|
1596
|
-
content,
|
1597
|
-
isOpen,
|
1598
|
-
variant = "secondary"
|
1599
|
-
}) => {
|
1600
|
-
const { formatMessage } = useIntl();
|
1601
|
-
const handleClose = async () => {
|
1602
|
-
if (onCancel) {
|
1603
|
-
await onCancel();
|
1604
|
-
}
|
1605
|
-
onClose();
|
1606
|
-
};
|
1607
|
-
const handleConfirm = async () => {
|
1608
|
-
if (onConfirm) {
|
1609
|
-
await onConfirm();
|
1610
|
-
}
|
1611
|
-
onClose();
|
1612
|
-
};
|
1613
|
-
return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
|
1614
|
-
/* @__PURE__ */ jsx(Dialog.Header, { children: title }),
|
1615
|
-
/* @__PURE__ */ jsx(Dialog.Body, { children: content }),
|
1616
|
-
/* @__PURE__ */ jsxs(Dialog.Footer, { children: [
|
1617
|
-
/* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
|
1618
|
-
id: "app.components.Button.cancel",
|
1619
|
-
defaultMessage: "Cancel"
|
1620
|
-
}) }) }),
|
1621
|
-
/* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, children: formatMessage({
|
1622
|
-
id: "app.components.Button.confirm",
|
1623
|
-
defaultMessage: "Confirm"
|
1624
|
-
}) })
|
1625
|
-
] })
|
1626
|
-
] }) });
|
1627
|
-
};
|
1628
|
-
const DocumentActionModal = ({
|
1629
|
-
isOpen,
|
1630
|
-
title,
|
1631
|
-
onClose,
|
1632
|
-
footer: Footer,
|
1633
|
-
content: Content,
|
1634
|
-
onModalClose
|
1635
|
-
}) => {
|
1636
|
-
const handleClose = () => {
|
1637
|
-
if (onClose) {
|
1638
|
-
onClose();
|
1639
|
-
}
|
1640
|
-
onModalClose();
|
1641
|
-
};
|
1642
|
-
return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
|
1643
|
-
/* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
|
1644
|
-
typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsx(Modal.Body, { children: Content }),
|
1645
|
-
typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
|
1646
|
-
] }) });
|
1647
|
-
};
|
1648
|
-
const PublishAction$1 = ({
|
1649
|
-
activeTab,
|
1650
|
-
documentId,
|
1651
|
-
model,
|
1652
|
-
collectionType,
|
1653
|
-
meta,
|
1654
|
-
document
|
1655
|
-
}) => {
|
1656
|
-
const { schema } = useDoc();
|
1657
|
-
const navigate = useNavigate();
|
1658
|
-
const { toggleNotification } = useNotification();
|
1659
|
-
const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
|
1660
|
-
const isCloning = useMatch(CLONE_PATH) !== null;
|
1661
|
-
const { formatMessage } = useIntl();
|
1662
|
-
const { canPublish, canCreate, canUpdate } = useDocumentRBAC(
|
1663
|
-
"PublishAction",
|
1664
|
-
({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 }) => ({ canPublish: canPublish2, canCreate: canCreate2, canUpdate: canUpdate2 })
|
1665
|
-
);
|
1666
|
-
const { publish } = useDocumentActions();
|
1667
|
-
const [
|
1668
|
-
countDraftRelations,
|
1669
|
-
{ isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
|
1670
|
-
] = useLazyGetDraftRelationCountQuery();
|
1671
|
-
const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React.useState(0);
|
1672
|
-
const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React.useState(0);
|
1673
|
-
const [{ query, rawQuery }] = useQueryParams();
|
1674
|
-
const params = React.useMemo(() => buildValidParams(query), [query]);
|
1675
|
-
const modified = useForm("PublishAction", ({ modified: modified2 }) => modified2);
|
1676
|
-
const setSubmitting = useForm("PublishAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
|
1677
|
-
const isSubmitting = useForm("PublishAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
|
1678
|
-
const validate = useForm("PublishAction", (state) => state.validate);
|
1679
|
-
const setErrors = useForm("PublishAction", (state) => state.setErrors);
|
1680
|
-
const formValues = useForm("PublishAction", ({ values }) => values);
|
1681
|
-
React.useEffect(() => {
|
1682
|
-
if (isErrorDraftRelations) {
|
1683
|
-
toggleNotification({
|
1684
|
-
type: "danger",
|
1685
|
-
message: formatMessage({
|
1686
|
-
id: getTranslation("error.records.fetch-draft-relatons"),
|
1687
|
-
defaultMessage: "An error occurred while fetching draft relations on this document."
|
1688
|
-
})
|
1689
|
-
});
|
1690
|
-
}
|
1691
|
-
}, [isErrorDraftRelations, toggleNotification, formatMessage]);
|
1692
|
-
React.useEffect(() => {
|
1693
|
-
const localDraftRelations = /* @__PURE__ */ new Set();
|
1694
|
-
const extractDraftRelations = (data) => {
|
1695
|
-
const relations = data.connect || [];
|
1696
|
-
relations.forEach((relation) => {
|
1697
|
-
if (relation.status === "draft") {
|
1698
|
-
localDraftRelations.add(relation.id);
|
1699
|
-
}
|
1700
|
-
});
|
1701
|
-
};
|
1702
|
-
const traverseAndExtract = (data) => {
|
1703
|
-
Object.entries(data).forEach(([key, value]) => {
|
1704
|
-
if (key === "connect" && Array.isArray(value)) {
|
1705
|
-
extractDraftRelations({ connect: value });
|
1706
|
-
} else if (typeof value === "object" && value !== null) {
|
1707
|
-
traverseAndExtract(value);
|
1708
|
-
}
|
1709
|
-
});
|
1710
|
-
};
|
1711
|
-
if (!documentId || modified) {
|
1712
|
-
traverseAndExtract(formValues);
|
1713
|
-
setLocalCountOfDraftRelations(localDraftRelations.size);
|
1714
|
-
}
|
1715
|
-
}, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
|
1716
|
-
React.useEffect(() => {
|
1717
|
-
if (documentId) {
|
1718
|
-
const fetchDraftRelationsCount = async () => {
|
1719
|
-
const { data, error } = await countDraftRelations({
|
1720
|
-
collectionType,
|
1721
|
-
model,
|
1722
|
-
documentId,
|
1723
|
-
params
|
1724
|
-
});
|
1725
|
-
if (error) {
|
1726
|
-
throw error;
|
1727
|
-
}
|
1728
|
-
if (data) {
|
1729
|
-
setServerCountOfDraftRelations(data.data);
|
1730
|
-
}
|
1731
|
-
};
|
1732
|
-
fetchDraftRelationsCount();
|
1742
|
+
{
|
1743
|
+
path: "403",
|
1744
|
+
Component: NoPermissions
|
1745
|
+
},
|
1746
|
+
{
|
1747
|
+
path: "no-content-types",
|
1748
|
+
Component: NoContentType
|
1749
|
+
},
|
1750
|
+
...routes$1
|
1751
|
+
];
|
1752
|
+
const DocumentActions = ({ actions: actions2 }) => {
|
1753
|
+
const { formatMessage } = useIntl();
|
1754
|
+
const [primaryAction, secondaryAction, ...restActions] = actions2.filter((action) => {
|
1755
|
+
if (action.position === void 0) {
|
1756
|
+
return true;
|
1733
1757
|
}
|
1734
|
-
|
1735
|
-
|
1736
|
-
|
1758
|
+
const positions = Array.isArray(action.position) ? action.position : [action.position];
|
1759
|
+
return positions.includes("panel");
|
1760
|
+
});
|
1761
|
+
if (!primaryAction) {
|
1737
1762
|
return null;
|
1738
1763
|
}
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1743
|
-
|
1744
|
-
toggleNotification({
|
1745
|
-
type: "danger",
|
1746
|
-
message: formatMessage({
|
1747
|
-
id: "content-manager.validation.error",
|
1748
|
-
defaultMessage: "There are validation errors in your document. Please fix them before saving."
|
1749
|
-
})
|
1750
|
-
});
|
1751
|
-
return;
|
1752
|
-
}
|
1753
|
-
const res = await publish(
|
1754
|
-
{
|
1755
|
-
collectionType,
|
1756
|
-
model,
|
1757
|
-
documentId,
|
1758
|
-
params
|
1759
|
-
},
|
1760
|
-
formValues
|
1761
|
-
);
|
1762
|
-
if ("data" in res && collectionType !== SINGLE_TYPES) {
|
1763
|
-
navigate({
|
1764
|
-
pathname: `../${collectionType}/${model}/${res.data.documentId}`,
|
1765
|
-
search: rawQuery
|
1766
|
-
});
|
1767
|
-
} else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
|
1768
|
-
setErrors(formatValidationErrors(res.error));
|
1769
|
-
}
|
1770
|
-
} finally {
|
1771
|
-
setSubmitting(false);
|
1772
|
-
}
|
1773
|
-
};
|
1774
|
-
const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
|
1775
|
-
const hasDraftRelations = totalDraftRelations > 0;
|
1776
|
-
return {
|
1777
|
-
/**
|
1778
|
-
* Disabled when:
|
1779
|
-
* - currently if you're cloning a document we don't support publish & clone at the same time.
|
1780
|
-
* - the form is submitting
|
1781
|
-
* - the active tab is the published tab
|
1782
|
-
* - the document is already published & not modified
|
1783
|
-
* - the document is being created & not modified
|
1784
|
-
* - the user doesn't have the permission to publish
|
1785
|
-
* - the user doesn't have the permission to create a new document
|
1786
|
-
* - the user doesn't have the permission to update the document
|
1787
|
-
*/
|
1788
|
-
disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish || Boolean(!document?.documentId && !canCreate || document?.documentId && !canUpdate),
|
1789
|
-
label: formatMessage({
|
1790
|
-
id: "app.utils.publish",
|
1791
|
-
defaultMessage: "Publish"
|
1792
|
-
}),
|
1793
|
-
onClick: async () => {
|
1794
|
-
if (hasDraftRelations) {
|
1795
|
-
return;
|
1796
|
-
}
|
1797
|
-
await performPublish();
|
1798
|
-
},
|
1799
|
-
dialog: hasDraftRelations ? {
|
1800
|
-
type: "dialog",
|
1801
|
-
variant: "danger",
|
1802
|
-
footer: null,
|
1803
|
-
title: formatMessage({
|
1804
|
-
id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
|
1805
|
-
defaultMessage: "Confirmation"
|
1806
|
-
}),
|
1807
|
-
content: formatMessage(
|
1808
|
-
{
|
1809
|
-
id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
|
1810
|
-
defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
|
1811
|
-
},
|
1764
|
+
return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, alignItems: "stretch", width: "100%", children: [
|
1765
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
1766
|
+
/* @__PURE__ */ jsx(DocumentActionButton, { ...primaryAction, variant: primaryAction.variant || "default" }),
|
1767
|
+
restActions.length > 0 ? /* @__PURE__ */ jsx(
|
1768
|
+
DocumentActionsMenu,
|
1812
1769
|
{
|
1813
|
-
|
1770
|
+
actions: restActions,
|
1771
|
+
label: formatMessage({
|
1772
|
+
id: "content-manager.containers.edit.panels.default.more-actions",
|
1773
|
+
defaultMessage: "More document actions"
|
1774
|
+
})
|
1814
1775
|
}
|
1815
|
-
)
|
1816
|
-
|
1817
|
-
|
1776
|
+
) : null
|
1777
|
+
] }),
|
1778
|
+
secondaryAction ? /* @__PURE__ */ jsx(
|
1779
|
+
DocumentActionButton,
|
1780
|
+
{
|
1781
|
+
...secondaryAction,
|
1782
|
+
variant: secondaryAction.variant || "secondary"
|
1818
1783
|
}
|
1819
|
-
|
1820
|
-
};
|
1784
|
+
) : null
|
1785
|
+
] });
|
1821
1786
|
};
|
1822
|
-
|
1823
|
-
const
|
1824
|
-
activeTab,
|
1825
|
-
documentId,
|
1826
|
-
model,
|
1827
|
-
collectionType
|
1828
|
-
}) => {
|
1829
|
-
const navigate = useNavigate();
|
1787
|
+
const DocumentActionButton = (action) => {
|
1788
|
+
const [dialogId, setDialogId] = React.useState(null);
|
1830
1789
|
const { toggleNotification } = useNotification();
|
1831
|
-
const
|
1832
|
-
|
1833
|
-
|
1834
|
-
|
1835
|
-
|
1836
|
-
|
1837
|
-
canUpdate: canUpdate2
|
1838
|
-
}));
|
1839
|
-
const { create, update, clone } = useDocumentActions();
|
1840
|
-
const [{ query, rawQuery }] = useQueryParams();
|
1841
|
-
const params = React.useMemo(() => buildValidParams(query), [query]);
|
1842
|
-
const isSubmitting = useForm("UpdateAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
|
1843
|
-
const modified = useForm("UpdateAction", ({ modified: modified2 }) => modified2);
|
1844
|
-
const setSubmitting = useForm("UpdateAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
|
1845
|
-
const document = useForm("UpdateAction", ({ values }) => values);
|
1846
|
-
const validate = useForm("UpdateAction", (state) => state.validate);
|
1847
|
-
const setErrors = useForm("UpdateAction", (state) => state.setErrors);
|
1848
|
-
const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
|
1849
|
-
return {
|
1850
|
-
/**
|
1851
|
-
* Disabled when:
|
1852
|
-
* - the form is submitting
|
1853
|
-
* - the document is not modified & we're not cloning (you can save a clone entity straight away)
|
1854
|
-
* - the active tab is the published tab
|
1855
|
-
* - the user doesn't have the permission to create a new document
|
1856
|
-
* - the user doesn't have the permission to update the document
|
1857
|
-
*/
|
1858
|
-
disabled: isSubmitting || !modified && !isCloning || activeTab === "published" || Boolean(!documentId && !canCreate || documentId && !canUpdate),
|
1859
|
-
label: formatMessage({
|
1860
|
-
id: "content-manager.containers.Edit.save",
|
1861
|
-
defaultMessage: "Save"
|
1862
|
-
}),
|
1863
|
-
onClick: async () => {
|
1864
|
-
setSubmitting(true);
|
1865
|
-
try {
|
1866
|
-
const { errors } = await validate();
|
1867
|
-
if (errors) {
|
1790
|
+
const handleClick = (action2) => async (e) => {
|
1791
|
+
const { onClick = () => false, dialog, id } = action2;
|
1792
|
+
const muteDialog = await onClick(e);
|
1793
|
+
if (dialog && !muteDialog) {
|
1794
|
+
switch (dialog.type) {
|
1795
|
+
case "notification":
|
1868
1796
|
toggleNotification({
|
1869
|
-
|
1870
|
-
message:
|
1871
|
-
|
1872
|
-
|
1873
|
-
|
1797
|
+
title: dialog.title,
|
1798
|
+
message: dialog.content,
|
1799
|
+
type: dialog.status,
|
1800
|
+
timeout: dialog.timeout,
|
1801
|
+
onClose: dialog.onClose
|
1874
1802
|
});
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1879
|
-
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1884
|
-
|
1885
|
-
|
1886
|
-
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1890
|
-
|
1891
|
-
|
1892
|
-
|
1893
|
-
|
1894
|
-
|
1895
|
-
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1900
|
-
|
1901
|
-
|
1902
|
-
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1912
|
-
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
1916
|
-
params
|
1917
|
-
},
|
1918
|
-
document
|
1919
|
-
);
|
1920
|
-
if ("data" in res && collectionType !== SINGLE_TYPES) {
|
1921
|
-
navigate(
|
1922
|
-
{
|
1923
|
-
pathname: `../${res.data.documentId}`,
|
1924
|
-
search: rawQuery
|
1925
|
-
},
|
1926
|
-
{ replace: true, relative: "path" }
|
1927
|
-
);
|
1928
|
-
} else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
|
1929
|
-
setErrors(formatValidationErrors(res.error));
|
1930
|
-
}
|
1931
|
-
}
|
1932
|
-
} finally {
|
1933
|
-
setSubmitting(false);
|
1803
|
+
break;
|
1804
|
+
case "dialog":
|
1805
|
+
case "modal":
|
1806
|
+
e.preventDefault();
|
1807
|
+
setDialogId(id);
|
1808
|
+
}
|
1809
|
+
}
|
1810
|
+
};
|
1811
|
+
const handleClose = () => {
|
1812
|
+
setDialogId(null);
|
1813
|
+
};
|
1814
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
1815
|
+
/* @__PURE__ */ jsx(
|
1816
|
+
Button,
|
1817
|
+
{
|
1818
|
+
flex: "auto",
|
1819
|
+
startIcon: action.icon,
|
1820
|
+
disabled: action.disabled,
|
1821
|
+
onClick: handleClick(action),
|
1822
|
+
justifyContent: "center",
|
1823
|
+
variant: action.variant || "default",
|
1824
|
+
paddingTop: "7px",
|
1825
|
+
paddingBottom: "7px",
|
1826
|
+
children: action.label
|
1827
|
+
}
|
1828
|
+
),
|
1829
|
+
action.dialog?.type === "dialog" ? /* @__PURE__ */ jsx(
|
1830
|
+
DocumentActionConfirmDialog,
|
1831
|
+
{
|
1832
|
+
...action.dialog,
|
1833
|
+
variant: action.dialog?.variant ?? action.variant,
|
1834
|
+
isOpen: dialogId === action.id,
|
1835
|
+
onClose: handleClose
|
1836
|
+
}
|
1837
|
+
) : null,
|
1838
|
+
action.dialog?.type === "modal" ? /* @__PURE__ */ jsx(
|
1839
|
+
DocumentActionModal,
|
1840
|
+
{
|
1841
|
+
...action.dialog,
|
1842
|
+
onModalClose: handleClose,
|
1843
|
+
isOpen: dialogId === action.id
|
1934
1844
|
}
|
1935
|
-
|
1936
|
-
};
|
1937
|
-
};
|
1938
|
-
UpdateAction.type = "update";
|
1939
|
-
const UNPUBLISH_DRAFT_OPTIONS = {
|
1940
|
-
KEEP: "keep",
|
1941
|
-
DISCARD: "discard"
|
1845
|
+
) : null
|
1846
|
+
] });
|
1942
1847
|
};
|
1943
|
-
const
|
1944
|
-
|
1945
|
-
|
1946
|
-
|
1947
|
-
|
1948
|
-
document
|
1848
|
+
const DocumentActionsMenu = ({
|
1849
|
+
actions: actions2,
|
1850
|
+
children,
|
1851
|
+
label,
|
1852
|
+
variant = "tertiary"
|
1949
1853
|
}) => {
|
1854
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
1855
|
+
const [dialogId, setDialogId] = React.useState(null);
|
1950
1856
|
const { formatMessage } = useIntl();
|
1951
|
-
const { schema } = useDoc();
|
1952
|
-
const canPublish = useDocumentRBAC("UnpublishAction", ({ canPublish: canPublish2 }) => canPublish2);
|
1953
|
-
const { unpublish } = useDocumentActions();
|
1954
|
-
const [{ query }] = useQueryParams();
|
1955
|
-
const params = React.useMemo(() => buildValidParams(query), [query]);
|
1956
1857
|
const { toggleNotification } = useNotification();
|
1957
|
-
const
|
1958
|
-
const
|
1959
|
-
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
1964
|
-
}
|
1965
|
-
return {
|
1966
|
-
disabled: !canPublish || activeTab === "published" || document?.status !== "published" && document?.status !== "modified",
|
1967
|
-
label: formatMessage({
|
1968
|
-
id: "app.utils.unpublish",
|
1969
|
-
defaultMessage: "Unpublish"
|
1970
|
-
}),
|
1971
|
-
icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
|
1972
|
-
onClick: async () => {
|
1973
|
-
if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
|
1974
|
-
if (!documentId) {
|
1975
|
-
console.error(
|
1976
|
-
"You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
|
1977
|
-
);
|
1858
|
+
const isDisabled = actions2.every((action) => action.disabled) || actions2.length === 0;
|
1859
|
+
const handleClick = (action) => async (e) => {
|
1860
|
+
const { onClick = () => false, dialog, id } = action;
|
1861
|
+
const muteDialog = await onClick(e);
|
1862
|
+
if (dialog && !muteDialog) {
|
1863
|
+
switch (dialog.type) {
|
1864
|
+
case "notification":
|
1978
1865
|
toggleNotification({
|
1979
|
-
|
1980
|
-
|
1981
|
-
|
1982
|
-
|
1983
|
-
|
1866
|
+
title: dialog.title,
|
1867
|
+
message: dialog.content,
|
1868
|
+
type: dialog.status,
|
1869
|
+
timeout: dialog.timeout,
|
1870
|
+
onClose: dialog.onClose
|
1984
1871
|
});
|
1985
|
-
|
1986
|
-
|
1872
|
+
break;
|
1873
|
+
case "dialog":
|
1874
|
+
case "modal":
|
1875
|
+
setDialogId(id);
|
1987
1876
|
}
|
1988
|
-
|
1989
|
-
|
1990
|
-
|
1991
|
-
|
1992
|
-
|
1993
|
-
|
1994
|
-
|
1995
|
-
|
1996
|
-
|
1997
|
-
|
1998
|
-
|
1999
|
-
|
2000
|
-
|
2001
|
-
|
2002
|
-
|
2003
|
-
|
2004
|
-
|
2005
|
-
|
2006
|
-
|
1877
|
+
}
|
1878
|
+
};
|
1879
|
+
const handleClose = () => {
|
1880
|
+
setDialogId(null);
|
1881
|
+
setIsOpen(false);
|
1882
|
+
};
|
1883
|
+
return /* @__PURE__ */ jsxs(Menu.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
|
1884
|
+
/* @__PURE__ */ jsxs(
|
1885
|
+
Menu.Trigger,
|
1886
|
+
{
|
1887
|
+
disabled: isDisabled,
|
1888
|
+
size: "S",
|
1889
|
+
endIcon: null,
|
1890
|
+
paddingTop: "4px",
|
1891
|
+
paddingLeft: "7px",
|
1892
|
+
paddingRight: "7px",
|
1893
|
+
variant,
|
1894
|
+
children: [
|
1895
|
+
/* @__PURE__ */ jsx(More, { "aria-hidden": true, focusable: false }),
|
1896
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { tag: "span", children: label || formatMessage({
|
1897
|
+
id: "content-manager.containers.edit.panels.default.more-actions",
|
1898
|
+
defaultMessage: "More document actions"
|
2007
1899
|
}) })
|
2008
|
-
]
|
2009
|
-
|
2010
|
-
|
2011
|
-
|
2012
|
-
|
2013
|
-
|
2014
|
-
|
2015
|
-
id: "content-manager.actions.unpublish.dialog.radio-label",
|
2016
|
-
defaultMessage: "Choose an option to unpublish the document."
|
2017
|
-
}),
|
2018
|
-
onValueChange: handleChange,
|
2019
|
-
children: [
|
2020
|
-
/* @__PURE__ */ jsx(Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
|
2021
|
-
id: "content-manager.actions.unpublish.dialog.option.keep-draft",
|
2022
|
-
defaultMessage: "Keep draft"
|
2023
|
-
}) }),
|
2024
|
-
/* @__PURE__ */ jsx(Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
|
2025
|
-
id: "content-manager.actions.unpublish.dialog.option.replace-draft",
|
2026
|
-
defaultMessage: "Replace draft"
|
2027
|
-
}) })
|
2028
|
-
]
|
2029
|
-
}
|
2030
|
-
)
|
2031
|
-
] }),
|
2032
|
-
onConfirm: async () => {
|
2033
|
-
if (!documentId && collectionType !== SINGLE_TYPES) {
|
2034
|
-
console.error(
|
2035
|
-
"You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
|
2036
|
-
);
|
2037
|
-
toggleNotification({
|
2038
|
-
message: formatMessage({
|
2039
|
-
id: "content-manager.actions.unpublish.error",
|
2040
|
-
defaultMessage: "An error occurred while trying to unpublish the document."
|
2041
|
-
}),
|
2042
|
-
type: "danger"
|
2043
|
-
});
|
2044
|
-
}
|
2045
|
-
await unpublish(
|
1900
|
+
]
|
1901
|
+
}
|
1902
|
+
),
|
1903
|
+
/* @__PURE__ */ jsxs(Menu.Content, { maxHeight: void 0, popoverPlacement: "bottom-end", children: [
|
1904
|
+
actions2.map((action) => {
|
1905
|
+
return /* @__PURE__ */ jsx(
|
1906
|
+
Menu.Item,
|
2046
1907
|
{
|
2047
|
-
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
1908
|
+
disabled: action.disabled,
|
1909
|
+
onSelect: handleClick(action),
|
1910
|
+
display: "block",
|
1911
|
+
children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 4, children: [
|
1912
|
+
/* @__PURE__ */ jsxs(
|
1913
|
+
Flex,
|
1914
|
+
{
|
1915
|
+
color: !action.disabled ? convertActionVariantToColor(action.variant) : "inherit",
|
1916
|
+
gap: 2,
|
1917
|
+
tag: "span",
|
1918
|
+
children: [
|
1919
|
+
/* @__PURE__ */ jsx(
|
1920
|
+
Flex,
|
1921
|
+
{
|
1922
|
+
tag: "span",
|
1923
|
+
color: !action.disabled ? convertActionVariantToIconColor(action.variant) : "inherit",
|
1924
|
+
children: action.icon
|
1925
|
+
}
|
1926
|
+
),
|
1927
|
+
action.label
|
1928
|
+
]
|
1929
|
+
}
|
1930
|
+
),
|
1931
|
+
action.id.startsWith("HistoryAction") && /* @__PURE__ */ jsx(
|
1932
|
+
Flex,
|
1933
|
+
{
|
1934
|
+
alignItems: "center",
|
1935
|
+
background: "alternative100",
|
1936
|
+
borderStyle: "solid",
|
1937
|
+
borderColor: "alternative200",
|
1938
|
+
borderWidth: "1px",
|
1939
|
+
height: 5,
|
1940
|
+
paddingLeft: 2,
|
1941
|
+
paddingRight: 2,
|
1942
|
+
hasRadius: true,
|
1943
|
+
color: "alternative600",
|
1944
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", lineHeight: 1, children: formatMessage({ id: "global.new", defaultMessage: "New" }) })
|
1945
|
+
}
|
1946
|
+
)
|
1947
|
+
] })
|
2051
1948
|
},
|
2052
|
-
|
1949
|
+
action.id
|
2053
1950
|
);
|
2054
|
-
}
|
2055
|
-
|
2056
|
-
|
2057
|
-
|
1951
|
+
}),
|
1952
|
+
children
|
1953
|
+
] }),
|
1954
|
+
actions2.map((action) => {
|
1955
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
1956
|
+
action.dialog?.type === "dialog" ? /* @__PURE__ */ jsx(
|
1957
|
+
DocumentActionConfirmDialog,
|
1958
|
+
{
|
1959
|
+
...action.dialog,
|
1960
|
+
variant: action.variant,
|
1961
|
+
isOpen: dialogId === action.id,
|
1962
|
+
onClose: handleClose
|
1963
|
+
}
|
1964
|
+
) : null,
|
1965
|
+
action.dialog?.type === "modal" ? /* @__PURE__ */ jsx(
|
1966
|
+
DocumentActionModal,
|
1967
|
+
{
|
1968
|
+
...action.dialog,
|
1969
|
+
onModalClose: handleClose,
|
1970
|
+
isOpen: dialogId === action.id
|
1971
|
+
}
|
1972
|
+
) : null
|
1973
|
+
] }, action.id);
|
1974
|
+
})
|
1975
|
+
] });
|
1976
|
+
};
|
1977
|
+
const convertActionVariantToColor = (variant = "secondary") => {
|
1978
|
+
switch (variant) {
|
1979
|
+
case "danger":
|
1980
|
+
return "danger600";
|
1981
|
+
case "secondary":
|
1982
|
+
return void 0;
|
1983
|
+
case "success":
|
1984
|
+
return "success600";
|
1985
|
+
default:
|
1986
|
+
return "primary600";
|
1987
|
+
}
|
1988
|
+
};
|
1989
|
+
const convertActionVariantToIconColor = (variant = "secondary") => {
|
1990
|
+
switch (variant) {
|
1991
|
+
case "danger":
|
1992
|
+
return "danger600";
|
1993
|
+
case "secondary":
|
1994
|
+
return "neutral500";
|
1995
|
+
case "success":
|
1996
|
+
return "success600";
|
1997
|
+
default:
|
1998
|
+
return "primary600";
|
1999
|
+
}
|
2000
|
+
};
|
2001
|
+
const DocumentActionConfirmDialog = ({
|
2002
|
+
onClose,
|
2003
|
+
onCancel,
|
2004
|
+
onConfirm,
|
2005
|
+
title,
|
2006
|
+
content,
|
2007
|
+
isOpen,
|
2008
|
+
variant = "secondary"
|
2009
|
+
}) => {
|
2010
|
+
const { formatMessage } = useIntl();
|
2011
|
+
const handleClose = async () => {
|
2012
|
+
if (onCancel) {
|
2013
|
+
await onCancel();
|
2014
|
+
}
|
2015
|
+
onClose();
|
2016
|
+
};
|
2017
|
+
const handleConfirm = async () => {
|
2018
|
+
if (onConfirm) {
|
2019
|
+
await onConfirm();
|
2020
|
+
}
|
2021
|
+
onClose();
|
2058
2022
|
};
|
2023
|
+
return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
|
2024
|
+
/* @__PURE__ */ jsx(Dialog.Header, { children: title }),
|
2025
|
+
/* @__PURE__ */ jsx(Dialog.Body, { children: content }),
|
2026
|
+
/* @__PURE__ */ jsxs(Dialog.Footer, { children: [
|
2027
|
+
/* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", fullWidth: true, children: formatMessage({
|
2028
|
+
id: "app.components.Button.cancel",
|
2029
|
+
defaultMessage: "Cancel"
|
2030
|
+
}) }) }),
|
2031
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleConfirm, variant, fullWidth: true, children: formatMessage({
|
2032
|
+
id: "app.components.Button.confirm",
|
2033
|
+
defaultMessage: "Confirm"
|
2034
|
+
}) })
|
2035
|
+
] })
|
2036
|
+
] }) });
|
2059
2037
|
};
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2066
|
-
|
2038
|
+
const DocumentActionModal = ({
|
2039
|
+
isOpen,
|
2040
|
+
title,
|
2041
|
+
onClose,
|
2042
|
+
footer: Footer,
|
2043
|
+
content: Content,
|
2044
|
+
onModalClose
|
2067
2045
|
}) => {
|
2068
|
-
const
|
2069
|
-
|
2070
|
-
|
2071
|
-
const { discard } = useDocumentActions();
|
2072
|
-
const [{ query }] = useQueryParams();
|
2073
|
-
const params = React.useMemo(() => buildValidParams(query), [query]);
|
2074
|
-
if (!schema?.options?.draftAndPublish) {
|
2075
|
-
return null;
|
2076
|
-
}
|
2077
|
-
return {
|
2078
|
-
disabled: !canUpdate || activeTab === "published" || document?.status !== "modified",
|
2079
|
-
label: formatMessage({
|
2080
|
-
id: "content-manager.actions.discard.label",
|
2081
|
-
defaultMessage: "Discard changes"
|
2082
|
-
}),
|
2083
|
-
icon: /* @__PURE__ */ jsx(StyledCrossCircle, {}),
|
2084
|
-
position: ["panel", "table-row"],
|
2085
|
-
variant: "danger",
|
2086
|
-
dialog: {
|
2087
|
-
type: "dialog",
|
2088
|
-
title: formatMessage({
|
2089
|
-
id: "app.components.ConfirmDialog.title",
|
2090
|
-
defaultMessage: "Confirmation"
|
2091
|
-
}),
|
2092
|
-
content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
|
2093
|
-
/* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
|
2094
|
-
/* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
|
2095
|
-
id: "content-manager.actions.discard.dialog.body",
|
2096
|
-
defaultMessage: "Are you sure?"
|
2097
|
-
}) })
|
2098
|
-
] }),
|
2099
|
-
onConfirm: async () => {
|
2100
|
-
await discard({
|
2101
|
-
collectionType,
|
2102
|
-
model,
|
2103
|
-
documentId,
|
2104
|
-
params
|
2105
|
-
});
|
2106
|
-
}
|
2046
|
+
const handleClose = () => {
|
2047
|
+
if (onClose) {
|
2048
|
+
onClose();
|
2107
2049
|
}
|
2050
|
+
onModalClose();
|
2108
2051
|
};
|
2052
|
+
return /* @__PURE__ */ jsx(Modal.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
|
2053
|
+
/* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
|
2054
|
+
typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : /* @__PURE__ */ jsx(Modal.Body, { children: Content }),
|
2055
|
+
typeof Footer === "function" ? /* @__PURE__ */ jsx(Footer, { onClose: handleClose }) : Footer
|
2056
|
+
] }) });
|
2109
2057
|
};
|
2110
|
-
|
2111
|
-
|
2112
|
-
|
2113
|
-
fill: currentColor;
|
2114
|
-
}
|
2115
|
-
`;
|
2116
|
-
const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
|
2117
|
-
const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
|
2118
|
-
const RelativeTime = React.forwardRef(
|
2119
|
-
({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
|
2120
|
-
const { formatRelativeTime, formatDate, formatTime } = useIntl();
|
2121
|
-
const interval = intervalToDuration({
|
2122
|
-
start: timestamp,
|
2123
|
-
end: Date.now()
|
2124
|
-
// see https://github.com/date-fns/date-fns/issues/2891 – No idea why it's all partial it returns it every time.
|
2125
|
-
});
|
2126
|
-
const unit = intervals.find((intervalUnit) => {
|
2127
|
-
return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
|
2128
|
-
});
|
2129
|
-
const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
|
2130
|
-
const customInterval = customIntervals.find(
|
2131
|
-
(custom) => interval[custom.unit] < custom.threshold
|
2132
|
-
);
|
2133
|
-
const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, { numeric: "auto" });
|
2134
|
-
return /* @__PURE__ */ jsx(
|
2135
|
-
"time",
|
2136
|
-
{
|
2137
|
-
ref: forwardedRef,
|
2138
|
-
dateTime: timestamp.toISOString(),
|
2139
|
-
role: "time",
|
2140
|
-
title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
|
2141
|
-
...restProps,
|
2142
|
-
children: displayText
|
2143
|
-
}
|
2144
|
-
);
|
2145
|
-
}
|
2146
|
-
);
|
2147
|
-
const getDisplayName = ({
|
2148
|
-
firstname,
|
2149
|
-
lastname,
|
2150
|
-
username,
|
2151
|
-
email
|
2152
|
-
} = {}) => {
|
2153
|
-
if (username) {
|
2154
|
-
return username;
|
2058
|
+
const transformData = (data) => {
|
2059
|
+
if (Array.isArray(data)) {
|
2060
|
+
return data.map(transformData);
|
2155
2061
|
}
|
2156
|
-
if (
|
2157
|
-
|
2062
|
+
if (typeof data === "object" && data !== null) {
|
2063
|
+
if ("apiData" in data) {
|
2064
|
+
return data.apiData;
|
2065
|
+
}
|
2066
|
+
return mapValues(transformData)(data);
|
2158
2067
|
}
|
2159
|
-
return
|
2068
|
+
return data;
|
2160
2069
|
};
|
2161
|
-
const
|
2162
|
-
|
2163
|
-
|
2164
|
-
|
2165
|
-
|
2166
|
-
|
2167
|
-
|
2070
|
+
const PublishAction$1 = ({
|
2071
|
+
activeTab,
|
2072
|
+
documentId,
|
2073
|
+
model,
|
2074
|
+
collectionType,
|
2075
|
+
meta,
|
2076
|
+
document
|
2077
|
+
}) => {
|
2078
|
+
const { schema } = useDoc();
|
2079
|
+
const navigate = useNavigate();
|
2080
|
+
const { toggleNotification } = useNotification();
|
2081
|
+
const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
|
2082
|
+
const isListView = useMatch(LIST_PATH) !== null;
|
2168
2083
|
const isCloning = useMatch(CLONE_PATH) !== null;
|
2169
|
-
const title = isCreating ? formatMessage({
|
2170
|
-
id: "content-manager.containers.edit.title.new",
|
2171
|
-
defaultMessage: "Create an entry"
|
2172
|
-
}) : documentTitle;
|
2173
|
-
return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
|
2174
|
-
/* @__PURE__ */ jsx(BackButton, {}),
|
2175
|
-
/* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
|
2176
|
-
/* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
|
2177
|
-
/* @__PURE__ */ jsx(HeaderToolbar, {})
|
2178
|
-
] }),
|
2179
|
-
status ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
|
2180
|
-
] });
|
2181
|
-
};
|
2182
|
-
const HeaderToolbar = () => {
|
2183
2084
|
const { formatMessage } = useIntl();
|
2184
|
-
const
|
2085
|
+
const canPublish = useDocumentRBAC("PublishAction", ({ canPublish: canPublish2 }) => canPublish2);
|
2086
|
+
const { publish } = useDocumentActions();
|
2185
2087
|
const [
|
2186
|
-
|
2187
|
-
|
2088
|
+
countDraftRelations,
|
2089
|
+
{ isLoading: isLoadingDraftRelations, isError: isErrorDraftRelations }
|
2090
|
+
] = useLazyGetDraftRelationCountQuery();
|
2091
|
+
const [localCountOfDraftRelations, setLocalCountOfDraftRelations] = React.useState(0);
|
2092
|
+
const [serverCountOfDraftRelations, setServerCountOfDraftRelations] = React.useState(0);
|
2093
|
+
const [{ query, rawQuery }] = useQueryParams();
|
2094
|
+
const params = React.useMemo(() => buildValidParams(query), [query]);
|
2095
|
+
const modified = useForm("PublishAction", ({ modified: modified2 }) => modified2);
|
2096
|
+
const setSubmitting = useForm("PublishAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
|
2097
|
+
const isSubmitting = useForm("PublishAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
|
2098
|
+
const validate = useForm("PublishAction", (state) => state.validate);
|
2099
|
+
const setErrors = useForm("PublishAction", (state) => state.setErrors);
|
2100
|
+
const formValues = useForm("PublishAction", ({ values }) => values);
|
2101
|
+
React.useEffect(() => {
|
2102
|
+
if (isErrorDraftRelations) {
|
2103
|
+
toggleNotification({
|
2104
|
+
type: "danger",
|
2105
|
+
message: formatMessage({
|
2106
|
+
id: getTranslation("error.records.fetch-draft-relatons"),
|
2107
|
+
defaultMessage: "An error occurred while fetching draft relations on this document."
|
2108
|
+
})
|
2109
|
+
});
|
2188
2110
|
}
|
2189
|
-
|
2190
|
-
|
2191
|
-
|
2192
|
-
|
2193
|
-
|
2194
|
-
|
2195
|
-
|
2196
|
-
|
2197
|
-
activeTab: status,
|
2198
|
-
model,
|
2199
|
-
documentId: id,
|
2200
|
-
document: isCloning ? void 0 : document,
|
2201
|
-
meta: isCloning ? void 0 : meta,
|
2202
|
-
collectionType
|
2203
|
-
},
|
2204
|
-
descriptions: plugins["content-manager"].apis.getHeaderActions(),
|
2205
|
-
children: (actions2) => {
|
2206
|
-
if (actions2.length > 0) {
|
2207
|
-
return /* @__PURE__ */ jsx(HeaderActions, { actions: actions2 });
|
2208
|
-
} else {
|
2209
|
-
return null;
|
2210
|
-
}
|
2111
|
+
}, [isErrorDraftRelations, toggleNotification, formatMessage]);
|
2112
|
+
React.useEffect(() => {
|
2113
|
+
const localDraftRelations = /* @__PURE__ */ new Set();
|
2114
|
+
const extractDraftRelations = (data) => {
|
2115
|
+
const relations = data.connect || [];
|
2116
|
+
relations.forEach((relation) => {
|
2117
|
+
if (relation.status === "draft") {
|
2118
|
+
localDraftRelations.add(relation.id);
|
2211
2119
|
}
|
2212
|
-
}
|
2213
|
-
|
2214
|
-
|
2215
|
-
|
2216
|
-
|
2217
|
-
|
2218
|
-
|
2219
|
-
|
2220
|
-
documentId: id,
|
2221
|
-
document: isCloning ? void 0 : document,
|
2222
|
-
meta: isCloning ? void 0 : meta,
|
2223
|
-
collectionType
|
2224
|
-
},
|
2225
|
-
descriptions: plugins["content-manager"].apis.getDocumentActions(),
|
2226
|
-
children: (actions2) => {
|
2227
|
-
const headerActions = actions2.filter((action) => {
|
2228
|
-
const positions = Array.isArray(action.position) ? action.position : [action.position];
|
2229
|
-
return positions.includes("header");
|
2230
|
-
});
|
2231
|
-
return /* @__PURE__ */ jsx(
|
2232
|
-
DocumentActionsMenu,
|
2233
|
-
{
|
2234
|
-
actions: headerActions,
|
2235
|
-
label: formatMessage({
|
2236
|
-
id: "content-manager.containers.edit.header.more-actions",
|
2237
|
-
defaultMessage: "More actions"
|
2238
|
-
}),
|
2239
|
-
children: /* @__PURE__ */ jsx(Information, { activeTab: status })
|
2240
|
-
}
|
2241
|
-
);
|
2120
|
+
});
|
2121
|
+
};
|
2122
|
+
const traverseAndExtract = (data) => {
|
2123
|
+
Object.entries(data).forEach(([key, value]) => {
|
2124
|
+
if (key === "connect" && Array.isArray(value)) {
|
2125
|
+
extractDraftRelations({ connect: value });
|
2126
|
+
} else if (typeof value === "object" && value !== null) {
|
2127
|
+
traverseAndExtract(value);
|
2242
2128
|
}
|
2129
|
+
});
|
2130
|
+
};
|
2131
|
+
if (!documentId || modified) {
|
2132
|
+
traverseAndExtract(formValues);
|
2133
|
+
setLocalCountOfDraftRelations(localDraftRelations.size);
|
2134
|
+
}
|
2135
|
+
}, [documentId, modified, formValues, setLocalCountOfDraftRelations]);
|
2136
|
+
React.useEffect(() => {
|
2137
|
+
if (!document || !document.documentId || isListView) {
|
2138
|
+
return;
|
2139
|
+
}
|
2140
|
+
const fetchDraftRelationsCount = async () => {
|
2141
|
+
const { data, error } = await countDraftRelations({
|
2142
|
+
collectionType,
|
2143
|
+
model,
|
2144
|
+
documentId,
|
2145
|
+
params
|
2146
|
+
});
|
2147
|
+
if (error) {
|
2148
|
+
throw error;
|
2243
2149
|
}
|
2244
|
-
|
2245
|
-
|
2246
|
-
}
|
2247
|
-
|
2248
|
-
|
2249
|
-
|
2250
|
-
|
2150
|
+
if (data) {
|
2151
|
+
setServerCountOfDraftRelations(data.data);
|
2152
|
+
}
|
2153
|
+
};
|
2154
|
+
fetchDraftRelationsCount();
|
2155
|
+
}, [isListView, document, documentId, countDraftRelations, collectionType, model, params]);
|
2156
|
+
const isDocumentPublished = (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] || meta?.availableStatus.some((doc) => doc[PUBLISHED_AT_ATTRIBUTE_NAME] !== null)) && document?.status !== "modified";
|
2157
|
+
if (!schema?.options?.draftAndPublish) {
|
2251
2158
|
return null;
|
2252
2159
|
}
|
2253
|
-
const
|
2254
|
-
|
2255
|
-
|
2256
|
-
|
2257
|
-
|
2258
|
-
|
2259
|
-
|
2260
|
-
|
2261
|
-
|
2262
|
-
|
2263
|
-
|
2264
|
-
|
2160
|
+
const performPublish = async () => {
|
2161
|
+
setSubmitting(true);
|
2162
|
+
try {
|
2163
|
+
const { errors } = await validate(true, {
|
2164
|
+
status: "published"
|
2165
|
+
});
|
2166
|
+
if (errors) {
|
2167
|
+
toggleNotification({
|
2168
|
+
type: "danger",
|
2169
|
+
message: formatMessage({
|
2170
|
+
id: "content-manager.validation.error",
|
2171
|
+
defaultMessage: "There are validation errors in your document. Please fix them before saving."
|
2172
|
+
})
|
2173
|
+
});
|
2174
|
+
return;
|
2175
|
+
}
|
2176
|
+
const res = await publish(
|
2265
2177
|
{
|
2266
|
-
|
2267
|
-
|
2178
|
+
collectionType,
|
2179
|
+
model,
|
2180
|
+
documentId,
|
2181
|
+
params
|
2268
2182
|
},
|
2269
|
-
|
2270
|
-
|
2271
|
-
|
2272
|
-
|
2273
|
-
|
2274
|
-
|
2183
|
+
transformData(formValues)
|
2184
|
+
);
|
2185
|
+
if ("data" in res && collectionType !== SINGLE_TYPES) {
|
2186
|
+
navigate({
|
2187
|
+
pathname: `../${collectionType}/${model}/${res.data.documentId}`,
|
2188
|
+
search: rawQuery
|
2189
|
+
});
|
2190
|
+
} else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
|
2191
|
+
setErrors(formatValidationErrors(res.error));
|
2192
|
+
}
|
2193
|
+
} finally {
|
2194
|
+
setSubmitting(false);
|
2195
|
+
}
|
2196
|
+
};
|
2197
|
+
const totalDraftRelations = localCountOfDraftRelations + serverCountOfDraftRelations;
|
2198
|
+
const enableDraftRelationsCount = false;
|
2199
|
+
const hasDraftRelations = enableDraftRelationsCount;
|
2200
|
+
return {
|
2201
|
+
/**
|
2202
|
+
* Disabled when:
|
2203
|
+
* - currently if you're cloning a document we don't support publish & clone at the same time.
|
2204
|
+
* - the form is submitting
|
2205
|
+
* - the active tab is the published tab
|
2206
|
+
* - the document is already published & not modified
|
2207
|
+
* - the document is being created & not modified
|
2208
|
+
* - the user doesn't have the permission to publish
|
2209
|
+
*/
|
2210
|
+
disabled: isCloning || isSubmitting || isLoadingDraftRelations || activeTab === "published" || !modified && isDocumentPublished || !modified && !document?.documentId || !canPublish,
|
2211
|
+
label: formatMessage({
|
2212
|
+
id: "app.utils.publish",
|
2213
|
+
defaultMessage: "Publish"
|
2214
|
+
}),
|
2215
|
+
onClick: async () => {
|
2216
|
+
await performPublish();
|
2275
2217
|
},
|
2276
|
-
{
|
2277
|
-
|
2278
|
-
|
2279
|
-
|
2280
|
-
|
2218
|
+
dialog: hasDraftRelations ? {
|
2219
|
+
type: "dialog",
|
2220
|
+
variant: "danger",
|
2221
|
+
footer: null,
|
2222
|
+
title: formatMessage({
|
2223
|
+
id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.title`),
|
2224
|
+
defaultMessage: "Confirmation"
|
2281
2225
|
}),
|
2282
|
-
|
2226
|
+
content: formatMessage(
|
2283
2227
|
{
|
2284
|
-
id:
|
2285
|
-
defaultMessage:
|
2228
|
+
id: getTranslation(`popUpwarning.warning.bulk-has-draft-relations.message`),
|
2229
|
+
defaultMessage: "This entry is related to {count, plural, one {# draft entry} other {# draft entries}}. Publishing it could leave broken links in your app."
|
2286
2230
|
},
|
2287
2231
|
{
|
2288
|
-
|
2289
|
-
RelativeTime,
|
2290
|
-
{
|
2291
|
-
timestamp: new Date(createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME])
|
2292
|
-
}
|
2293
|
-
),
|
2294
|
-
isAnonymous: !updator,
|
2295
|
-
author: updator
|
2232
|
+
count: totalDraftRelations
|
2296
2233
|
}
|
2297
|
-
)
|
2298
|
-
|
2299
|
-
|
2300
|
-
|
2301
|
-
|
2302
|
-
|
2303
|
-
|
2304
|
-
|
2305
|
-
|
2306
|
-
|
2307
|
-
|
2308
|
-
|
2309
|
-
|
2310
|
-
|
2311
|
-
|
2312
|
-
|
2313
|
-
|
2314
|
-
|
2315
|
-
|
2316
|
-
|
2317
|
-
|
2318
|
-
|
2234
|
+
),
|
2235
|
+
onConfirm: async () => {
|
2236
|
+
await performPublish();
|
2237
|
+
}
|
2238
|
+
} : void 0
|
2239
|
+
};
|
2240
|
+
};
|
2241
|
+
PublishAction$1.type = "publish";
|
2242
|
+
const UpdateAction = ({
|
2243
|
+
activeTab,
|
2244
|
+
documentId,
|
2245
|
+
model,
|
2246
|
+
collectionType
|
2247
|
+
}) => {
|
2248
|
+
const navigate = useNavigate();
|
2249
|
+
const { toggleNotification } = useNotification();
|
2250
|
+
const { _unstableFormatValidationErrors: formatValidationErrors } = useAPIErrorHandler();
|
2251
|
+
const cloneMatch = useMatch(CLONE_PATH);
|
2252
|
+
const isCloning = cloneMatch !== null;
|
2253
|
+
const { formatMessage } = useIntl();
|
2254
|
+
const { create, update, clone } = useDocumentActions();
|
2255
|
+
const [{ query, rawQuery }] = useQueryParams();
|
2256
|
+
const params = React.useMemo(() => buildValidParams(query), [query]);
|
2257
|
+
const isSubmitting = useForm("UpdateAction", ({ isSubmitting: isSubmitting2 }) => isSubmitting2);
|
2258
|
+
const modified = useForm("UpdateAction", ({ modified: modified2 }) => modified2);
|
2259
|
+
const setSubmitting = useForm("UpdateAction", ({ setSubmitting: setSubmitting2 }) => setSubmitting2);
|
2260
|
+
const document = useForm("UpdateAction", ({ values }) => values);
|
2261
|
+
const validate = useForm("UpdateAction", (state) => state.validate);
|
2262
|
+
const setErrors = useForm("UpdateAction", (state) => state.setErrors);
|
2263
|
+
const resetForm = useForm("PublishAction", ({ resetForm: resetForm2 }) => resetForm2);
|
2264
|
+
return {
|
2265
|
+
/**
|
2266
|
+
* Disabled when:
|
2267
|
+
* - the form is submitting
|
2268
|
+
* - the document is not modified & we're not cloning (you can save a clone entity straight away)
|
2269
|
+
* - the active tab is the published tab
|
2270
|
+
*/
|
2271
|
+
disabled: isSubmitting || !modified && !isCloning || activeTab === "published",
|
2272
|
+
label: formatMessage({
|
2273
|
+
id: "content-manager.containers.Edit.save",
|
2274
|
+
defaultMessage: "Save"
|
2275
|
+
}),
|
2276
|
+
onClick: async () => {
|
2277
|
+
setSubmitting(true);
|
2278
|
+
try {
|
2279
|
+
const { errors } = await validate(true, {
|
2280
|
+
status: "draft"
|
2281
|
+
});
|
2282
|
+
if (errors) {
|
2283
|
+
toggleNotification({
|
2284
|
+
type: "danger",
|
2285
|
+
message: formatMessage({
|
2286
|
+
id: "content-manager.validation.error",
|
2287
|
+
defaultMessage: "There are validation errors in your document. Please fix them before saving."
|
2288
|
+
})
|
2289
|
+
});
|
2290
|
+
return;
|
2319
2291
|
}
|
2320
|
-
|
2321
|
-
|
2322
|
-
|
2323
|
-
|
2324
|
-
|
2325
|
-
|
2326
|
-
|
2327
|
-
|
2328
|
-
|
2329
|
-
|
2330
|
-
|
2331
|
-
|
2332
|
-
|
2333
|
-
|
2334
|
-
|
2335
|
-
|
2336
|
-
|
2337
|
-
|
2338
|
-
|
2339
|
-
|
2340
|
-
|
2341
|
-
|
2292
|
+
if (isCloning) {
|
2293
|
+
const res = await clone(
|
2294
|
+
{
|
2295
|
+
model,
|
2296
|
+
documentId: cloneMatch.params.origin,
|
2297
|
+
params
|
2298
|
+
},
|
2299
|
+
transformData(document)
|
2300
|
+
);
|
2301
|
+
if ("data" in res) {
|
2302
|
+
navigate(
|
2303
|
+
{
|
2304
|
+
pathname: `../${res.data.documentId}`,
|
2305
|
+
search: rawQuery
|
2306
|
+
},
|
2307
|
+
{ relative: "path" }
|
2308
|
+
);
|
2309
|
+
} else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
|
2310
|
+
setErrors(formatValidationErrors(res.error));
|
2311
|
+
}
|
2312
|
+
} else if (documentId || collectionType === SINGLE_TYPES) {
|
2313
|
+
const res = await update(
|
2314
|
+
{
|
2315
|
+
collectionType,
|
2316
|
+
model,
|
2317
|
+
documentId,
|
2318
|
+
params
|
2319
|
+
},
|
2320
|
+
transformData(document)
|
2321
|
+
);
|
2322
|
+
if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
|
2323
|
+
setErrors(formatValidationErrors(res.error));
|
2324
|
+
} else {
|
2325
|
+
resetForm();
|
2326
|
+
}
|
2327
|
+
} else {
|
2328
|
+
const res = await create(
|
2329
|
+
{
|
2330
|
+
model,
|
2331
|
+
params
|
2332
|
+
},
|
2333
|
+
transformData(document)
|
2334
|
+
);
|
2335
|
+
if ("data" in res && collectionType !== SINGLE_TYPES) {
|
2336
|
+
navigate(
|
2337
|
+
{
|
2338
|
+
pathname: `../${res.data.documentId}`,
|
2339
|
+
search: rawQuery
|
2340
|
+
},
|
2341
|
+
{ replace: true, relative: "path" }
|
2342
|
+
);
|
2343
|
+
} else if ("error" in res && isBaseQueryError(res.error) && res.error.name === "ValidationError") {
|
2344
|
+
setErrors(formatValidationErrors(res.error));
|
2345
|
+
}
|
2346
|
+
}
|
2347
|
+
} finally {
|
2348
|
+
setSubmitting(false);
|
2349
|
+
}
|
2342
2350
|
}
|
2343
|
-
|
2351
|
+
};
|
2344
2352
|
};
|
2345
|
-
|
2346
|
-
|
2347
|
-
|
2348
|
-
|
2349
|
-
SingleSelect,
|
2350
|
-
{
|
2351
|
-
size: "S",
|
2352
|
-
disabled: action.disabled,
|
2353
|
-
"aria-label": action.label,
|
2354
|
-
onChange: action.onSelect,
|
2355
|
-
value: action.value,
|
2356
|
-
children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
|
2357
|
-
},
|
2358
|
-
action.id
|
2359
|
-
);
|
2360
|
-
} else {
|
2361
|
-
return null;
|
2362
|
-
}
|
2363
|
-
}) });
|
2353
|
+
UpdateAction.type = "update";
|
2354
|
+
const UNPUBLISH_DRAFT_OPTIONS = {
|
2355
|
+
KEEP: "keep",
|
2356
|
+
DISCARD: "discard"
|
2364
2357
|
};
|
2365
|
-
const
|
2366
|
-
|
2358
|
+
const UnpublishAction$1 = ({
|
2359
|
+
activeTab,
|
2360
|
+
documentId,
|
2361
|
+
model,
|
2362
|
+
collectionType,
|
2363
|
+
document
|
2364
|
+
}) => {
|
2367
2365
|
const { formatMessage } = useIntl();
|
2368
|
-
|
2369
|
-
|
2370
|
-
|
2371
|
-
|
2372
|
-
|
2373
|
-
|
2374
|
-
|
2375
|
-
|
2376
|
-
|
2377
|
-
|
2366
|
+
const { schema } = useDoc();
|
2367
|
+
const canPublish = useDocumentRBAC("UnpublishAction", ({ canPublish: canPublish2 }) => canPublish2);
|
2368
|
+
const { unpublish } = useDocumentActions();
|
2369
|
+
const [{ query }] = useQueryParams();
|
2370
|
+
const params = React.useMemo(() => buildValidParams(query), [query]);
|
2371
|
+
const { toggleNotification } = useNotification();
|
2372
|
+
const [shouldKeepDraft, setShouldKeepDraft] = React.useState(true);
|
2373
|
+
const isDocumentModified = document?.status === "modified";
|
2374
|
+
const handleChange = (value) => {
|
2375
|
+
setShouldKeepDraft(value === UNPUBLISH_DRAFT_OPTIONS.KEEP);
|
2378
2376
|
};
|
2379
|
-
|
2380
|
-
|
2381
|
-
|
2382
|
-
const navigate = useNavigate();
|
2383
|
-
const { formatMessage } = useIntl();
|
2377
|
+
if (!schema?.options?.draftAndPublish) {
|
2378
|
+
return null;
|
2379
|
+
}
|
2384
2380
|
return {
|
2381
|
+
disabled: !canPublish || activeTab === "published" || document?.status !== "published" && document?.status !== "modified",
|
2385
2382
|
label: formatMessage({
|
2386
|
-
id: "
|
2387
|
-
defaultMessage: "
|
2383
|
+
id: "app.utils.unpublish",
|
2384
|
+
defaultMessage: "Unpublish"
|
2388
2385
|
}),
|
2389
|
-
icon: /* @__PURE__ */ jsx(
|
2390
|
-
onClick: () => {
|
2391
|
-
|
2386
|
+
icon: /* @__PURE__ */ jsx(Cross, {}),
|
2387
|
+
onClick: async () => {
|
2388
|
+
if (!documentId && collectionType !== SINGLE_TYPES || isDocumentModified) {
|
2389
|
+
if (!documentId) {
|
2390
|
+
console.error(
|
2391
|
+
"You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
|
2392
|
+
);
|
2393
|
+
toggleNotification({
|
2394
|
+
message: formatMessage({
|
2395
|
+
id: "content-manager.actions.unpublish.error",
|
2396
|
+
defaultMessage: "An error occurred while trying to unpublish the document."
|
2397
|
+
}),
|
2398
|
+
type: "danger"
|
2399
|
+
});
|
2400
|
+
}
|
2401
|
+
return;
|
2402
|
+
}
|
2403
|
+
await unpublish({
|
2404
|
+
collectionType,
|
2405
|
+
model,
|
2406
|
+
documentId,
|
2407
|
+
params
|
2408
|
+
});
|
2392
2409
|
},
|
2393
|
-
|
2410
|
+
dialog: isDocumentModified ? {
|
2411
|
+
type: "dialog",
|
2412
|
+
title: formatMessage({
|
2413
|
+
id: "app.components.ConfirmDialog.title",
|
2414
|
+
defaultMessage: "Confirmation"
|
2415
|
+
}),
|
2416
|
+
content: /* @__PURE__ */ jsxs(Flex, { alignItems: "flex-start", direction: "column", gap: 6, children: [
|
2417
|
+
/* @__PURE__ */ jsxs(Flex, { width: "100%", direction: "column", gap: 2, children: [
|
2418
|
+
/* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
|
2419
|
+
/* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
|
2420
|
+
id: "content-manager.actions.unpublish.dialog.body",
|
2421
|
+
defaultMessage: "Are you sure?"
|
2422
|
+
}) })
|
2423
|
+
] }),
|
2424
|
+
/* @__PURE__ */ jsxs(
|
2425
|
+
Radio.Group,
|
2426
|
+
{
|
2427
|
+
defaultValue: UNPUBLISH_DRAFT_OPTIONS.KEEP,
|
2428
|
+
name: "discard-options",
|
2429
|
+
"aria-label": formatMessage({
|
2430
|
+
id: "content-manager.actions.unpublish.dialog.radio-label",
|
2431
|
+
defaultMessage: "Choose an option to unpublish the document."
|
2432
|
+
}),
|
2433
|
+
onValueChange: handleChange,
|
2434
|
+
children: [
|
2435
|
+
/* @__PURE__ */ jsx(Radio.Item, { checked: shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.KEEP, children: formatMessage({
|
2436
|
+
id: "content-manager.actions.unpublish.dialog.option.keep-draft",
|
2437
|
+
defaultMessage: "Keep draft"
|
2438
|
+
}) }),
|
2439
|
+
/* @__PURE__ */ jsx(Radio.Item, { checked: !shouldKeepDraft, value: UNPUBLISH_DRAFT_OPTIONS.DISCARD, children: formatMessage({
|
2440
|
+
id: "content-manager.actions.unpublish.dialog.option.replace-draft",
|
2441
|
+
defaultMessage: "Replace draft"
|
2442
|
+
}) })
|
2443
|
+
]
|
2444
|
+
}
|
2445
|
+
)
|
2446
|
+
] }),
|
2447
|
+
onConfirm: async () => {
|
2448
|
+
if (!documentId && collectionType !== SINGLE_TYPES) {
|
2449
|
+
console.error(
|
2450
|
+
"You're trying to unpublish a document without an id, this is likely a bug with Strapi. Please open an issue."
|
2451
|
+
);
|
2452
|
+
toggleNotification({
|
2453
|
+
message: formatMessage({
|
2454
|
+
id: "content-manager.actions.unpublish.error",
|
2455
|
+
defaultMessage: "An error occurred while trying to unpublish the document."
|
2456
|
+
}),
|
2457
|
+
type: "danger"
|
2458
|
+
});
|
2459
|
+
}
|
2460
|
+
await unpublish(
|
2461
|
+
{
|
2462
|
+
collectionType,
|
2463
|
+
model,
|
2464
|
+
documentId,
|
2465
|
+
params
|
2466
|
+
},
|
2467
|
+
!shouldKeepDraft
|
2468
|
+
);
|
2469
|
+
}
|
2470
|
+
} : void 0,
|
2471
|
+
variant: "danger",
|
2472
|
+
position: ["panel", "table-row"]
|
2394
2473
|
};
|
2395
2474
|
};
|
2396
|
-
|
2397
|
-
const
|
2398
|
-
|
2475
|
+
UnpublishAction$1.type = "unpublish";
|
2476
|
+
const DiscardAction = ({
|
2477
|
+
activeTab,
|
2478
|
+
documentId,
|
2479
|
+
model,
|
2480
|
+
collectionType,
|
2481
|
+
document
|
2482
|
+
}) => {
|
2399
2483
|
const { formatMessage } = useIntl();
|
2400
|
-
const
|
2401
|
-
const
|
2402
|
-
const {
|
2403
|
-
const {
|
2404
|
-
const
|
2484
|
+
const { schema } = useDoc();
|
2485
|
+
const canUpdate = useDocumentRBAC("DiscardAction", ({ canUpdate: canUpdate2 }) => canUpdate2);
|
2486
|
+
const { discard } = useDocumentActions();
|
2487
|
+
const [{ query }] = useQueryParams();
|
2488
|
+
const params = React.useMemo(() => buildValidParams(query), [query]);
|
2489
|
+
if (!schema?.options?.draftAndPublish) {
|
2490
|
+
return null;
|
2491
|
+
}
|
2405
2492
|
return {
|
2406
|
-
disabled: !
|
2493
|
+
disabled: !canUpdate || activeTab === "published" || document?.status !== "modified",
|
2407
2494
|
label: formatMessage({
|
2408
|
-
id: "content-manager.actions.
|
2409
|
-
defaultMessage: "
|
2495
|
+
id: "content-manager.actions.discard.label",
|
2496
|
+
defaultMessage: "Discard changes"
|
2410
2497
|
}),
|
2411
|
-
icon: /* @__PURE__ */ jsx(
|
2498
|
+
icon: /* @__PURE__ */ jsx(Cross, {}),
|
2499
|
+
position: ["panel", "table-row"],
|
2500
|
+
variant: "danger",
|
2412
2501
|
dialog: {
|
2413
2502
|
type: "dialog",
|
2414
2503
|
title: formatMessage({
|
@@ -2418,92 +2507,90 @@ const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
|
|
2418
2507
|
content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
|
2419
2508
|
/* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
|
2420
2509
|
/* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
|
2421
|
-
id: "content-manager.actions.
|
2510
|
+
id: "content-manager.actions.discard.dialog.body",
|
2422
2511
|
defaultMessage: "Are you sure?"
|
2423
2512
|
}) })
|
2424
2513
|
] }),
|
2425
2514
|
onConfirm: async () => {
|
2426
|
-
|
2427
|
-
|
2428
|
-
|
2429
|
-
|
2430
|
-
|
2431
|
-
|
2432
|
-
|
2433
|
-
|
2434
|
-
|
2435
|
-
|
2436
|
-
|
2437
|
-
|
2438
|
-
|
2439
|
-
|
2440
|
-
|
2441
|
-
|
2442
|
-
|
2443
|
-
|
2444
|
-
|
2445
|
-
|
2446
|
-
|
2447
|
-
|
2448
|
-
|
2449
|
-
|
2450
|
-
|
2451
|
-
|
2452
|
-
|
2453
|
-
|
2454
|
-
|
2455
|
-
|
2456
|
-
|
2457
|
-
|
2458
|
-
|
2515
|
+
await discard({
|
2516
|
+
collectionType,
|
2517
|
+
model,
|
2518
|
+
documentId,
|
2519
|
+
params
|
2520
|
+
});
|
2521
|
+
}
|
2522
|
+
}
|
2523
|
+
};
|
2524
|
+
};
|
2525
|
+
DiscardAction.type = "discard";
|
2526
|
+
const DEFAULT_ACTIONS = [PublishAction$1, UpdateAction, UnpublishAction$1, DiscardAction];
|
2527
|
+
const intervals = ["years", "months", "days", "hours", "minutes", "seconds"];
|
2528
|
+
const RelativeTime = React.forwardRef(
|
2529
|
+
({ timestamp, customIntervals = [], ...restProps }, forwardedRef) => {
|
2530
|
+
const { formatRelativeTime, formatDate, formatTime } = useIntl();
|
2531
|
+
const interval = intervalToDuration({
|
2532
|
+
start: timestamp,
|
2533
|
+
end: Date.now()
|
2534
|
+
// see https://github.com/date-fns/date-fns/issues/2891 – No idea why it's all partial it returns it every time.
|
2535
|
+
});
|
2536
|
+
const unit = intervals.find((intervalUnit) => {
|
2537
|
+
return interval[intervalUnit] > 0 && Object.keys(interval).includes(intervalUnit);
|
2538
|
+
});
|
2539
|
+
const relativeTime = isPast(timestamp) ? -interval[unit] : interval[unit];
|
2540
|
+
const customInterval = customIntervals.find(
|
2541
|
+
(custom) => interval[custom.unit] < custom.threshold
|
2542
|
+
);
|
2543
|
+
const displayText = customInterval ? customInterval.text : formatRelativeTime(relativeTime, unit, { numeric: "auto" });
|
2544
|
+
return /* @__PURE__ */ jsx(
|
2545
|
+
"time",
|
2546
|
+
{
|
2547
|
+
ref: forwardedRef,
|
2548
|
+
dateTime: timestamp.toISOString(),
|
2549
|
+
role: "time",
|
2550
|
+
title: `${formatDate(timestamp)} ${formatTime(timestamp)}`,
|
2551
|
+
...restProps,
|
2552
|
+
children: displayText
|
2459
2553
|
}
|
2460
|
-
|
2461
|
-
|
2462
|
-
|
2463
|
-
|
2554
|
+
);
|
2555
|
+
}
|
2556
|
+
);
|
2557
|
+
const getDisplayName = ({
|
2558
|
+
firstname,
|
2559
|
+
lastname,
|
2560
|
+
username,
|
2561
|
+
email
|
2562
|
+
} = {}) => {
|
2563
|
+
if (username) {
|
2564
|
+
return username;
|
2565
|
+
}
|
2566
|
+
if (firstname) {
|
2567
|
+
return `${firstname} ${lastname ?? ""}`.trim();
|
2568
|
+
}
|
2569
|
+
return email ?? "";
|
2464
2570
|
};
|
2465
|
-
|
2466
|
-
const
|
2467
|
-
const
|
2468
|
-
|
2469
|
-
const [
|
2470
|
-
{
|
2471
|
-
query: { status }
|
2472
|
-
}
|
2473
|
-
] = useQueryParams({
|
2474
|
-
status: "draft"
|
2475
|
-
});
|
2476
|
-
const { model, id, document, meta, collectionType } = useDoc();
|
2477
|
-
const plugins = useStrapiApp("Panels", (state) => state.plugins);
|
2478
|
-
const props = {
|
2479
|
-
activeTab: status,
|
2480
|
-
model,
|
2481
|
-
documentId: id,
|
2482
|
-
document: isCloning ? void 0 : document,
|
2483
|
-
meta: isCloning ? void 0 : meta,
|
2484
|
-
collectionType
|
2485
|
-
};
|
2486
|
-
return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
|
2487
|
-
DescriptionComponentRenderer,
|
2488
|
-
{
|
2489
|
-
props,
|
2490
|
-
descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
|
2491
|
-
children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
|
2492
|
-
}
|
2493
|
-
) });
|
2571
|
+
const capitalise = (str) => str.charAt(0).toUpperCase() + str.slice(1);
|
2572
|
+
const DocumentStatus = ({ status = "draft", ...restProps }) => {
|
2573
|
+
const statusVariant = status === "draft" ? "secondary" : status === "published" ? "success" : "alternative";
|
2574
|
+
return /* @__PURE__ */ jsx(Status, { ...restProps, showBullet: false, size: "S", variant: statusVariant, children: /* @__PURE__ */ jsx(Typography, { tag: "span", variant: "omega", fontWeight: "bold", children: capitalise(status) }) });
|
2494
2575
|
};
|
2495
|
-
const
|
2576
|
+
const Header = ({ isCreating, status, title: documentTitle = "Untitled" }) => {
|
2496
2577
|
const { formatMessage } = useIntl();
|
2497
|
-
|
2498
|
-
|
2499
|
-
|
2500
|
-
|
2501
|
-
|
2502
|
-
|
2503
|
-
|
2578
|
+
const isCloning = useMatch(CLONE_PATH) !== null;
|
2579
|
+
const title = isCreating ? formatMessage({
|
2580
|
+
id: "content-manager.containers.edit.title.new",
|
2581
|
+
defaultMessage: "Create an entry"
|
2582
|
+
}) : documentTitle;
|
2583
|
+
return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", paddingTop: 6, paddingBottom: 4, gap: 2, children: [
|
2584
|
+
/* @__PURE__ */ jsx(BackButton, {}),
|
2585
|
+
/* @__PURE__ */ jsxs(Flex, { width: "100%", justifyContent: "space-between", gap: "80px", alignItems: "flex-start", children: [
|
2586
|
+
/* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: title }),
|
2587
|
+
/* @__PURE__ */ jsx(HeaderToolbar, {})
|
2588
|
+
] }),
|
2589
|
+
status ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(DocumentStatus, { status: isCloning ? "draft" : status }) }) : null
|
2590
|
+
] });
|
2504
2591
|
};
|
2505
|
-
|
2506
|
-
const
|
2592
|
+
const HeaderToolbar = () => {
|
2593
|
+
const { formatMessage } = useIntl();
|
2507
2594
|
const isCloning = useMatch(CLONE_PATH) !== null;
|
2508
2595
|
const [
|
2509
2596
|
{
|
@@ -2511,355 +2598,432 @@ const ActionsPanelContent = () => {
|
|
2511
2598
|
}
|
2512
2599
|
] = useQueryParams();
|
2513
2600
|
const { model, id, document, meta, collectionType } = useDoc();
|
2514
|
-
const plugins = useStrapiApp("
|
2515
|
-
|
2516
|
-
activeTab: status,
|
2517
|
-
model,
|
2518
|
-
documentId: id,
|
2519
|
-
document: isCloning ? void 0 : document,
|
2520
|
-
meta: isCloning ? void 0 : meta,
|
2521
|
-
collectionType
|
2522
|
-
};
|
2523
|
-
return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
|
2601
|
+
const plugins = useStrapiApp("HeaderToolbar", (state) => state.plugins);
|
2602
|
+
return /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
2524
2603
|
/* @__PURE__ */ jsx(
|
2525
2604
|
DescriptionComponentRenderer,
|
2526
2605
|
{
|
2527
|
-
props
|
2528
|
-
|
2529
|
-
|
2606
|
+
props: {
|
2607
|
+
activeTab: status,
|
2608
|
+
model,
|
2609
|
+
documentId: id,
|
2610
|
+
document: isCloning ? void 0 : document,
|
2611
|
+
meta: isCloning ? void 0 : meta,
|
2612
|
+
collectionType
|
2613
|
+
},
|
2614
|
+
descriptions: plugins["content-manager"].apis.getHeaderActions(),
|
2615
|
+
children: (actions2) => {
|
2616
|
+
if (actions2.length > 0) {
|
2617
|
+
return /* @__PURE__ */ jsx(HeaderActions, { actions: actions2 });
|
2618
|
+
} else {
|
2619
|
+
return null;
|
2620
|
+
}
|
2621
|
+
}
|
2530
2622
|
}
|
2531
2623
|
),
|
2532
|
-
/* @__PURE__ */ jsx(
|
2624
|
+
/* @__PURE__ */ jsx(
|
2625
|
+
DescriptionComponentRenderer,
|
2626
|
+
{
|
2627
|
+
props: {
|
2628
|
+
activeTab: status,
|
2629
|
+
model,
|
2630
|
+
documentId: id,
|
2631
|
+
document: isCloning ? void 0 : document,
|
2632
|
+
meta: isCloning ? void 0 : meta,
|
2633
|
+
collectionType
|
2634
|
+
},
|
2635
|
+
descriptions: plugins["content-manager"].apis.getDocumentActions(),
|
2636
|
+
children: (actions2) => {
|
2637
|
+
const headerActions = actions2.filter((action) => {
|
2638
|
+
const positions = Array.isArray(action.position) ? action.position : [action.position];
|
2639
|
+
return positions.includes("header");
|
2640
|
+
});
|
2641
|
+
return /* @__PURE__ */ jsx(
|
2642
|
+
DocumentActionsMenu,
|
2643
|
+
{
|
2644
|
+
actions: headerActions,
|
2645
|
+
label: formatMessage({
|
2646
|
+
id: "content-manager.containers.edit.header.more-actions",
|
2647
|
+
defaultMessage: "More actions"
|
2648
|
+
}),
|
2649
|
+
children: /* @__PURE__ */ jsx(Information, { activeTab: status })
|
2650
|
+
}
|
2651
|
+
);
|
2652
|
+
}
|
2653
|
+
}
|
2654
|
+
)
|
2533
2655
|
] });
|
2534
2656
|
};
|
2535
|
-
const
|
2536
|
-
|
2537
|
-
|
2657
|
+
const Information = ({ activeTab }) => {
|
2658
|
+
const { formatMessage } = useIntl();
|
2659
|
+
const { document, meta } = useDoc();
|
2660
|
+
if (!document || !document.id) {
|
2661
|
+
return null;
|
2662
|
+
}
|
2663
|
+
const createAndUpdateDocument = activeTab === "draft" ? document : meta?.availableStatus.find((status) => status.publishedAt === null);
|
2664
|
+
const publishDocument = activeTab === "published" ? document : meta?.availableStatus.find((status) => status.publishedAt !== null);
|
2665
|
+
const creator = createAndUpdateDocument?.[CREATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[CREATED_BY_ATTRIBUTE_NAME]) : null;
|
2666
|
+
const updator = createAndUpdateDocument?.[UPDATED_BY_ATTRIBUTE_NAME] ? getDisplayName(createAndUpdateDocument[UPDATED_BY_ATTRIBUTE_NAME]) : null;
|
2667
|
+
const information = [
|
2538
2668
|
{
|
2539
|
-
|
2540
|
-
|
2541
|
-
|
2542
|
-
|
2543
|
-
borderColor: "neutral150",
|
2544
|
-
hasRadius: true,
|
2545
|
-
paddingBottom: 4,
|
2546
|
-
paddingLeft: 4,
|
2547
|
-
paddingRight: 4,
|
2548
|
-
paddingTop: 4,
|
2549
|
-
shadow: "tableShadow",
|
2550
|
-
gap: 3,
|
2551
|
-
direction: "column",
|
2552
|
-
justifyContent: "stretch",
|
2553
|
-
alignItems: "flex-start",
|
2554
|
-
children: [
|
2555
|
-
/* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", children: title }),
|
2556
|
-
children
|
2557
|
-
]
|
2558
|
-
}
|
2559
|
-
);
|
2560
|
-
});
|
2561
|
-
const HOOKS = {
|
2562
|
-
/**
|
2563
|
-
* Hook that allows to mutate the displayed headers of the list view table
|
2564
|
-
* @constant
|
2565
|
-
* @type {string}
|
2566
|
-
*/
|
2567
|
-
INJECT_COLUMN_IN_TABLE: "Admin/CM/pages/ListView/inject-column-in-table",
|
2568
|
-
/**
|
2569
|
-
* Hook that allows to mutate the CM's collection types links pre-set filters
|
2570
|
-
* @constant
|
2571
|
-
* @type {string}
|
2572
|
-
*/
|
2573
|
-
MUTATE_COLLECTION_TYPES_LINKS: "Admin/CM/pages/App/mutate-collection-types-links",
|
2574
|
-
/**
|
2575
|
-
* Hook that allows to mutate the CM's edit view layout
|
2576
|
-
* @constant
|
2577
|
-
* @type {string}
|
2578
|
-
*/
|
2579
|
-
MUTATE_EDIT_VIEW_LAYOUT: "Admin/CM/pages/EditView/mutate-edit-view-layout",
|
2580
|
-
/**
|
2581
|
-
* Hook that allows to mutate the CM's single types links pre-set filters
|
2582
|
-
* @constant
|
2583
|
-
* @type {string}
|
2584
|
-
*/
|
2585
|
-
MUTATE_SINGLE_TYPES_LINKS: "Admin/CM/pages/App/mutate-single-types-links"
|
2586
|
-
};
|
2587
|
-
const contentTypesApi = contentManagerApi.injectEndpoints({
|
2588
|
-
endpoints: (builder) => ({
|
2589
|
-
getContentTypeConfiguration: builder.query({
|
2590
|
-
query: (uid) => ({
|
2591
|
-
url: `/content-manager/content-types/${uid}/configuration`,
|
2592
|
-
method: "GET"
|
2669
|
+
isDisplayed: !!publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME],
|
2670
|
+
label: formatMessage({
|
2671
|
+
id: "content-manager.containers.edit.information.last-published.label",
|
2672
|
+
defaultMessage: "Published"
|
2593
2673
|
}),
|
2594
|
-
|
2595
|
-
|
2596
|
-
|
2597
|
-
|
2598
|
-
|
2599
|
-
|
2600
|
-
|
2601
|
-
|
2602
|
-
|
2603
|
-
|
2604
|
-
|
2605
|
-
|
2606
|
-
|
2607
|
-
|
2608
|
-
|
2609
|
-
|
2674
|
+
value: formatMessage(
|
2675
|
+
{
|
2676
|
+
id: "content-manager.containers.edit.information.last-published.value",
|
2677
|
+
defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
|
2678
|
+
},
|
2679
|
+
{
|
2680
|
+
time: /* @__PURE__ */ jsx(RelativeTime, { timestamp: new Date(publishDocument?.[PUBLISHED_AT_ATTRIBUTE_NAME]) }),
|
2681
|
+
isAnonymous: !publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME],
|
2682
|
+
author: publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME] ? getDisplayName(publishDocument?.[PUBLISHED_BY_ATTRIBUTE_NAME]) : null
|
2683
|
+
}
|
2684
|
+
)
|
2685
|
+
},
|
2686
|
+
{
|
2687
|
+
isDisplayed: !!createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME],
|
2688
|
+
label: formatMessage({
|
2689
|
+
id: "content-manager.containers.edit.information.last-draft.label",
|
2690
|
+
defaultMessage: "Updated"
|
2691
|
+
}),
|
2692
|
+
value: formatMessage(
|
2693
|
+
{
|
2694
|
+
id: "content-manager.containers.edit.information.last-draft.value",
|
2695
|
+
defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
|
2696
|
+
},
|
2697
|
+
{
|
2698
|
+
time: /* @__PURE__ */ jsx(
|
2699
|
+
RelativeTime,
|
2700
|
+
{
|
2701
|
+
timestamp: new Date(createAndUpdateDocument?.[UPDATED_AT_ATTRIBUTE_NAME])
|
2702
|
+
}
|
2703
|
+
),
|
2704
|
+
isAnonymous: !updator,
|
2705
|
+
author: updator
|
2706
|
+
}
|
2707
|
+
)
|
2708
|
+
},
|
2709
|
+
{
|
2710
|
+
isDisplayed: !!createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME],
|
2711
|
+
label: formatMessage({
|
2712
|
+
id: "content-manager.containers.edit.information.document.label",
|
2713
|
+
defaultMessage: "Created"
|
2610
2714
|
}),
|
2611
|
-
|
2612
|
-
|
2613
|
-
|
2614
|
-
|
2615
|
-
|
2616
|
-
{
|
2617
|
-
|
2618
|
-
|
2619
|
-
|
2620
|
-
|
2621
|
-
|
2622
|
-
|
2623
|
-
|
2624
|
-
|
2625
|
-
}
|
2626
|
-
|
2627
|
-
|
2628
|
-
|
2629
|
-
|
2630
|
-
|
2631
|
-
|
2632
|
-
|
2633
|
-
|
2634
|
-
|
2635
|
-
|
2636
|
-
|
2637
|
-
|
2638
|
-
|
2639
|
-
|
2715
|
+
value: formatMessage(
|
2716
|
+
{
|
2717
|
+
id: "content-manager.containers.edit.information.document.value",
|
2718
|
+
defaultMessage: `{time}{isAnonymous, select, true {} other { by {author}}}`
|
2719
|
+
},
|
2720
|
+
{
|
2721
|
+
time: /* @__PURE__ */ jsx(
|
2722
|
+
RelativeTime,
|
2723
|
+
{
|
2724
|
+
timestamp: new Date(createAndUpdateDocument?.[CREATED_AT_ATTRIBUTE_NAME])
|
2725
|
+
}
|
2726
|
+
),
|
2727
|
+
isAnonymous: !creator,
|
2728
|
+
author: creator
|
2729
|
+
}
|
2730
|
+
)
|
2731
|
+
}
|
2732
|
+
].filter((info) => info.isDisplayed);
|
2733
|
+
return /* @__PURE__ */ jsx(
|
2734
|
+
Flex,
|
2735
|
+
{
|
2736
|
+
borderWidth: "1px 0 0 0",
|
2737
|
+
borderStyle: "solid",
|
2738
|
+
borderColor: "neutral150",
|
2739
|
+
direction: "column",
|
2740
|
+
marginTop: 2,
|
2741
|
+
tag: "dl",
|
2742
|
+
padding: 5,
|
2743
|
+
gap: 3,
|
2744
|
+
alignItems: "flex-start",
|
2745
|
+
marginLeft: "-0.4rem",
|
2746
|
+
marginRight: "-0.4rem",
|
2747
|
+
width: "calc(100% + 8px)",
|
2748
|
+
children: information.map((info) => /* @__PURE__ */ jsxs(Flex, { gap: 1, direction: "column", alignItems: "flex-start", children: [
|
2749
|
+
/* @__PURE__ */ jsx(Typography, { tag: "dt", variant: "pi", fontWeight: "bold", children: info.label }),
|
2750
|
+
/* @__PURE__ */ jsx(Typography, { tag: "dd", variant: "pi", textColor: "neutral600", children: info.value })
|
2751
|
+
] }, info.label))
|
2752
|
+
}
|
2640
2753
|
);
|
2641
|
-
return {
|
2642
|
-
name: mainFieldName,
|
2643
|
-
type: mainFieldType ?? "string"
|
2644
|
-
};
|
2645
|
-
};
|
2646
|
-
const DEFAULT_SETTINGS = {
|
2647
|
-
bulkable: false,
|
2648
|
-
filterable: false,
|
2649
|
-
searchable: false,
|
2650
|
-
pagination: false,
|
2651
|
-
defaultSortBy: "",
|
2652
|
-
defaultSortOrder: "asc",
|
2653
|
-
mainField: "id",
|
2654
|
-
pageSize: 10
|
2655
2754
|
};
|
2656
|
-
const
|
2657
|
-
const
|
2658
|
-
const
|
2659
|
-
|
2660
|
-
|
2661
|
-
|
2662
|
-
|
2663
|
-
|
2664
|
-
|
2665
|
-
|
2666
|
-
error,
|
2667
|
-
isFetching: isFetchingConfigs
|
2668
|
-
} = useGetContentTypeConfigurationQuery(model);
|
2669
|
-
const isLoading = isLoadingSchemas || isFetchingConfigs || isLoadingConfigs;
|
2670
|
-
React.useEffect(() => {
|
2671
|
-
if (error) {
|
2672
|
-
toggleNotification({
|
2673
|
-
type: "danger",
|
2674
|
-
message: formatAPIError(error)
|
2675
|
-
});
|
2755
|
+
const HeaderActions = ({ actions: actions2 }) => {
|
2756
|
+
const [dialogId, setDialogId] = React.useState(null);
|
2757
|
+
const handleClick = (action) => async (e) => {
|
2758
|
+
if (!("options" in action)) {
|
2759
|
+
const { onClick = () => false, dialog, id } = action;
|
2760
|
+
const muteDialog = await onClick(e);
|
2761
|
+
if (dialog && !muteDialog) {
|
2762
|
+
e.preventDefault();
|
2763
|
+
setDialogId(id);
|
2764
|
+
}
|
2676
2765
|
}
|
2677
|
-
}, [error, formatAPIError, toggleNotification]);
|
2678
|
-
const editLayout = React.useMemo(
|
2679
|
-
() => data && !isLoading ? formatEditLayout(data, { schemas, schema, components }) : {
|
2680
|
-
layout: [],
|
2681
|
-
components: {},
|
2682
|
-
metadatas: {},
|
2683
|
-
options: {},
|
2684
|
-
settings: DEFAULT_SETTINGS
|
2685
|
-
},
|
2686
|
-
[data, isLoading, schemas, schema, components]
|
2687
|
-
);
|
2688
|
-
const listLayout = React.useMemo(() => {
|
2689
|
-
return data && !isLoading ? formatListLayout(data, { schemas, schema, components }) : {
|
2690
|
-
layout: [],
|
2691
|
-
metadatas: {},
|
2692
|
-
options: {},
|
2693
|
-
settings: DEFAULT_SETTINGS
|
2694
|
-
};
|
2695
|
-
}, [data, isLoading, schemas, schema, components]);
|
2696
|
-
const { layout: edit } = React.useMemo(
|
2697
|
-
() => runHookWaterfall(HOOKS.MUTATE_EDIT_VIEW_LAYOUT, {
|
2698
|
-
layout: editLayout,
|
2699
|
-
query
|
2700
|
-
}),
|
2701
|
-
[editLayout, query, runHookWaterfall]
|
2702
|
-
);
|
2703
|
-
return {
|
2704
|
-
error,
|
2705
|
-
isLoading,
|
2706
|
-
edit,
|
2707
|
-
list: listLayout
|
2708
2766
|
};
|
2709
|
-
|
2710
|
-
|
2711
|
-
|
2712
|
-
return
|
2713
|
-
|
2714
|
-
|
2715
|
-
|
2716
|
-
|
2717
|
-
|
2718
|
-
|
2719
|
-
|
2720
|
-
|
2721
|
-
|
2722
|
-
|
2723
|
-
|
2724
|
-
|
2725
|
-
schemas
|
2726
|
-
).reduce((panels, row) => {
|
2727
|
-
if (row.some((field) => field.type === "dynamiczone")) {
|
2728
|
-
panels.push([row]);
|
2729
|
-
currentPanelIndex += 2;
|
2767
|
+
const handleClose = () => {
|
2768
|
+
setDialogId(null);
|
2769
|
+
};
|
2770
|
+
return /* @__PURE__ */ jsx(Flex, { gap: 1, children: actions2.map((action) => {
|
2771
|
+
if (action.options) {
|
2772
|
+
return /* @__PURE__ */ jsx(
|
2773
|
+
SingleSelect,
|
2774
|
+
{
|
2775
|
+
size: "S",
|
2776
|
+
onChange: action.onSelect,
|
2777
|
+
"aria-label": action.label,
|
2778
|
+
...action,
|
2779
|
+
children: action.options.map(({ label, ...option }) => /* @__PURE__ */ jsx(SingleSelectOption, { ...option, children: label }, option.value))
|
2780
|
+
},
|
2781
|
+
action.id
|
2782
|
+
);
|
2730
2783
|
} else {
|
2731
|
-
if (
|
2732
|
-
|
2784
|
+
if (action.type === "icon") {
|
2785
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
2786
|
+
/* @__PURE__ */ jsx(
|
2787
|
+
IconButton,
|
2788
|
+
{
|
2789
|
+
disabled: action.disabled,
|
2790
|
+
label: action.label,
|
2791
|
+
size: "S",
|
2792
|
+
onClick: handleClick(action),
|
2793
|
+
children: action.icon
|
2794
|
+
}
|
2795
|
+
),
|
2796
|
+
action.dialog ? /* @__PURE__ */ jsx(
|
2797
|
+
HeaderActionDialog,
|
2798
|
+
{
|
2799
|
+
...action.dialog,
|
2800
|
+
isOpen: dialogId === action.id,
|
2801
|
+
onClose: handleClose
|
2802
|
+
}
|
2803
|
+
) : null
|
2804
|
+
] }, action.id);
|
2733
2805
|
}
|
2734
|
-
panels[currentPanelIndex].push(row);
|
2735
2806
|
}
|
2736
|
-
|
2737
|
-
|
2738
|
-
|
2739
|
-
|
2740
|
-
|
2741
|
-
|
2742
|
-
|
2743
|
-
|
2744
|
-
|
2745
|
-
|
2746
|
-
|
2747
|
-
|
2748
|
-
|
2749
|
-
|
2750
|
-
|
2751
|
-
|
2752
|
-
|
2807
|
+
}) });
|
2808
|
+
};
|
2809
|
+
const HeaderActionDialog = ({
|
2810
|
+
onClose,
|
2811
|
+
onCancel,
|
2812
|
+
title,
|
2813
|
+
content: Content,
|
2814
|
+
isOpen
|
2815
|
+
}) => {
|
2816
|
+
const handleClose = async () => {
|
2817
|
+
if (onCancel) {
|
2818
|
+
await onCancel();
|
2819
|
+
}
|
2820
|
+
onClose();
|
2821
|
+
};
|
2822
|
+
return /* @__PURE__ */ jsx(Dialog.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
|
2823
|
+
/* @__PURE__ */ jsx(Dialog.Header, { children: title }),
|
2824
|
+
typeof Content === "function" ? /* @__PURE__ */ jsx(Content, { onClose: handleClose }) : Content
|
2825
|
+
] }) });
|
2826
|
+
};
|
2827
|
+
const ConfigureTheViewAction = ({ collectionType, model }) => {
|
2828
|
+
const navigate = useNavigate();
|
2829
|
+
const { formatMessage } = useIntl();
|
2830
|
+
return {
|
2831
|
+
label: formatMessage({
|
2832
|
+
id: "app.links.configure-view",
|
2833
|
+
defaultMessage: "Configure the view"
|
2834
|
+
}),
|
2835
|
+
icon: /* @__PURE__ */ jsx(ListPlus, {}),
|
2836
|
+
onClick: () => {
|
2837
|
+
navigate(`../${collectionType}/${model}/configurations/edit`);
|
2753
2838
|
},
|
2754
|
-
|
2755
|
-
|
2756
|
-
|
2757
|
-
|
2758
|
-
|
2759
|
-
|
2760
|
-
|
2761
|
-
|
2839
|
+
position: "header"
|
2840
|
+
};
|
2841
|
+
};
|
2842
|
+
ConfigureTheViewAction.type = "configure-the-view";
|
2843
|
+
const EditTheModelAction = ({ model }) => {
|
2844
|
+
const navigate = useNavigate();
|
2845
|
+
const { formatMessage } = useIntl();
|
2846
|
+
return {
|
2847
|
+
label: formatMessage({
|
2848
|
+
id: "content-manager.link-to-ctb",
|
2849
|
+
defaultMessage: "Edit the model"
|
2850
|
+
}),
|
2851
|
+
icon: /* @__PURE__ */ jsx(Pencil, {}),
|
2852
|
+
onClick: () => {
|
2853
|
+
navigate(`/plugins/content-type-builder/content-types/${model}`);
|
2762
2854
|
},
|
2763
|
-
|
2764
|
-
|
2855
|
+
position: "header"
|
2856
|
+
};
|
2857
|
+
};
|
2858
|
+
EditTheModelAction.type = "edit-the-model";
|
2859
|
+
const DeleteAction$1 = ({ documentId, model, collectionType, document }) => {
|
2860
|
+
const navigate = useNavigate();
|
2861
|
+
const { formatMessage } = useIntl();
|
2862
|
+
const listViewPathMatch = useMatch(LIST_PATH);
|
2863
|
+
const canDelete = useDocumentRBAC("DeleteAction", (state) => state.canDelete);
|
2864
|
+
const { delete: deleteAction } = useDocumentActions();
|
2865
|
+
const { toggleNotification } = useNotification();
|
2866
|
+
const setSubmitting = useForm("DeleteAction", (state) => state.setSubmitting);
|
2867
|
+
const isLocalized = document?.locale != null;
|
2765
2868
|
return {
|
2766
|
-
|
2767
|
-
|
2768
|
-
|
2769
|
-
|
2770
|
-
|
2771
|
-
|
2869
|
+
disabled: !canDelete || !document,
|
2870
|
+
label: formatMessage(
|
2871
|
+
{
|
2872
|
+
id: "content-manager.actions.delete.label",
|
2873
|
+
defaultMessage: "Delete entry{isLocalized, select, true { (all locales)} other {}}"
|
2874
|
+
},
|
2875
|
+
{ isLocalized }
|
2876
|
+
),
|
2877
|
+
icon: /* @__PURE__ */ jsx(Trash, {}),
|
2878
|
+
dialog: {
|
2879
|
+
type: "dialog",
|
2880
|
+
title: formatMessage({
|
2881
|
+
id: "app.components.ConfirmDialog.title",
|
2882
|
+
defaultMessage: "Confirmation"
|
2883
|
+
}),
|
2884
|
+
content: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
|
2885
|
+
/* @__PURE__ */ jsx(WarningCircle, { width: "24px", height: "24px", fill: "danger600" }),
|
2886
|
+
/* @__PURE__ */ jsx(Typography, { tag: "p", variant: "omega", textAlign: "center", children: formatMessage({
|
2887
|
+
id: "content-manager.actions.delete.dialog.body",
|
2888
|
+
defaultMessage: "Are you sure?"
|
2889
|
+
}) })
|
2890
|
+
] }),
|
2891
|
+
onConfirm: async () => {
|
2892
|
+
if (!listViewPathMatch) {
|
2893
|
+
setSubmitting(true);
|
2894
|
+
}
|
2895
|
+
try {
|
2896
|
+
if (!documentId && collectionType !== SINGLE_TYPES) {
|
2897
|
+
console.error(
|
2898
|
+
"You're trying to delete a document without an id, this is likely a bug with Strapi. Please open an issue."
|
2899
|
+
);
|
2900
|
+
toggleNotification({
|
2901
|
+
message: formatMessage({
|
2902
|
+
id: "content-manager.actions.delete.error",
|
2903
|
+
defaultMessage: "An error occurred while trying to delete the document."
|
2904
|
+
}),
|
2905
|
+
type: "danger"
|
2906
|
+
});
|
2907
|
+
return;
|
2908
|
+
}
|
2909
|
+
const res = await deleteAction({
|
2910
|
+
documentId,
|
2911
|
+
model,
|
2912
|
+
collectionType,
|
2913
|
+
params: {
|
2914
|
+
locale: "*"
|
2915
|
+
}
|
2916
|
+
});
|
2917
|
+
if (!("error" in res)) {
|
2918
|
+
navigate({ pathname: `../${collectionType}/${model}` }, { replace: true });
|
2919
|
+
}
|
2920
|
+
} finally {
|
2921
|
+
if (!listViewPathMatch) {
|
2922
|
+
setSubmitting(false);
|
2923
|
+
}
|
2924
|
+
}
|
2925
|
+
}
|
2772
2926
|
},
|
2773
|
-
|
2774
|
-
|
2775
|
-
...schema?.pluginOptions,
|
2776
|
-
...data.contentType.options
|
2777
|
-
}
|
2927
|
+
variant: "danger",
|
2928
|
+
position: ["header", "table-row"]
|
2778
2929
|
};
|
2779
2930
|
};
|
2780
|
-
|
2781
|
-
|
2782
|
-
|
2783
|
-
|
2784
|
-
|
2785
|
-
|
2786
|
-
}
|
2787
|
-
|
2788
|
-
|
2789
|
-
|
2790
|
-
|
2791
|
-
|
2792
|
-
|
2793
|
-
|
2794
|
-
|
2795
|
-
|
2796
|
-
|
2797
|
-
|
2798
|
-
|
2799
|
-
|
2800
|
-
|
2801
|
-
|
2802
|
-
|
2803
|
-
|
2804
|
-
|
2805
|
-
|
2806
|
-
}
|
2807
|
-
}
|
2808
|
-
);
|
2931
|
+
DeleteAction$1.type = "delete";
|
2932
|
+
const DEFAULT_HEADER_ACTIONS = [EditTheModelAction, ConfigureTheViewAction, DeleteAction$1];
|
2933
|
+
const Panels = () => {
|
2934
|
+
const isCloning = useMatch(CLONE_PATH) !== null;
|
2935
|
+
const [
|
2936
|
+
{
|
2937
|
+
query: { status }
|
2938
|
+
}
|
2939
|
+
] = useQueryParams({
|
2940
|
+
status: "draft"
|
2941
|
+
});
|
2942
|
+
const { model, id, document, meta, collectionType } = useDoc();
|
2943
|
+
const plugins = useStrapiApp("Panels", (state) => state.plugins);
|
2944
|
+
const props = {
|
2945
|
+
activeTab: status,
|
2946
|
+
model,
|
2947
|
+
documentId: id,
|
2948
|
+
document: isCloning ? void 0 : document,
|
2949
|
+
meta: isCloning ? void 0 : meta,
|
2950
|
+
collectionType
|
2951
|
+
};
|
2952
|
+
return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: /* @__PURE__ */ jsx(
|
2953
|
+
DescriptionComponentRenderer,
|
2954
|
+
{
|
2955
|
+
props,
|
2956
|
+
descriptions: plugins["content-manager"].apis.getEditViewSidePanels(),
|
2957
|
+
children: (panels) => panels.map(({ content, id: id2, ...description }) => /* @__PURE__ */ jsx(Panel, { ...description, children: content }, id2))
|
2958
|
+
}
|
2959
|
+
) });
|
2809
2960
|
};
|
2810
|
-
const
|
2811
|
-
|
2812
|
-
schema,
|
2813
|
-
components
|
2814
|
-
}) => {
|
2815
|
-
const listMetadatas = Object.entries(data.contentType.metadatas).reduce(
|
2816
|
-
(acc, [attribute, metadata]) => {
|
2817
|
-
return {
|
2818
|
-
...acc,
|
2819
|
-
[attribute]: metadata.list
|
2820
|
-
};
|
2821
|
-
},
|
2822
|
-
{}
|
2823
|
-
);
|
2824
|
-
const listAttributes = convertListLayoutToFieldLayouts(
|
2825
|
-
data.contentType.layouts.list,
|
2826
|
-
schema?.attributes,
|
2827
|
-
listMetadatas,
|
2828
|
-
{ configurations: data.components, schemas: components },
|
2829
|
-
schemas
|
2830
|
-
);
|
2961
|
+
const ActionsPanel = () => {
|
2962
|
+
const { formatMessage } = useIntl();
|
2831
2963
|
return {
|
2832
|
-
|
2833
|
-
|
2834
|
-
|
2835
|
-
|
2836
|
-
|
2837
|
-
...schema?.pluginOptions,
|
2838
|
-
...data.contentType.options
|
2839
|
-
}
|
2964
|
+
title: formatMessage({
|
2965
|
+
id: "content-manager.containers.edit.panels.default.title",
|
2966
|
+
defaultMessage: "Entry"
|
2967
|
+
}),
|
2968
|
+
content: /* @__PURE__ */ jsx(ActionsPanelContent, {})
|
2840
2969
|
};
|
2841
2970
|
};
|
2842
|
-
|
2843
|
-
|
2844
|
-
|
2845
|
-
|
2846
|
-
|
2971
|
+
ActionsPanel.type = "actions";
|
2972
|
+
const ActionsPanelContent = () => {
|
2973
|
+
const isCloning = useMatch(CLONE_PATH) !== null;
|
2974
|
+
const [
|
2975
|
+
{
|
2976
|
+
query: { status = "draft" }
|
2847
2977
|
}
|
2848
|
-
|
2849
|
-
|
2850
|
-
|
2851
|
-
|
2852
|
-
|
2853
|
-
|
2854
|
-
|
2855
|
-
|
2856
|
-
|
2857
|
-
|
2858
|
-
|
2859
|
-
|
2860
|
-
|
2861
|
-
|
2978
|
+
] = useQueryParams();
|
2979
|
+
const { model, id, document, meta, collectionType } = useDoc();
|
2980
|
+
const plugins = useStrapiApp("ActionsPanel", (state) => state.plugins);
|
2981
|
+
const props = {
|
2982
|
+
activeTab: status,
|
2983
|
+
model,
|
2984
|
+
documentId: id,
|
2985
|
+
document: isCloning ? void 0 : document,
|
2986
|
+
meta: isCloning ? void 0 : meta,
|
2987
|
+
collectionType
|
2988
|
+
};
|
2989
|
+
return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, width: "100%", children: [
|
2990
|
+
/* @__PURE__ */ jsx(
|
2991
|
+
DescriptionComponentRenderer,
|
2992
|
+
{
|
2993
|
+
props,
|
2994
|
+
descriptions: plugins["content-manager"].apis.getDocumentActions(),
|
2995
|
+
children: (actions2) => /* @__PURE__ */ jsx(DocumentActions, { actions: actions2 })
|
2996
|
+
}
|
2997
|
+
),
|
2998
|
+
/* @__PURE__ */ jsx(InjectionZone, { area: "editView.right-links", slug: model })
|
2999
|
+
] });
|
2862
3000
|
};
|
3001
|
+
const Panel = React.forwardRef(({ children, title }, ref) => {
|
3002
|
+
return /* @__PURE__ */ jsxs(
|
3003
|
+
Flex,
|
3004
|
+
{
|
3005
|
+
ref,
|
3006
|
+
tag: "aside",
|
3007
|
+
"aria-labelledby": "additional-information",
|
3008
|
+
background: "neutral0",
|
3009
|
+
borderColor: "neutral150",
|
3010
|
+
hasRadius: true,
|
3011
|
+
paddingBottom: 4,
|
3012
|
+
paddingLeft: 4,
|
3013
|
+
paddingRight: 4,
|
3014
|
+
paddingTop: 4,
|
3015
|
+
shadow: "tableShadow",
|
3016
|
+
gap: 3,
|
3017
|
+
direction: "column",
|
3018
|
+
justifyContent: "stretch",
|
3019
|
+
alignItems: "flex-start",
|
3020
|
+
children: [
|
3021
|
+
/* @__PURE__ */ jsx(Typography, { tag: "h2", variant: "sigma", textTransform: "uppercase", textColor: "neutral600", children: title }),
|
3022
|
+
children
|
3023
|
+
]
|
3024
|
+
}
|
3025
|
+
);
|
3026
|
+
});
|
2863
3027
|
const ConfirmBulkActionDialog = ({
|
2864
3028
|
onToggleDialog,
|
2865
3029
|
isOpen = false,
|
@@ -2898,6 +3062,7 @@ const ConfirmDialogPublishAll = ({
|
|
2898
3062
|
const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(getTranslation);
|
2899
3063
|
const { model, schema } = useDoc();
|
2900
3064
|
const [{ query }] = useQueryParams();
|
3065
|
+
const enableDraftRelationsCount = false;
|
2901
3066
|
const {
|
2902
3067
|
data: countDraftRelations = 0,
|
2903
3068
|
isLoading,
|
@@ -2909,7 +3074,7 @@ const ConfirmDialogPublishAll = ({
|
|
2909
3074
|
locale: query?.plugins?.i18n?.locale
|
2910
3075
|
},
|
2911
3076
|
{
|
2912
|
-
skip:
|
3077
|
+
skip: !enableDraftRelationsCount
|
2913
3078
|
}
|
2914
3079
|
);
|
2915
3080
|
React.useEffect(() => {
|
@@ -3094,7 +3259,7 @@ const SelectedEntriesTableContent = ({
|
|
3094
3259
|
status: row.status
|
3095
3260
|
}
|
3096
3261
|
) }),
|
3097
|
-
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(
|
3262
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(
|
3098
3263
|
IconButton,
|
3099
3264
|
{
|
3100
3265
|
tag: Link,
|
@@ -3117,9 +3282,10 @@ const SelectedEntriesTableContent = ({
|
|
3117
3282
|
),
|
3118
3283
|
target: "_blank",
|
3119
3284
|
marginLeft: "auto",
|
3120
|
-
|
3285
|
+
variant: "ghost",
|
3286
|
+
children: /* @__PURE__ */ jsx(Pencil, { width: "1.6rem", height: "1.6rem" })
|
3121
3287
|
}
|
3122
|
-
) })
|
3288
|
+
) }) })
|
3123
3289
|
] }, row.id)) })
|
3124
3290
|
] });
|
3125
3291
|
};
|
@@ -3156,7 +3322,13 @@ const SelectedEntriesModalContent = ({
|
|
3156
3322
|
);
|
3157
3323
|
const { rows, validationErrors } = React.useMemo(() => {
|
3158
3324
|
if (data.length > 0 && schema) {
|
3159
|
-
const validate = createYupSchema(
|
3325
|
+
const validate = createYupSchema(
|
3326
|
+
schema.attributes,
|
3327
|
+
components,
|
3328
|
+
// Since this is the "Publish" action, the validation
|
3329
|
+
// schema must enforce the rules for published entities
|
3330
|
+
{ status: "published" }
|
3331
|
+
);
|
3160
3332
|
const validationErrors2 = {};
|
3161
3333
|
const rows2 = data.map((entry) => {
|
3162
3334
|
try {
|
@@ -3506,7 +3678,7 @@ const TableActions = ({ document }) => {
|
|
3506
3678
|
DescriptionComponentRenderer,
|
3507
3679
|
{
|
3508
3680
|
props,
|
3509
|
-
descriptions: plugins["content-manager"].apis.getDocumentActions(),
|
3681
|
+
descriptions: plugins["content-manager"].apis.getDocumentActions().filter((action) => action.name !== "PublishAction"),
|
3510
3682
|
children: (actions2) => {
|
3511
3683
|
const tableRowActions = actions2.filter((action) => {
|
3512
3684
|
const positions = Array.isArray(action.position) ? action.position : [action.position];
|
@@ -3617,7 +3789,7 @@ const CloneAction = ({ model, documentId }) => {
|
|
3617
3789
|
}),
|
3618
3790
|
content: /* @__PURE__ */ jsx(AutoCloneFailureModalBody, { prohibitedFields }),
|
3619
3791
|
footer: ({ onClose }) => {
|
3620
|
-
return /* @__PURE__ */ jsxs(
|
3792
|
+
return /* @__PURE__ */ jsxs(Modal.Footer, { children: [
|
3621
3793
|
/* @__PURE__ */ jsx(Button, { onClick: onClose, variant: "tertiary", children: formatMessage({
|
3622
3794
|
id: "cancel",
|
3623
3795
|
defaultMessage: "Cancel"
|
@@ -3829,6 +4001,15 @@ const { setInitialData } = actions;
|
|
3829
4001
|
const reducer = combineReducers({
|
3830
4002
|
app: reducer$1
|
3831
4003
|
});
|
4004
|
+
const FEATURE_ID = "preview";
|
4005
|
+
const previewAdmin = {
|
4006
|
+
bootstrap(app) {
|
4007
|
+
if (!window.strapi.future.isEnabled(FEATURE_ID)) {
|
4008
|
+
return {};
|
4009
|
+
}
|
4010
|
+
console.log("Bootstrapping preview admin");
|
4011
|
+
}
|
4012
|
+
};
|
3832
4013
|
const index = {
|
3833
4014
|
register(app) {
|
3834
4015
|
const cm = new ContentManagerPlugin();
|
@@ -3848,7 +4029,7 @@ const index = {
|
|
3848
4029
|
app.router.addRoute({
|
3849
4030
|
path: "content-manager/*",
|
3850
4031
|
lazy: async () => {
|
3851
|
-
const { Layout } = await import("./layout-
|
4032
|
+
const { Layout } = await import("./layout-_5-cXs34.mjs");
|
3852
4033
|
return {
|
3853
4034
|
Component: Layout
|
3854
4035
|
};
|
@@ -3861,11 +4042,14 @@ const index = {
|
|
3861
4042
|
if (typeof historyAdmin.bootstrap === "function") {
|
3862
4043
|
historyAdmin.bootstrap(app);
|
3863
4044
|
}
|
4045
|
+
if (typeof previewAdmin.bootstrap === "function") {
|
4046
|
+
previewAdmin.bootstrap(app);
|
4047
|
+
}
|
3864
4048
|
},
|
3865
4049
|
async registerTrads({ locales }) {
|
3866
4050
|
const importedTrads = await Promise.all(
|
3867
4051
|
locales.map((locale) => {
|
3868
|
-
return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-
|
4052
|
+
return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => import("./ar-CCEVvqGG.mjs"), "./translations/ca.json": () => import("./ca-5U32ON2v.mjs"), "./translations/cs.json": () => import("./cs-CM2aBUar.mjs"), "./translations/de.json": () => import("./de-C72KDNOl.mjs"), "./translations/en.json": () => import("./en-DKV44jRb.mjs"), "./translations/es.json": () => import("./es-CeXiYflN.mjs"), "./translations/eu.json": () => import("./eu-CdALomew.mjs"), "./translations/fr.json": () => import("./fr-CD9VFbPM.mjs"), "./translations/gu.json": () => import("./gu-CNpaMDpH.mjs"), "./translations/hi.json": () => import("./hi-Dwvd04m3.mjs"), "./translations/hu.json": () => import("./hu-CeYvaaO0.mjs"), "./translations/id.json": () => import("./id-BtwA9WJT.mjs"), "./translations/it.json": () => import("./it-BrVPqaf1.mjs"), "./translations/ja.json": () => import("./ja-CtsUxOvk.mjs"), "./translations/ko.json": () => import("./ko-HVQRlfUI.mjs"), "./translations/ml.json": () => import("./ml-BihZwQit.mjs"), "./translations/ms.json": () => import("./ms-m_WjyWx7.mjs"), "./translations/nl.json": () => import("./nl-D4R9gHx5.mjs"), "./translations/pl.json": () => import("./pl-sbx9mSt_.mjs"), "./translations/pt-BR.json": () => import("./pt-BR-C71iDxnh.mjs"), "./translations/pt.json": () => import("./pt-BsaFvS8-.mjs"), "./translations/ru.json": () => import("./ru-BE6A4Exp.mjs"), "./translations/sa.json": () => import("./sa-Dag0k-Z8.mjs"), "./translations/sk.json": () => import("./sk-BFg-R8qJ.mjs"), "./translations/sv.json": () => import("./sv-CUnfWGsh.mjs"), "./translations/th.json": () => import("./th-BqbI8lIT.mjs"), "./translations/tr.json": () => import("./tr-CgeK3wJM.mjs"), "./translations/uk.json": () => import("./uk-CR-zDhAY.mjs"), "./translations/vi.json": () => import("./vi-DUXIk_fw.mjs"), "./translations/zh-Hans.json": () => import("./zh-Hans-BPQcRIyH.mjs"), "./translations/zh.json": () => import("./zh-BWZspA60.mjs") }), `./translations/${locale}.json`).then(({ default: data }) => {
|
3869
4053
|
return {
|
3870
4054
|
data: prefixPluginTranslations(data, PLUGIN_ID),
|
3871
4055
|
locale
|
@@ -3886,13 +4070,15 @@ export {
|
|
3886
4070
|
BulkActionsRenderer as B,
|
3887
4071
|
COLLECTION_TYPES as C,
|
3888
4072
|
DocumentStatus as D,
|
3889
|
-
|
3890
|
-
|
3891
|
-
|
4073
|
+
extractContentTypeComponents as E,
|
4074
|
+
DEFAULT_SETTINGS as F,
|
4075
|
+
convertEditLayoutToFieldLayouts as G,
|
3892
4076
|
HOOKS as H,
|
3893
4077
|
InjectionZone as I,
|
3894
|
-
|
3895
|
-
|
4078
|
+
useDocument as J,
|
4079
|
+
index as K,
|
4080
|
+
useContentManagerContext as L,
|
4081
|
+
useDocumentActions as M,
|
3896
4082
|
Panels as P,
|
3897
4083
|
RelativeTime as R,
|
3898
4084
|
SINGLE_TYPES as S,
|
@@ -3910,18 +4096,18 @@ export {
|
|
3910
4096
|
PERMISSIONS as k,
|
3911
4097
|
DocumentRBAC as l,
|
3912
4098
|
DOCUMENT_META_FIELDS as m,
|
3913
|
-
|
3914
|
-
|
3915
|
-
|
3916
|
-
|
3917
|
-
|
4099
|
+
CLONE_PATH as n,
|
4100
|
+
useDocLayout as o,
|
4101
|
+
useGetContentTypeConfigurationQuery as p,
|
4102
|
+
CREATOR_FIELDS as q,
|
4103
|
+
getMainField as r,
|
3918
4104
|
setInitialData as s,
|
3919
|
-
|
4105
|
+
getDisplayName as t,
|
3920
4106
|
useContentTypeSchema as u,
|
3921
|
-
|
3922
|
-
|
3923
|
-
|
3924
|
-
|
3925
|
-
|
4107
|
+
checkIfAttributeIsDisplayable as v,
|
4108
|
+
useGetAllDocumentsQuery as w,
|
4109
|
+
convertListLayoutToFieldLayouts as x,
|
4110
|
+
capitalise as y,
|
4111
|
+
useUpdateContentTypeConfigurationMutation as z
|
3926
4112
|
};
|
3927
|
-
//# sourceMappingURL=index-
|
4113
|
+
//# sourceMappingURL=index-BrUzbQ30.mjs.map
|