node_modules ignore

This commit is contained in:
2025-05-08 23:43:47 +02:00
parent e19d52f172
commit 4574544c9f
65041 changed files with 10593536 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var Icons = require('@strapi/icons');
var qs = require('qs');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
const HistoryAction = ({ model, document })=>{
const { formatMessage } = reactIntl.useIntl();
const [{ query }] = strapiAdmin.useQueryParams();
const navigate = reactRouterDom.useNavigate();
const { trackUsage } = strapiAdmin.useTracking();
const { pathname } = reactRouterDom.useLocation();
const pluginsQueryParams = qs.stringify({
plugins: query.plugins
}, {
encode: false
});
if (!window.strapi.features.isEnabled('cms-content-history')) {
return null;
}
const handleOnClick = ()=>{
const destination = {
pathname: 'history',
search: pluginsQueryParams
};
trackUsage('willNavigate', {
from: pathname,
to: `${pathname}/${destination.pathname}`
});
navigate(destination);
};
return {
icon: /*#__PURE__*/ jsxRuntime.jsx(Icons.ClockCounterClockwise, {}),
label: formatMessage({
id: 'content-manager.history.document-action',
defaultMessage: 'Content History'
}),
onClick: handleOnClick,
disabled: /**
* The user is creating a new document.
* It hasn't been saved yet, so there's no history to go to
*/ !document || /**
* The document has been created but the current dimension has never been saved.
* For example, the user is creating a new locale in an existing document,
* so there's no history for the document in that locale
*/ !document.id || /**
* History is only available for content types created by the user.
* These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
* which start with `admin::` or `plugin::`
*/ !model.startsWith('api::'),
position: 'header'
};
};
HistoryAction.type = 'history';
HistoryAction.position = 'header';
exports.HistoryAction = HistoryAction;
//# sourceMappingURL=HistoryAction.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HistoryAction.js","sources":["../../../../admin/src/history/components/HistoryAction.tsx"],"sourcesContent":["import { useQueryParams, useTracking } from '@strapi/admin/strapi-admin';\nimport { ClockCounterClockwise } from '@strapi/icons';\nimport { stringify } from 'qs';\nimport { useIntl } from 'react-intl';\nimport { useNavigate, useLocation } from 'react-router-dom';\n\nimport type { DocumentActionComponent } from '../../content-manager';\n\nconst HistoryAction: DocumentActionComponent = ({ model, document }) => {\n const { formatMessage } = useIntl();\n const [{ query }] = useQueryParams<{ plugins?: Record<string, unknown> }>();\n const navigate = useNavigate();\n const { trackUsage } = useTracking();\n const { pathname } = useLocation();\n const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });\n\n if (!window.strapi.features.isEnabled('cms-content-history')) {\n return null;\n }\n\n const handleOnClick = () => {\n const destination = { pathname: 'history', search: pluginsQueryParams };\n trackUsage('willNavigate', {\n from: pathname,\n to: `${pathname}/${destination.pathname}`,\n });\n navigate(destination);\n };\n\n return {\n icon: <ClockCounterClockwise />,\n label: formatMessage({\n id: 'content-manager.history.document-action',\n defaultMessage: 'Content History',\n }),\n onClick: handleOnClick,\n disabled:\n /**\n * The user is creating a new document.\n * It hasn't been saved yet, so there's no history to go to\n */\n !document ||\n /**\n * The document has been created but the current dimension has never been saved.\n * For example, the user is creating a new locale in an existing document,\n * so there's no history for the document in that locale\n */\n !document.id ||\n /**\n * History is only available for content types created by the user.\n * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,\n * which start with `admin::` or `plugin::`\n */\n !model.startsWith('api::'),\n position: 'header',\n };\n};\n\nHistoryAction.type = 'history';\nHistoryAction.position = 'header';\n\nexport { HistoryAction };\n"],"names":["HistoryAction","model","document","formatMessage","useIntl","query","useQueryParams","navigate","useNavigate","trackUsage","useTracking","pathname","useLocation","pluginsQueryParams","stringify","plugins","encode","window","strapi","features","isEnabled","handleOnClick","destination","search","from","to","icon","_jsx","ClockCounterClockwise","label","id","defaultMessage","onClick","disabled","startsWith","position","type"],"mappings":";;;;;;;;;AAQA,MAAMA,gBAAyC,CAAC,EAAEC,KAAK,EAAEC,QAAQ,EAAE,GAAA;IACjE,MAAM,EAAEC,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;AAC1B,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,0BAAAA,EAAAA;AACpB,IAAA,MAAMC,QAAWC,GAAAA,0BAAAA,EAAAA;IACjB,MAAM,EAAEC,UAAU,EAAE,GAAGC,uBAAAA,EAAAA;IACvB,MAAM,EAAEC,QAAQ,EAAE,GAAGC,0BAAAA,EAAAA;AACrB,IAAA,MAAMC,qBAAqBC,YAAU,CAAA;AAAEC,QAAAA,OAAAA,EAASV,MAAMU;KAAW,EAAA;QAAEC,MAAQ,EAAA;AAAM,KAAA,CAAA;IAEjF,IAAI,CAACC,OAAOC,MAAM,CAACC,QAAQ,CAACC,SAAS,CAAC,qBAAwB,CAAA,EAAA;QAC5D,OAAO,IAAA;AACT;AAEA,IAAA,MAAMC,aAAgB,GAAA,IAAA;AACpB,QAAA,MAAMC,WAAc,GAAA;YAAEX,QAAU,EAAA,SAAA;YAAWY,MAAQV,EAAAA;AAAmB,SAAA;AACtEJ,QAAAA,UAAAA,CAAW,cAAgB,EAAA;YACzBe,IAAMb,EAAAA,QAAAA;YACNc,EAAI,EAAA,CAAC,EAAEd,QAAS,CAAA,CAAC,EAAEW,WAAYX,CAAAA,QAAQ,CAAC;AAC1C,SAAA,CAAA;QACAJ,QAASe,CAAAA,WAAAA,CAAAA;AACX,KAAA;IAEA,OAAO;AACLI,QAAAA,IAAAA,gBAAMC,cAACC,CAAAA,2BAAAA,EAAAA,EAAAA,CAAAA;AACPC,QAAAA,KAAAA,EAAO1B,aAAc,CAAA;YACnB2B,EAAI,EAAA,yCAAA;YACJC,cAAgB,EAAA;AAClB,SAAA,CAAA;QACAC,OAASX,EAAAA,aAAAA;QACTY,QACE;;;AAGC,UACD,CAAC/B,QACD;;;;UAKA,CAACA,QAAS4B,CAAAA,EAAE;;;;UAMZ,CAAC7B,KAAMiC,CAAAA,UAAU,CAAC,OAAA,CAAA;QACpBC,QAAU,EAAA;AACZ,KAAA;AACF;AAEAnC,aAAAA,CAAcoC,IAAI,GAAG,SAAA;AACrBpC,aAAAA,CAAcmC,QAAQ,GAAG,QAAA;;;;"}

View File

@@ -0,0 +1,59 @@
import { jsx } from 'react/jsx-runtime';
import { useQueryParams, useTracking } from '@strapi/admin/strapi-admin';
import { ClockCounterClockwise } from '@strapi/icons';
import { stringify } from 'qs';
import { useIntl } from 'react-intl';
import { useNavigate, useLocation } from 'react-router-dom';
const HistoryAction = ({ model, document })=>{
const { formatMessage } = useIntl();
const [{ query }] = useQueryParams();
const navigate = useNavigate();
const { trackUsage } = useTracking();
const { pathname } = useLocation();
const pluginsQueryParams = stringify({
plugins: query.plugins
}, {
encode: false
});
if (!window.strapi.features.isEnabled('cms-content-history')) {
return null;
}
const handleOnClick = ()=>{
const destination = {
pathname: 'history',
search: pluginsQueryParams
};
trackUsage('willNavigate', {
from: pathname,
to: `${pathname}/${destination.pathname}`
});
navigate(destination);
};
return {
icon: /*#__PURE__*/ jsx(ClockCounterClockwise, {}),
label: formatMessage({
id: 'content-manager.history.document-action',
defaultMessage: 'Content History'
}),
onClick: handleOnClick,
disabled: /**
* The user is creating a new document.
* It hasn't been saved yet, so there's no history to go to
*/ !document || /**
* The document has been created but the current dimension has never been saved.
* For example, the user is creating a new locale in an existing document,
* so there's no history for the document in that locale
*/ !document.id || /**
* History is only available for content types created by the user.
* These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,
* which start with `admin::` or `plugin::`
*/ !model.startsWith('api::'),
position: 'header'
};
};
HistoryAction.type = 'history';
HistoryAction.position = 'header';
export { HistoryAction };
//# sourceMappingURL=HistoryAction.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HistoryAction.mjs","sources":["../../../../admin/src/history/components/HistoryAction.tsx"],"sourcesContent":["import { useQueryParams, useTracking } from '@strapi/admin/strapi-admin';\nimport { ClockCounterClockwise } from '@strapi/icons';\nimport { stringify } from 'qs';\nimport { useIntl } from 'react-intl';\nimport { useNavigate, useLocation } from 'react-router-dom';\n\nimport type { DocumentActionComponent } from '../../content-manager';\n\nconst HistoryAction: DocumentActionComponent = ({ model, document }) => {\n const { formatMessage } = useIntl();\n const [{ query }] = useQueryParams<{ plugins?: Record<string, unknown> }>();\n const navigate = useNavigate();\n const { trackUsage } = useTracking();\n const { pathname } = useLocation();\n const pluginsQueryParams = stringify({ plugins: query.plugins }, { encode: false });\n\n if (!window.strapi.features.isEnabled('cms-content-history')) {\n return null;\n }\n\n const handleOnClick = () => {\n const destination = { pathname: 'history', search: pluginsQueryParams };\n trackUsage('willNavigate', {\n from: pathname,\n to: `${pathname}/${destination.pathname}`,\n });\n navigate(destination);\n };\n\n return {\n icon: <ClockCounterClockwise />,\n label: formatMessage({\n id: 'content-manager.history.document-action',\n defaultMessage: 'Content History',\n }),\n onClick: handleOnClick,\n disabled:\n /**\n * The user is creating a new document.\n * It hasn't been saved yet, so there's no history to go to\n */\n !document ||\n /**\n * The document has been created but the current dimension has never been saved.\n * For example, the user is creating a new locale in an existing document,\n * so there's no history for the document in that locale\n */\n !document.id ||\n /**\n * History is only available for content types created by the user.\n * These have the `api::` prefix, as opposed to the ones created by Strapi or plugins,\n * which start with `admin::` or `plugin::`\n */\n !model.startsWith('api::'),\n position: 'header',\n };\n};\n\nHistoryAction.type = 'history';\nHistoryAction.position = 'header';\n\nexport { HistoryAction };\n"],"names":["HistoryAction","model","document","formatMessage","useIntl","query","useQueryParams","navigate","useNavigate","trackUsage","useTracking","pathname","useLocation","pluginsQueryParams","stringify","plugins","encode","window","strapi","features","isEnabled","handleOnClick","destination","search","from","to","icon","_jsx","ClockCounterClockwise","label","id","defaultMessage","onClick","disabled","startsWith","position","type"],"mappings":";;;;;;;AAQA,MAAMA,gBAAyC,CAAC,EAAEC,KAAK,EAAEC,QAAQ,EAAE,GAAA;IACjE,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;AAC1B,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,cAAAA,EAAAA;AACpB,IAAA,MAAMC,QAAWC,GAAAA,WAAAA,EAAAA;IACjB,MAAM,EAAEC,UAAU,EAAE,GAAGC,WAAAA,EAAAA;IACvB,MAAM,EAAEC,QAAQ,EAAE,GAAGC,WAAAA,EAAAA;AACrB,IAAA,MAAMC,qBAAqBC,SAAU,CAAA;AAAEC,QAAAA,OAAAA,EAASV,MAAMU;KAAW,EAAA;QAAEC,MAAQ,EAAA;AAAM,KAAA,CAAA;IAEjF,IAAI,CAACC,OAAOC,MAAM,CAACC,QAAQ,CAACC,SAAS,CAAC,qBAAwB,CAAA,EAAA;QAC5D,OAAO,IAAA;AACT;AAEA,IAAA,MAAMC,aAAgB,GAAA,IAAA;AACpB,QAAA,MAAMC,WAAc,GAAA;YAAEX,QAAU,EAAA,SAAA;YAAWY,MAAQV,EAAAA;AAAmB,SAAA;AACtEJ,QAAAA,UAAAA,CAAW,cAAgB,EAAA;YACzBe,IAAMb,EAAAA,QAAAA;YACNc,EAAI,EAAA,CAAC,EAAEd,QAAS,CAAA,CAAC,EAAEW,WAAYX,CAAAA,QAAQ,CAAC;AAC1C,SAAA,CAAA;QACAJ,QAASe,CAAAA,WAAAA,CAAAA;AACX,KAAA;IAEA,OAAO;AACLI,QAAAA,IAAAA,gBAAMC,GAACC,CAAAA,qBAAAA,EAAAA,EAAAA,CAAAA;AACPC,QAAAA,KAAAA,EAAO1B,aAAc,CAAA;YACnB2B,EAAI,EAAA,yCAAA;YACJC,cAAgB,EAAA;AAClB,SAAA,CAAA;QACAC,OAASX,EAAAA,aAAAA;QACTY,QACE;;;AAGC,UACD,CAAC/B,QACD;;;;UAKA,CAACA,QAAS4B,CAAAA,EAAE;;;;UAMZ,CAAC7B,KAAMiC,CAAAA,UAAU,CAAC,OAAA,CAAA;QACpBC,QAAU,EAAA;AACZ,KAAA;AACF;AAEAnC,aAAAA,CAAcoC,IAAI,GAAG,SAAA;AACrBpC,aAAAA,CAAcmC,QAAQ,GAAG,QAAA;;;;"}

View File

@@ -0,0 +1,264 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var pipe = require('lodash/fp/pipe');
var reactIntl = require('react-intl');
var useDocument = require('../../hooks/useDocument.js');
var hooks = require('../../modules/hooks.js');
var data = require('../../pages/EditView/utils/data.js');
var History = require('../pages/History.js');
var VersionInputRenderer = require('./VersionInputRenderer.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const createLayoutFromFields = (fields)=>{
return fields.reduce((rows, field)=>{
if (field.type === 'dynamiczone') {
// Dynamic zones take up all the columns in a row
rows.push([
field
]);
return rows;
}
if (!rows[rows.length - 1]) {
// Create a new row if there isn't one available
rows.push([]);
}
// Push fields to the current row, they wrap and handle their own column size
rows[rows.length - 1].push(field);
return rows;
}, [])// Map the rows to panels
.map((row)=>[
row
]);
};
/**
* Build a layout for the fields that are were deleted from the edit view layout
* via the configure the view page. This layout will be merged with the main one.
* Those fields would be restored if the user restores the history version, which is why it's
* important to show them, even if they're not in the normal layout.
*/ function getRemaingFieldsLayout({ layout, metadatas, schemaAttributes, fieldSizes }) {
const fieldsInLayout = layout.flatMap((panel)=>panel.flatMap((row)=>row.flatMap((field)=>field.name)));
const remainingFields = Object.entries(metadatas).reduce((currentRemainingFields, [name, field])=>{
// Make sure we do not fields that are not visible, e.g. "id"
if (!fieldsInLayout.includes(name) && field.edit.visible === true) {
const attribute = schemaAttributes[name];
// @ts-expect-error not sure why attribute causes type error
currentRemainingFields.push({
attribute,
type: attribute.type,
visible: true,
disabled: true,
label: field.edit.label || name,
name: name,
size: fieldSizes[attribute.type].default ?? 12
});
}
return currentRemainingFields;
}, []);
return createLayoutFromFields(remainingFields);
}
/* -------------------------------------------------------------------------------------------------
* FormPanel
* -----------------------------------------------------------------------------------------------*/ const FormPanel = ({ panel })=>{
if (panel.some((row)=>row.some((field)=>field.type === 'dynamiczone'))) {
const [row] = panel;
const [field] = row;
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Root, {
gap: 4,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: 12,
s: 12,
xs: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(VersionInputRenderer.VersionInputRenderer, {
...field
})
})
}, field.name);
}
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
hasRadius: true,
background: "neutral0",
shadow: "tableShadow",
paddingLeft: 6,
paddingRight: 6,
paddingTop: 6,
paddingBottom: 6,
borderColor: "neutral150",
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
children: panel.map((row, gridRowIndex)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Root, {
gap: 4,
children: row.map(({ size, ...field })=>{
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Grid.Item, {
col: size,
s: 12,
xs: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsxRuntime.jsx(VersionInputRenderer.VersionInputRenderer, {
...field
})
}, field.name);
})
}, gridRowIndex))
})
});
};
const VersionContent = ()=>{
const { formatMessage } = reactIntl.useIntl();
const { fieldSizes } = hooks.useTypedSelector((state)=>state['content-manager'].app);
const version = History.useHistoryContext('VersionContent', (state)=>state.selectedVersion);
const layout = History.useHistoryContext('VersionContent', (state)=>state.layout);
const configuration = History.useHistoryContext('VersionContent', (state)=>state.configuration);
const schema = History.useHistoryContext('VersionContent', (state)=>state.schema);
// Build a layout for the unknown fields section
const removedAttributes = version.meta.unknownAttributes.removed;
const removedAttributesAsFields = Object.entries(removedAttributes).map(([attributeName, attribute])=>{
const field = {
attribute,
shouldIgnoreRBAC: true,
type: attribute.type,
visible: true,
disabled: true,
label: attributeName,
name: attributeName,
size: fieldSizes[attribute.type].default ?? 12
};
return field;
});
const unknownFieldsLayout = createLayoutFromFields(removedAttributesAsFields);
// Build a layout for the fields that are were deleted from the layout
const remainingFieldsLayout = getRemaingFieldsLayout({
metadatas: configuration.contentType.metadatas,
layout,
schemaAttributes: schema.attributes,
fieldSizes
});
const { components } = useDocument.useDoc();
/**
* Transform the data before passing it to the form so that each field
* has a uniquely generated key
*/ const transformedData = React__namespace.useMemo(()=>{
const transform = (schemaAttributes, components = {})=>(document)=>{
const schema = {
attributes: schemaAttributes
};
const transformations = pipe(data.removeFieldsThatDontExistOnSchema(schema), data.prepareTempKeys(schema, components));
return transformations(document);
};
return transform(version.schema, components)(version.data);
}, [
components,
version.data,
version.schema
]);
return /*#__PURE__*/ jsxRuntime.jsxs(strapiAdmin.Layouts.Content, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
paddingBottom: 8,
children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Form, {
disabled: true,
method: "PUT",
initialValues: transformedData,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
position: "relative",
children: [
...layout,
...remainingFieldsLayout
].map((panel, index)=>{
return /*#__PURE__*/ jsxRuntime.jsx(FormPanel, {
panel: panel
}, index);
})
})
})
}),
removedAttributesAsFields.length > 0 && /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Divider, {}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Box, {
paddingTop: 8,
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "flex-start",
paddingBottom: 6,
gap: 1,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "delta",
children: formatMessage({
id: 'content-manager.history.content.unknown-fields.title',
defaultMessage: 'Unknown fields'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "pi",
children: formatMessage({
id: 'content-manager.history.content.unknown-fields.message',
defaultMessage: 'These fields have been deleted or renamed in the Content-Type Builder. <b>These fields will not be restored.</b>'
}, {
b: (chunks)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "pi",
fontWeight: "bold",
children: chunks
})
})
})
]
}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Form, {
disabled: true,
method: "PUT",
initialValues: version.data,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
position: "relative",
children: unknownFieldsLayout.map((panel, index)=>{
return /*#__PURE__*/ jsxRuntime.jsx(FormPanel, {
panel: panel
}, index);
})
})
})
]
})
]
})
]
});
};
exports.VersionContent = VersionContent;
exports.getRemaingFieldsLayout = getRemaingFieldsLayout;
//# sourceMappingURL=VersionContent.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,242 @@
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import * as React from 'react';
import { Layouts, Form } from '@strapi/admin/strapi-admin';
import { Box, Flex, Divider, Typography, Grid } from '@strapi/design-system';
import pipe from 'lodash/fp/pipe';
import { useIntl } from 'react-intl';
import { useDoc } from '../../hooks/useDocument.mjs';
import { useTypedSelector } from '../../modules/hooks.mjs';
import { removeFieldsThatDontExistOnSchema, prepareTempKeys } from '../../pages/EditView/utils/data.mjs';
import { useHistoryContext } from '../pages/History.mjs';
import { VersionInputRenderer } from './VersionInputRenderer.mjs';
const createLayoutFromFields = (fields)=>{
return fields.reduce((rows, field)=>{
if (field.type === 'dynamiczone') {
// Dynamic zones take up all the columns in a row
rows.push([
field
]);
return rows;
}
if (!rows[rows.length - 1]) {
// Create a new row if there isn't one available
rows.push([]);
}
// Push fields to the current row, they wrap and handle their own column size
rows[rows.length - 1].push(field);
return rows;
}, [])// Map the rows to panels
.map((row)=>[
row
]);
};
/**
* Build a layout for the fields that are were deleted from the edit view layout
* via the configure the view page. This layout will be merged with the main one.
* Those fields would be restored if the user restores the history version, which is why it's
* important to show them, even if they're not in the normal layout.
*/ function getRemaingFieldsLayout({ layout, metadatas, schemaAttributes, fieldSizes }) {
const fieldsInLayout = layout.flatMap((panel)=>panel.flatMap((row)=>row.flatMap((field)=>field.name)));
const remainingFields = Object.entries(metadatas).reduce((currentRemainingFields, [name, field])=>{
// Make sure we do not fields that are not visible, e.g. "id"
if (!fieldsInLayout.includes(name) && field.edit.visible === true) {
const attribute = schemaAttributes[name];
// @ts-expect-error not sure why attribute causes type error
currentRemainingFields.push({
attribute,
type: attribute.type,
visible: true,
disabled: true,
label: field.edit.label || name,
name: name,
size: fieldSizes[attribute.type].default ?? 12
});
}
return currentRemainingFields;
}, []);
return createLayoutFromFields(remainingFields);
}
/* -------------------------------------------------------------------------------------------------
* FormPanel
* -----------------------------------------------------------------------------------------------*/ const FormPanel = ({ panel })=>{
if (panel.some((row)=>row.some((field)=>field.type === 'dynamiczone'))) {
const [row] = panel;
const [field] = row;
return /*#__PURE__*/ jsx(Grid.Root, {
gap: 4,
children: /*#__PURE__*/ jsx(Grid.Item, {
col: 12,
s: 12,
xs: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(VersionInputRenderer, {
...field
})
})
}, field.name);
}
return /*#__PURE__*/ jsx(Box, {
hasRadius: true,
background: "neutral0",
shadow: "tableShadow",
paddingLeft: 6,
paddingRight: 6,
paddingTop: 6,
paddingBottom: 6,
borderColor: "neutral150",
children: /*#__PURE__*/ jsx(Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
children: panel.map((row, gridRowIndex)=>/*#__PURE__*/ jsx(Grid.Root, {
gap: 4,
children: row.map(({ size, ...field })=>{
return /*#__PURE__*/ jsx(Grid.Item, {
col: size,
s: 12,
xs: 12,
direction: "column",
alignItems: "stretch",
children: /*#__PURE__*/ jsx(VersionInputRenderer, {
...field
})
}, field.name);
})
}, gridRowIndex))
})
});
};
const VersionContent = ()=>{
const { formatMessage } = useIntl();
const { fieldSizes } = useTypedSelector((state)=>state['content-manager'].app);
const version = useHistoryContext('VersionContent', (state)=>state.selectedVersion);
const layout = useHistoryContext('VersionContent', (state)=>state.layout);
const configuration = useHistoryContext('VersionContent', (state)=>state.configuration);
const schema = useHistoryContext('VersionContent', (state)=>state.schema);
// Build a layout for the unknown fields section
const removedAttributes = version.meta.unknownAttributes.removed;
const removedAttributesAsFields = Object.entries(removedAttributes).map(([attributeName, attribute])=>{
const field = {
attribute,
shouldIgnoreRBAC: true,
type: attribute.type,
visible: true,
disabled: true,
label: attributeName,
name: attributeName,
size: fieldSizes[attribute.type].default ?? 12
};
return field;
});
const unknownFieldsLayout = createLayoutFromFields(removedAttributesAsFields);
// Build a layout for the fields that are were deleted from the layout
const remainingFieldsLayout = getRemaingFieldsLayout({
metadatas: configuration.contentType.metadatas,
layout,
schemaAttributes: schema.attributes,
fieldSizes
});
const { components } = useDoc();
/**
* Transform the data before passing it to the form so that each field
* has a uniquely generated key
*/ const transformedData = React.useMemo(()=>{
const transform = (schemaAttributes, components = {})=>(document)=>{
const schema = {
attributes: schemaAttributes
};
const transformations = pipe(removeFieldsThatDontExistOnSchema(schema), prepareTempKeys(schema, components));
return transformations(document);
};
return transform(version.schema, components)(version.data);
}, [
components,
version.data,
version.schema
]);
return /*#__PURE__*/ jsxs(Layouts.Content, {
children: [
/*#__PURE__*/ jsx(Box, {
paddingBottom: 8,
children: /*#__PURE__*/ jsx(Form, {
disabled: true,
method: "PUT",
initialValues: transformedData,
children: /*#__PURE__*/ jsx(Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
position: "relative",
children: [
...layout,
...remainingFieldsLayout
].map((panel, index)=>{
return /*#__PURE__*/ jsx(FormPanel, {
panel: panel
}, index);
})
})
})
}),
removedAttributesAsFields.length > 0 && /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsx(Divider, {}),
/*#__PURE__*/ jsxs(Box, {
paddingTop: 8,
children: [
/*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "flex-start",
paddingBottom: 6,
gap: 1,
children: [
/*#__PURE__*/ jsx(Typography, {
variant: "delta",
children: formatMessage({
id: 'content-manager.history.content.unknown-fields.title',
defaultMessage: 'Unknown fields'
})
}),
/*#__PURE__*/ jsx(Typography, {
variant: "pi",
children: formatMessage({
id: 'content-manager.history.content.unknown-fields.message',
defaultMessage: 'These fields have been deleted or renamed in the Content-Type Builder. <b>These fields will not be restored.</b>'
}, {
b: (chunks)=>/*#__PURE__*/ jsx(Typography, {
variant: "pi",
fontWeight: "bold",
children: chunks
})
})
})
]
}),
/*#__PURE__*/ jsx(Form, {
disabled: true,
method: "PUT",
initialValues: version.data,
children: /*#__PURE__*/ jsx(Flex, {
direction: "column",
alignItems: "stretch",
gap: 6,
position: "relative",
children: unknownFieldsLayout.map((panel, index)=>{
return /*#__PURE__*/ jsx(FormPanel, {
panel: panel
}, index);
})
})
})
]
})
]
})
]
});
};
export { VersionContent, getRemaingFieldsLayout };
//# sourceMappingURL=VersionContent.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,210 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var Icons = require('@strapi/icons');
var qs = require('qs');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var plugin = require('../../constants/plugin.js');
var History = require('../pages/History.js');
var historyVersion = require('../services/historyVersion.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const VersionHeader = ({ headerId })=>{
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = React__namespace.useState(false);
const navigate = reactRouterDom.useNavigate();
const { formatMessage, formatDate } = reactIntl.useIntl();
const { trackUsage } = strapiAdmin.useTracking();
const { toggleNotification } = strapiAdmin.useNotification();
const [{ query }] = strapiAdmin.useQueryParams();
const { collectionType, slug } = reactRouterDom.useParams();
const [restoreVersion, { isLoading }] = historyVersion.useRestoreVersionMutation();
const { allowedActions } = strapiAdmin.useRBAC(plugin.PERMISSIONS.map((action)=>({
action,
subject: slug
})));
const version = History.useHistoryContext('VersionHeader', (state)=>state.selectedVersion);
const mainField = History.useHistoryContext('VersionHeader', (state)=>state.mainField);
const schema = History.useHistoryContext('VersionHeader', (state)=>state.schema);
const isCurrentVersion = History.useHistoryContext('VersionHeader', (state)=>state.page === 1 && state.versions.data[0].id === state.selectedVersion.id);
const mainFieldValue = version.data[mainField];
const getNextNavigation = ()=>{
const pluginsQueryParams = qs.stringify({
plugins: query.plugins
}, {
encode: false
});
return {
pathname: '..',
search: pluginsQueryParams
};
};
const handleRestore = async ()=>{
try {
const response = await restoreVersion({
documentId: version.relatedDocumentId,
collectionType,
params: {
versionId: version.id,
contentType: version.contentType
},
body: {
contentType: version.contentType
}
});
if ('data' in response) {
navigate(getNextNavigation(), {
relative: 'path'
});
toggleNotification({
type: 'success',
title: formatMessage({
id: 'content-manager.restore.success.title',
defaultMessage: 'Version restored.'
}),
message: formatMessage({
id: 'content-manager.restore.success.message',
defaultMessage: 'A past version of the content was restored.'
})
});
trackUsage('didRestoreHistoryVersion');
}
if ('error' in response) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'content-manager.history.restore.error.message',
defaultMessage: 'Could not restore version.'
})
});
}
} catch (error) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
};
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Dialog.Root, {
open: isConfirmDialogOpen,
onOpenChange: setIsConfirmDialogOpen,
children: [
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Layouts.BaseHeader, {
id: headerId,
title: formatDate(new Date(version.createdAt), {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric'
}),
subtitle: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "epsilon",
textColor: "neutral600",
children: formatMessage({
id: 'content-manager.history.version.subtitle',
defaultMessage: '{hasLocale, select, true {{subtitle}, in {locale}} other {{subtitle}}}'
}, {
hasLocale: Boolean(version.locale),
subtitle: `${mainFieldValue || ''} (${schema.info.singularName})`.trim(),
locale: version.locale?.name
})
}),
navigationAction: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Link, {
startIcon: /*#__PURE__*/ jsxRuntime.jsx(Icons.ArrowLeft, {}),
tag: reactRouterDom.NavLink,
to: getNextNavigation(),
relative: "path",
isExternal: false,
children: formatMessage({
id: 'global.back',
defaultMessage: 'Back'
})
}),
sticky: false,
primaryAction: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Dialog.Trigger, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
disabled: !allowedActions.canUpdate || isCurrentVersion,
onClick: ()=>{
setIsConfirmDialogOpen(true);
},
children: formatMessage({
id: 'content-manager.history.restore.confirm.button',
defaultMessage: 'Restore'
})
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.ConfirmDialog, {
onConfirm: handleRestore,
endAction: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Button, {
variant: "secondary",
onClick: handleRestore,
loading: isLoading,
children: formatMessage({
id: 'content-manager.history.restore.confirm.button',
defaultMessage: 'Restore'
})
}),
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "center",
justifyContent: "center",
gap: 2,
textAlign: "center",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
justifyContent: "center",
children: /*#__PURE__*/ jsxRuntime.jsx(Icons.WarningCircle, {
width: "24px",
height: "24px",
fill: "danger600"
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
children: formatMessage({
id: 'content-manager.history.restore.confirm.title',
defaultMessage: 'Are you sure you want to restore this version?'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
children: formatMessage({
id: 'content-manager.history.restore.confirm.message',
defaultMessage: "{isDraft, select, true {The restored content will override your draft.} other {The restored content won't be published, it will override the draft and be saved as pending changes. You'll be able to publish the changes at anytime.}}"
}, {
isDraft: version.status === 'draft'
})
})
]
})
})
]
});
};
exports.VersionHeader = VersionHeader;
//# sourceMappingURL=VersionHeader.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,189 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import * as React from 'react';
import { useTracking, useNotification, useQueryParams, useRBAC, Layouts, ConfirmDialog } from '@strapi/admin/strapi-admin';
import { Dialog, Typography, Link, Button, Flex } from '@strapi/design-system';
import { ArrowLeft, WarningCircle } from '@strapi/icons';
import { stringify } from 'qs';
import { useIntl } from 'react-intl';
import { useNavigate, useParams, NavLink } from 'react-router-dom';
import { PERMISSIONS } from '../../constants/plugin.mjs';
import { useHistoryContext } from '../pages/History.mjs';
import { useRestoreVersionMutation } from '../services/historyVersion.mjs';
const VersionHeader = ({ headerId })=>{
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = React.useState(false);
const navigate = useNavigate();
const { formatMessage, formatDate } = useIntl();
const { trackUsage } = useTracking();
const { toggleNotification } = useNotification();
const [{ query }] = useQueryParams();
const { collectionType, slug } = useParams();
const [restoreVersion, { isLoading }] = useRestoreVersionMutation();
const { allowedActions } = useRBAC(PERMISSIONS.map((action)=>({
action,
subject: slug
})));
const version = useHistoryContext('VersionHeader', (state)=>state.selectedVersion);
const mainField = useHistoryContext('VersionHeader', (state)=>state.mainField);
const schema = useHistoryContext('VersionHeader', (state)=>state.schema);
const isCurrentVersion = useHistoryContext('VersionHeader', (state)=>state.page === 1 && state.versions.data[0].id === state.selectedVersion.id);
const mainFieldValue = version.data[mainField];
const getNextNavigation = ()=>{
const pluginsQueryParams = stringify({
plugins: query.plugins
}, {
encode: false
});
return {
pathname: '..',
search: pluginsQueryParams
};
};
const handleRestore = async ()=>{
try {
const response = await restoreVersion({
documentId: version.relatedDocumentId,
collectionType,
params: {
versionId: version.id,
contentType: version.contentType
},
body: {
contentType: version.contentType
}
});
if ('data' in response) {
navigate(getNextNavigation(), {
relative: 'path'
});
toggleNotification({
type: 'success',
title: formatMessage({
id: 'content-manager.restore.success.title',
defaultMessage: 'Version restored.'
}),
message: formatMessage({
id: 'content-manager.restore.success.message',
defaultMessage: 'A past version of the content was restored.'
})
});
trackUsage('didRestoreHistoryVersion');
}
if ('error' in response) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'content-manager.history.restore.error.message',
defaultMessage: 'Could not restore version.'
})
});
}
} catch (error) {
toggleNotification({
type: 'danger',
message: formatMessage({
id: 'notification.error',
defaultMessage: 'An error occurred'
})
});
}
};
return /*#__PURE__*/ jsxs(Dialog.Root, {
open: isConfirmDialogOpen,
onOpenChange: setIsConfirmDialogOpen,
children: [
/*#__PURE__*/ jsx(Layouts.BaseHeader, {
id: headerId,
title: formatDate(new Date(version.createdAt), {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric'
}),
subtitle: /*#__PURE__*/ jsx(Typography, {
variant: "epsilon",
textColor: "neutral600",
children: formatMessage({
id: 'content-manager.history.version.subtitle',
defaultMessage: '{hasLocale, select, true {{subtitle}, in {locale}} other {{subtitle}}}'
}, {
hasLocale: Boolean(version.locale),
subtitle: `${mainFieldValue || ''} (${schema.info.singularName})`.trim(),
locale: version.locale?.name
})
}),
navigationAction: /*#__PURE__*/ jsx(Link, {
startIcon: /*#__PURE__*/ jsx(ArrowLeft, {}),
tag: NavLink,
to: getNextNavigation(),
relative: "path",
isExternal: false,
children: formatMessage({
id: 'global.back',
defaultMessage: 'Back'
})
}),
sticky: false,
primaryAction: /*#__PURE__*/ jsx(Dialog.Trigger, {
children: /*#__PURE__*/ jsx(Button, {
disabled: !allowedActions.canUpdate || isCurrentVersion,
onClick: ()=>{
setIsConfirmDialogOpen(true);
},
children: formatMessage({
id: 'content-manager.history.restore.confirm.button',
defaultMessage: 'Restore'
})
})
})
}),
/*#__PURE__*/ jsx(ConfirmDialog, {
onConfirm: handleRestore,
endAction: /*#__PURE__*/ jsx(Button, {
variant: "secondary",
onClick: handleRestore,
loading: isLoading,
children: formatMessage({
id: 'content-manager.history.restore.confirm.button',
defaultMessage: 'Restore'
})
}),
children: /*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "center",
justifyContent: "center",
gap: 2,
textAlign: "center",
children: [
/*#__PURE__*/ jsx(Flex, {
justifyContent: "center",
children: /*#__PURE__*/ jsx(WarningCircle, {
width: "24px",
height: "24px",
fill: "danger600"
})
}),
/*#__PURE__*/ jsx(Typography, {
children: formatMessage({
id: 'content-manager.history.restore.confirm.title',
defaultMessage: 'Are you sure you want to restore this version?'
})
}),
/*#__PURE__*/ jsx(Typography, {
children: formatMessage({
id: 'content-manager.history.restore.confirm.message',
defaultMessage: "{isDraft, select, true {The restored content will override your draft.} other {The restored content won't be published, it will override the draft and be saved as pending changes. You'll be able to publish the changes at anytime.}}"
}, {
isDraft: version.status === 'draft'
})
})
]
})
})
]
});
};
export { VersionHeader };
//# sourceMappingURL=VersionHeader.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,490 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var styledComponents = require('styled-components');
var collections = require('../../constants/collections.js');
var DocumentRBAC = require('../../features/DocumentRBAC.js');
var useDocument = require('../../hooks/useDocument.js');
var useDocumentLayout = require('../../hooks/useDocumentLayout.js');
var useLazyComponents = require('../../hooks/useLazyComponents.js');
var hooks = require('../../modules/hooks.js');
var DocumentStatus = require('../../pages/EditView/components/DocumentStatus.js');
var BlocksInput = require('../../pages/EditView/components/FormInputs/BlocksInput/BlocksInput.js');
var Input = require('../../pages/EditView/components/FormInputs/Component/Input.js');
var Field = require('../../pages/EditView/components/FormInputs/DynamicZone/Field.js');
var NotAllowed = require('../../pages/EditView/components/FormInputs/NotAllowed.js');
var UID = require('../../pages/EditView/components/FormInputs/UID.js');
var Field$1 = require('../../pages/EditView/components/FormInputs/Wysiwyg/Field.js');
var InputRenderer = require('../../pages/EditView/components/InputRenderer.js');
var relations = require('../../utils/relations.js');
var History = require('../pages/History.js');
var VersionContent = require('./VersionContent.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const StyledAlert = styledComponents.styled(designSystem.Alert).attrs({
closeLabel: 'Close',
onClose: ()=>{},
shadow: 'none'
})`
button {
display: none;
}
`;
/* -------------------------------------------------------------------------------------------------
* CustomRelationInput
* -----------------------------------------------------------------------------------------------*/ const LinkEllipsis = styledComponents.styled(designSystem.Link)`
display: block;
& > span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
`;
const CustomRelationInput = (props)=>{
const { formatMessage } = reactIntl.useIntl();
const field = strapiAdmin.useField(props.name);
/**
* Ideally the server would return the correct shape, however, for admin user relations
* it sanitizes everything out when it finds an object for the relation value.
*/ let formattedFieldValue;
if (field) {
formattedFieldValue = Array.isArray(field.value) ? {
results: field.value,
meta: {
missingCount: 0
}
} : field.value;
}
if (!formattedFieldValue || formattedFieldValue.results.length === 0 && formattedFieldValue.meta.missingCount === 0) {
return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
action: props.labelAction,
children: props.label
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
marginTop: 1,
children: /*#__PURE__*/ jsxRuntime.jsx(StyledAlert, {
variant: "default",
children: formatMessage({
id: 'content-manager.history.content.no-relations',
defaultMessage: 'No relations.'
})
})
})
]
});
}
const { results, meta } = formattedFieldValue;
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Box, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: props.label
}),
results.length > 0 && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
gap: 2,
marginTop: 1,
alignItems: "stretch",
children: results.map((relationData)=>{
// @ts-expect-error - targetModel does exist on the attribute. But it's not typed.
const { targetModel } = props.attribute;
const href = `../${collections.COLLECTION_TYPES}/${targetModel}/${relationData.documentId}`;
const label = relations.getRelationLabel(relationData, props.mainField);
const isAdminUserRelation = targetModel === 'admin::user';
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
paddingTop: 2,
paddingBottom: 2,
paddingLeft: 4,
paddingRight: 4,
hasRadius: true,
borderColor: "neutral200",
background: "neutral150",
justifyContent: "space-between",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
minWidth: 0,
paddingTop: 1,
paddingBottom: 1,
paddingRight: 4,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tooltip, {
label: label,
children: isAdminUserRelation ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
children: label
}) : /*#__PURE__*/ jsxRuntime.jsx(LinkEllipsis, {
tag: reactRouterDom.NavLink,
to: href,
children: label
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(DocumentStatus.DocumentStatus, {
status: relationData.status
})
]
}, relationData.documentId ?? relationData.id);
})
}),
meta.missingCount > 0 && /* @ts-expect-error we dont need closeLabel */ /*#__PURE__*/ jsxRuntime.jsx(StyledAlert, {
marginTop: 1,
variant: "warning",
title: formatMessage({
id: 'content-manager.history.content.missing-relations.title',
defaultMessage: '{number, plural, =1 {Missing relation} other {{number} missing relations}}'
}, {
number: meta.missingCount
}),
children: formatMessage({
id: 'content-manager.history.content.missing-relations.message',
defaultMessage: "{number, plural, =1 {It has} other {They have}} been deleted and can't be restored."
}, {
number: meta.missingCount
})
})
]
});
};
/* -------------------------------------------------------------------------------------------------
* CustomMediaInput
* -----------------------------------------------------------------------------------------------*/ // Create an object with value at key path (i.e. 'a.b.c')
const createInitialValuesForPath = (keyPath, value)=>{
const keys = keyPath.split('.');
// The root level object
const root = {};
// Make the first node the root
let node = root;
keys.forEach((key, index)=>{
// Skip prototype pollution keys
if (key === '__proto__' || key === 'constructor') return;
// If it's the last key, set the node value
if (index === keys.length - 1) {
node[key] = value;
} else {
// Ensure the key exists and is an object
node[key] = node[key] || {};
}
// Traverse down the tree
node = node[key];
});
return root;
};
const CustomMediaInput = (props)=>{
const { value } = strapiAdmin.useField(props.name);
const results = value?.results ?? [];
const meta = value?.meta ?? {
missingCount: 0
};
const { formatMessage } = reactIntl.useIntl();
const fields = strapiAdmin.useStrapiApp('CustomMediaInput', (state)=>state.fields);
const MediaLibrary = fields.media;
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
gap: 2,
alignItems: "stretch",
children: [
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Form, {
method: "PUT",
disabled: true,
initialValues: createInitialValuesForPath(props.name, results),
children: /*#__PURE__*/ jsxRuntime.jsx(MediaLibrary, {
...props,
disabled: true,
multiple: results.length > 1
})
}),
meta.missingCount > 0 && /*#__PURE__*/ jsxRuntime.jsx(StyledAlert, {
variant: "warning",
closeLabel: "Close",
onClose: ()=>{},
title: formatMessage({
id: 'content-manager.history.content.missing-assets.title',
defaultMessage: '{number, plural, =1 {Missing asset} other {{number} missing assets}}'
}, {
number: meta.missingCount
}),
children: formatMessage({
id: 'content-manager.history.content.missing-assets.message',
defaultMessage: "{number, plural, =1 {It has} other {They have}} been deleted in the Media Library and can't be restored."
}, {
number: meta.missingCount
})
})
]
});
};
/**
* Checks if the i18n plugin added a label action to the field and modifies it
* to adapt the wording for the history page.
*/ const getLabelAction = (labelAction)=>{
if (!/*#__PURE__*/ React__namespace.isValidElement(labelAction)) {
return labelAction;
}
// TODO: find a better way to do this rather than access internals
const labelActionTitleId = labelAction.props.title.id;
if (labelActionTitleId === 'i18n.Field.localized') {
return /*#__PURE__*/ React__namespace.cloneElement(labelAction, {
...labelAction.props,
title: {
id: 'history.content.localized',
defaultMessage: 'This value is specific to this locale. If you restore this version, the content will not be replaced for other locales.'
}
});
}
if (labelActionTitleId === 'i18n.Field.not-localized') {
return /*#__PURE__*/ React__namespace.cloneElement(labelAction, {
...labelAction.props,
title: {
id: 'history.content.not-localized',
defaultMessage: 'This value is common to all locales. If you restore this version and save the changes, the content will be replaced for all locales.'
}
});
}
// Label action is unrelated to i18n, don't touch it.
return labelAction;
};
/**
* @internal
*
* @description An abstraction around the regular form input renderer designed specifically
* to be used on the History page in the content-manager. It understands how to render specific
* inputs within the context of a history version (i.e. relations, media, ignored RBAC, etc...)
*/ const VersionInputRenderer = ({ visible, hint: providedHint, shouldIgnoreRBAC = false, labelAction, ...props })=>{
const customLabelAction = getLabelAction(labelAction);
const { formatMessage } = reactIntl.useIntl();
const version = History.useHistoryContext('VersionContent', (state)=>state.selectedVersion);
const configuration = History.useHistoryContext('VersionContent', (state)=>state.configuration);
const fieldSizes = hooks.useTypedSelector((state)=>state['content-manager'].app.fieldSizes);
const { id, components } = useDocument.useDoc();
const isFormDisabled = strapiAdmin.useForm('InputRenderer', (state)=>state.disabled);
const isInDynamicZone = Field.useDynamicZone('isInDynamicZone', (state)=>state.isInDynamicZone);
const canCreateFields = DocumentRBAC.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canCreateFields);
const canReadFields = DocumentRBAC.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canReadFields);
const canUpdateFields = DocumentRBAC.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canUpdateFields);
const canUserAction = DocumentRBAC.useDocumentRBAC('InputRenderer', (rbac)=>rbac.canUserAction);
const editableFields = id ? canUpdateFields : canCreateFields;
const readableFields = id ? canReadFields : canCreateFields;
/**
* Component fields are always readable and editable,
* however the fields within them may not be.
*/ const canUserReadField = canUserAction(props.name, readableFields, props.type);
const canUserEditField = canUserAction(props.name, editableFields, props.type);
const fields = strapiAdmin.useStrapiApp('InputRenderer', (app)=>app.fields);
const { lazyComponentStore } = useLazyComponents.useLazyComponents(attributeHasCustomFieldProperty(props.attribute) ? [
props.attribute.customField
] : undefined);
const hint = InputRenderer.useFieldHint(providedHint, props.attribute);
const { edit: { components: componentsLayout } } = useDocumentLayout.useDocLayout();
if (!visible) {
return null;
}
/**
* Don't render the field if the user can't read it.
*/ if (!shouldIgnoreRBAC && !canUserReadField && !isInDynamicZone) {
return /*#__PURE__*/ jsxRuntime.jsx(NotAllowed.NotAllowedInput, {
hint: hint,
...props
});
}
const fieldIsDisabled = !canUserEditField && !isInDynamicZone || props.disabled || isFormDisabled;
/**
* Attributes found on the current content-type schema cannot be restored. We handle
* this by displaying a warning alert to the user instead of the input for that field type.
*/ const addedAttributes = version.meta.unknownAttributes.added;
if (Object.keys(addedAttributes).includes(props.name)) {
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "flex-start",
gap: 1,
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Field.Label, {
children: props.label
}),
/*#__PURE__*/ jsxRuntime.jsx(StyledAlert, {
width: "100%",
closeLabel: "Close",
onClose: ()=>{},
variant: "warning",
title: formatMessage({
id: 'content-manager.history.content.new-field.title',
defaultMessage: 'New field'
}),
children: formatMessage({
id: 'content-manager.history.content.new-field.message',
defaultMessage: "This field didn't exist when this version was saved. If you restore this version, it will be empty."
})
})
]
});
}
/**
* Because a custom field has a unique prop but the type could be confused with either
* the useField hook or the type of the field we need to handle it separately and first.
*/ if (attributeHasCustomFieldProperty(props.attribute)) {
const CustomInput = lazyComponentStore[props.attribute.customField];
if (CustomInput) {
return /*#__PURE__*/ jsxRuntime.jsx(CustomInput, {
...props,
// @ts-expect-error TODO: fix this type error in the useLazyComponents hook.
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.InputRenderer, {
...props,
hint: hint,
labelAction: customLabelAction,
// @ts-expect-error this workaround lets us display that the custom field is missing.
type: props.attribute.customField,
disabled: fieldIsDisabled
});
}
/**
* Since media fields use a custom input via the upload plugin provided by the useLibrary hook,
* we need to handle the them before other custom inputs coming from the useLibrary hook.
*/ if (props.type === 'media') {
return /*#__PURE__*/ jsxRuntime.jsx(CustomMediaInput, {
...props,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
/**
* This is where we handle ONLY the fields from the `useLibrary` hook.
*/ const addedInputTypes = Object.keys(fields);
if (!attributeHasCustomFieldProperty(props.attribute) && addedInputTypes.includes(props.type)) {
const CustomInput = fields[props.type];
return /*#__PURE__*/ jsxRuntime.jsx(CustomInput, {
...props,
// @ts-expect-error TODO: fix this type error in the useLibrary hook.
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
/**
* These include the content-manager specific fields, failing that we fall back
* to the more generic form input renderer.
*/ switch(props.type){
case 'blocks':
return /*#__PURE__*/ jsxRuntime.jsx(BlocksInput.BlocksInput, {
...props,
hint: hint,
type: props.type,
disabled: fieldIsDisabled
});
case 'component':
const { layout } = componentsLayout[props.attribute.component];
// Components can only have one panel, so only save the first layout item
const [remainingFieldsLayout] = VersionContent.getRemaingFieldsLayout({
layout: [
layout
],
metadatas: configuration.components[props.attribute.component].metadatas,
fieldSizes,
schemaAttributes: components[props.attribute.component].attributes
});
return /*#__PURE__*/ jsxRuntime.jsx(Input.ComponentInput, {
...props,
layout: [
...layout,
...remainingFieldsLayout || []
],
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled,
children: (inputProps)=>/*#__PURE__*/ jsxRuntime.jsx(VersionInputRenderer, {
...inputProps,
shouldIgnoreRBAC: true
})
});
case 'dynamiczone':
return /*#__PURE__*/ jsxRuntime.jsx(Field.DynamicZone, {
...props,
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled,
children: (inputProps)=>/*#__PURE__*/ jsxRuntime.jsx(VersionInputRenderer, {
...inputProps,
shouldIgnoreRBAC: true
})
});
case 'relation':
return /*#__PURE__*/ jsxRuntime.jsx(CustomRelationInput, {
...props,
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
case 'richtext':
return /*#__PURE__*/ jsxRuntime.jsx(Field$1.Wysiwyg, {
...props,
hint: hint,
type: props.type,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
case 'uid':
return /*#__PURE__*/ jsxRuntime.jsx(UID.UIDInput, {
...props,
hint: hint,
type: props.type,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
/**
* Enumerations are a special case because they require options.
*/ case 'enumeration':
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.InputRenderer, {
...props,
hint: hint,
labelAction: customLabelAction,
options: props.attribute.enum.map((value)=>({
value
})),
// @ts-expect-error Temp workaround so we don't forget custom-fields don't work!
type: props.customField ? 'custom-field' : props.type,
disabled: fieldIsDisabled
});
default:
// These props are not needed for the generic form input renderer.
const { unique: _unique, mainField: _mainField, ...restProps } = props;
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.InputRenderer, {
...restProps,
hint: hint,
labelAction: customLabelAction,
// @ts-expect-error Temp workaround so we don't forget custom-fields don't work!
type: props.customField ? 'custom-field' : props.type,
disabled: fieldIsDisabled
});
}
};
const attributeHasCustomFieldProperty = (attribute)=>'customField' in attribute && typeof attribute.customField === 'string';
exports.VersionInputRenderer = VersionInputRenderer;
//# sourceMappingURL=VersionInputRenderer.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,469 @@
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import * as React from 'react';
import { useForm, useStrapiApp, InputRenderer, useField, Form } from '@strapi/admin/strapi-admin';
import { Alert, Link, Flex, Field, Box, Tooltip, Typography } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import { NavLink } from 'react-router-dom';
import { styled } from 'styled-components';
import { COLLECTION_TYPES } from '../../constants/collections.mjs';
import { useDocumentRBAC } from '../../features/DocumentRBAC.mjs';
import { useDoc } from '../../hooks/useDocument.mjs';
import { useDocLayout } from '../../hooks/useDocumentLayout.mjs';
import { useLazyComponents } from '../../hooks/useLazyComponents.mjs';
import { useTypedSelector } from '../../modules/hooks.mjs';
import { DocumentStatus } from '../../pages/EditView/components/DocumentStatus.mjs';
import { BlocksInput as MemoizedBlocksInput } from '../../pages/EditView/components/FormInputs/BlocksInput/BlocksInput.mjs';
import { ComponentInput as MemoizedComponentInput } from '../../pages/EditView/components/FormInputs/Component/Input.mjs';
import { useDynamicZone, DynamicZone } from '../../pages/EditView/components/FormInputs/DynamicZone/Field.mjs';
import { NotAllowedInput } from '../../pages/EditView/components/FormInputs/NotAllowed.mjs';
import { UIDInput as MemoizedUIDInput } from '../../pages/EditView/components/FormInputs/UID.mjs';
import { Wysiwyg as MemoizedWysiwyg } from '../../pages/EditView/components/FormInputs/Wysiwyg/Field.mjs';
import { useFieldHint } from '../../pages/EditView/components/InputRenderer.mjs';
import { getRelationLabel } from '../../utils/relations.mjs';
import { useHistoryContext } from '../pages/History.mjs';
import { getRemaingFieldsLayout } from './VersionContent.mjs';
const StyledAlert = styled(Alert).attrs({
closeLabel: 'Close',
onClose: ()=>{},
shadow: 'none'
})`
button {
display: none;
}
`;
/* -------------------------------------------------------------------------------------------------
* CustomRelationInput
* -----------------------------------------------------------------------------------------------*/ const LinkEllipsis = styled(Link)`
display: block;
& > span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
`;
const CustomRelationInput = (props)=>{
const { formatMessage } = useIntl();
const field = useField(props.name);
/**
* Ideally the server would return the correct shape, however, for admin user relations
* it sanitizes everything out when it finds an object for the relation value.
*/ let formattedFieldValue;
if (field) {
formattedFieldValue = Array.isArray(field.value) ? {
results: field.value,
meta: {
missingCount: 0
}
} : field.value;
}
if (!formattedFieldValue || formattedFieldValue.results.length === 0 && formattedFieldValue.meta.missingCount === 0) {
return /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsx(Field.Label, {
action: props.labelAction,
children: props.label
}),
/*#__PURE__*/ jsx(Box, {
marginTop: 1,
children: /*#__PURE__*/ jsx(StyledAlert, {
variant: "default",
children: formatMessage({
id: 'content-manager.history.content.no-relations',
defaultMessage: 'No relations.'
})
})
})
]
});
}
const { results, meta } = formattedFieldValue;
return /*#__PURE__*/ jsxs(Box, {
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: props.label
}),
results.length > 0 && /*#__PURE__*/ jsx(Flex, {
direction: "column",
gap: 2,
marginTop: 1,
alignItems: "stretch",
children: results.map((relationData)=>{
// @ts-expect-error - targetModel does exist on the attribute. But it's not typed.
const { targetModel } = props.attribute;
const href = `../${COLLECTION_TYPES}/${targetModel}/${relationData.documentId}`;
const label = getRelationLabel(relationData, props.mainField);
const isAdminUserRelation = targetModel === 'admin::user';
return /*#__PURE__*/ jsxs(Flex, {
paddingTop: 2,
paddingBottom: 2,
paddingLeft: 4,
paddingRight: 4,
hasRadius: true,
borderColor: "neutral200",
background: "neutral150",
justifyContent: "space-between",
children: [
/*#__PURE__*/ jsx(Box, {
minWidth: 0,
paddingTop: 1,
paddingBottom: 1,
paddingRight: 4,
children: /*#__PURE__*/ jsx(Tooltip, {
label: label,
children: isAdminUserRelation ? /*#__PURE__*/ jsx(Typography, {
children: label
}) : /*#__PURE__*/ jsx(LinkEllipsis, {
tag: NavLink,
to: href,
children: label
})
})
}),
/*#__PURE__*/ jsx(DocumentStatus, {
status: relationData.status
})
]
}, relationData.documentId ?? relationData.id);
})
}),
meta.missingCount > 0 && /* @ts-expect-error we dont need closeLabel */ /*#__PURE__*/ jsx(StyledAlert, {
marginTop: 1,
variant: "warning",
title: formatMessage({
id: 'content-manager.history.content.missing-relations.title',
defaultMessage: '{number, plural, =1 {Missing relation} other {{number} missing relations}}'
}, {
number: meta.missingCount
}),
children: formatMessage({
id: 'content-manager.history.content.missing-relations.message',
defaultMessage: "{number, plural, =1 {It has} other {They have}} been deleted and can't be restored."
}, {
number: meta.missingCount
})
})
]
});
};
/* -------------------------------------------------------------------------------------------------
* CustomMediaInput
* -----------------------------------------------------------------------------------------------*/ // Create an object with value at key path (i.e. 'a.b.c')
const createInitialValuesForPath = (keyPath, value)=>{
const keys = keyPath.split('.');
// The root level object
const root = {};
// Make the first node the root
let node = root;
keys.forEach((key, index)=>{
// Skip prototype pollution keys
if (key === '__proto__' || key === 'constructor') return;
// If it's the last key, set the node value
if (index === keys.length - 1) {
node[key] = value;
} else {
// Ensure the key exists and is an object
node[key] = node[key] || {};
}
// Traverse down the tree
node = node[key];
});
return root;
};
const CustomMediaInput = (props)=>{
const { value } = useField(props.name);
const results = value?.results ?? [];
const meta = value?.meta ?? {
missingCount: 0
};
const { formatMessage } = useIntl();
const fields = useStrapiApp('CustomMediaInput', (state)=>state.fields);
const MediaLibrary = fields.media;
return /*#__PURE__*/ jsxs(Flex, {
direction: "column",
gap: 2,
alignItems: "stretch",
children: [
/*#__PURE__*/ jsx(Form, {
method: "PUT",
disabled: true,
initialValues: createInitialValuesForPath(props.name, results),
children: /*#__PURE__*/ jsx(MediaLibrary, {
...props,
disabled: true,
multiple: results.length > 1
})
}),
meta.missingCount > 0 && /*#__PURE__*/ jsx(StyledAlert, {
variant: "warning",
closeLabel: "Close",
onClose: ()=>{},
title: formatMessage({
id: 'content-manager.history.content.missing-assets.title',
defaultMessage: '{number, plural, =1 {Missing asset} other {{number} missing assets}}'
}, {
number: meta.missingCount
}),
children: formatMessage({
id: 'content-manager.history.content.missing-assets.message',
defaultMessage: "{number, plural, =1 {It has} other {They have}} been deleted in the Media Library and can't be restored."
}, {
number: meta.missingCount
})
})
]
});
};
/**
* Checks if the i18n plugin added a label action to the field and modifies it
* to adapt the wording for the history page.
*/ const getLabelAction = (labelAction)=>{
if (!/*#__PURE__*/ React.isValidElement(labelAction)) {
return labelAction;
}
// TODO: find a better way to do this rather than access internals
const labelActionTitleId = labelAction.props.title.id;
if (labelActionTitleId === 'i18n.Field.localized') {
return /*#__PURE__*/ React.cloneElement(labelAction, {
...labelAction.props,
title: {
id: 'history.content.localized',
defaultMessage: 'This value is specific to this locale. If you restore this version, the content will not be replaced for other locales.'
}
});
}
if (labelActionTitleId === 'i18n.Field.not-localized') {
return /*#__PURE__*/ React.cloneElement(labelAction, {
...labelAction.props,
title: {
id: 'history.content.not-localized',
defaultMessage: 'This value is common to all locales. If you restore this version and save the changes, the content will be replaced for all locales.'
}
});
}
// Label action is unrelated to i18n, don't touch it.
return labelAction;
};
/**
* @internal
*
* @description An abstraction around the regular form input renderer designed specifically
* to be used on the History page in the content-manager. It understands how to render specific
* inputs within the context of a history version (i.e. relations, media, ignored RBAC, etc...)
*/ const VersionInputRenderer = ({ visible, hint: providedHint, shouldIgnoreRBAC = false, labelAction, ...props })=>{
const customLabelAction = getLabelAction(labelAction);
const { formatMessage } = useIntl();
const version = useHistoryContext('VersionContent', (state)=>state.selectedVersion);
const configuration = useHistoryContext('VersionContent', (state)=>state.configuration);
const fieldSizes = useTypedSelector((state)=>state['content-manager'].app.fieldSizes);
const { id, components } = useDoc();
const isFormDisabled = useForm('InputRenderer', (state)=>state.disabled);
const isInDynamicZone = useDynamicZone('isInDynamicZone', (state)=>state.isInDynamicZone);
const canCreateFields = useDocumentRBAC('InputRenderer', (rbac)=>rbac.canCreateFields);
const canReadFields = useDocumentRBAC('InputRenderer', (rbac)=>rbac.canReadFields);
const canUpdateFields = useDocumentRBAC('InputRenderer', (rbac)=>rbac.canUpdateFields);
const canUserAction = useDocumentRBAC('InputRenderer', (rbac)=>rbac.canUserAction);
const editableFields = id ? canUpdateFields : canCreateFields;
const readableFields = id ? canReadFields : canCreateFields;
/**
* Component fields are always readable and editable,
* however the fields within them may not be.
*/ const canUserReadField = canUserAction(props.name, readableFields, props.type);
const canUserEditField = canUserAction(props.name, editableFields, props.type);
const fields = useStrapiApp('InputRenderer', (app)=>app.fields);
const { lazyComponentStore } = useLazyComponents(attributeHasCustomFieldProperty(props.attribute) ? [
props.attribute.customField
] : undefined);
const hint = useFieldHint(providedHint, props.attribute);
const { edit: { components: componentsLayout } } = useDocLayout();
if (!visible) {
return null;
}
/**
* Don't render the field if the user can't read it.
*/ if (!shouldIgnoreRBAC && !canUserReadField && !isInDynamicZone) {
return /*#__PURE__*/ jsx(NotAllowedInput, {
hint: hint,
...props
});
}
const fieldIsDisabled = !canUserEditField && !isInDynamicZone || props.disabled || isFormDisabled;
/**
* Attributes found on the current content-type schema cannot be restored. We handle
* this by displaying a warning alert to the user instead of the input for that field type.
*/ const addedAttributes = version.meta.unknownAttributes.added;
if (Object.keys(addedAttributes).includes(props.name)) {
return /*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "flex-start",
gap: 1,
children: [
/*#__PURE__*/ jsx(Field.Label, {
children: props.label
}),
/*#__PURE__*/ jsx(StyledAlert, {
width: "100%",
closeLabel: "Close",
onClose: ()=>{},
variant: "warning",
title: formatMessage({
id: 'content-manager.history.content.new-field.title',
defaultMessage: 'New field'
}),
children: formatMessage({
id: 'content-manager.history.content.new-field.message',
defaultMessage: "This field didn't exist when this version was saved. If you restore this version, it will be empty."
})
})
]
});
}
/**
* Because a custom field has a unique prop but the type could be confused with either
* the useField hook or the type of the field we need to handle it separately and first.
*/ if (attributeHasCustomFieldProperty(props.attribute)) {
const CustomInput = lazyComponentStore[props.attribute.customField];
if (CustomInput) {
return /*#__PURE__*/ jsx(CustomInput, {
...props,
// @ts-expect-error TODO: fix this type error in the useLazyComponents hook.
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
return /*#__PURE__*/ jsx(InputRenderer, {
...props,
hint: hint,
labelAction: customLabelAction,
// @ts-expect-error this workaround lets us display that the custom field is missing.
type: props.attribute.customField,
disabled: fieldIsDisabled
});
}
/**
* Since media fields use a custom input via the upload plugin provided by the useLibrary hook,
* we need to handle the them before other custom inputs coming from the useLibrary hook.
*/ if (props.type === 'media') {
return /*#__PURE__*/ jsx(CustomMediaInput, {
...props,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
/**
* This is where we handle ONLY the fields from the `useLibrary` hook.
*/ const addedInputTypes = Object.keys(fields);
if (!attributeHasCustomFieldProperty(props.attribute) && addedInputTypes.includes(props.type)) {
const CustomInput = fields[props.type];
return /*#__PURE__*/ jsx(CustomInput, {
...props,
// @ts-expect-error TODO: fix this type error in the useLibrary hook.
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
}
/**
* These include the content-manager specific fields, failing that we fall back
* to the more generic form input renderer.
*/ switch(props.type){
case 'blocks':
return /*#__PURE__*/ jsx(MemoizedBlocksInput, {
...props,
hint: hint,
type: props.type,
disabled: fieldIsDisabled
});
case 'component':
const { layout } = componentsLayout[props.attribute.component];
// Components can only have one panel, so only save the first layout item
const [remainingFieldsLayout] = getRemaingFieldsLayout({
layout: [
layout
],
metadatas: configuration.components[props.attribute.component].metadatas,
fieldSizes,
schemaAttributes: components[props.attribute.component].attributes
});
return /*#__PURE__*/ jsx(MemoizedComponentInput, {
...props,
layout: [
...layout,
...remainingFieldsLayout || []
],
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled,
children: (inputProps)=>/*#__PURE__*/ jsx(VersionInputRenderer, {
...inputProps,
shouldIgnoreRBAC: true
})
});
case 'dynamiczone':
return /*#__PURE__*/ jsx(DynamicZone, {
...props,
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled,
children: (inputProps)=>/*#__PURE__*/ jsx(VersionInputRenderer, {
...inputProps,
shouldIgnoreRBAC: true
})
});
case 'relation':
return /*#__PURE__*/ jsx(CustomRelationInput, {
...props,
hint: hint,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
case 'richtext':
return /*#__PURE__*/ jsx(MemoizedWysiwyg, {
...props,
hint: hint,
type: props.type,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
case 'uid':
return /*#__PURE__*/ jsx(MemoizedUIDInput, {
...props,
hint: hint,
type: props.type,
labelAction: customLabelAction,
disabled: fieldIsDisabled
});
/**
* Enumerations are a special case because they require options.
*/ case 'enumeration':
return /*#__PURE__*/ jsx(InputRenderer, {
...props,
hint: hint,
labelAction: customLabelAction,
options: props.attribute.enum.map((value)=>({
value
})),
// @ts-expect-error Temp workaround so we don't forget custom-fields don't work!
type: props.customField ? 'custom-field' : props.type,
disabled: fieldIsDisabled
});
default:
// These props are not needed for the generic form input renderer.
const { unique: _unique, mainField: _mainField, ...restProps } = props;
return /*#__PURE__*/ jsx(InputRenderer, {
...restProps,
hint: hint,
labelAction: customLabelAction,
// @ts-expect-error Temp workaround so we don't forget custom-fields don't work!
type: props.customField ? 'custom-field' : props.type,
disabled: fieldIsDisabled
});
}
};
const attributeHasCustomFieldProperty = (attribute)=>'customField' in attribute && typeof attribute.customField === 'string';
export { VersionInputRenderer };
//# sourceMappingURL=VersionInputRenderer.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,210 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var qs = require('qs');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var RelativeTime = require('../../components/RelativeTime.js');
var DocumentStatus = require('../../pages/EditView/components/DocumentStatus.js');
var users = require('../../utils/users.js');
var History = require('../pages/History.js');
/* -------------------------------------------------------------------------------------------------
* BlueText
* -----------------------------------------------------------------------------------------------*/ const BlueText = (children)=>/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
textColor: "primary600",
variant: "pi",
children: children
});
const VersionCard = ({ version, isCurrent })=>{
const { formatDate, formatMessage } = reactIntl.useIntl();
const [{ query }] = strapiAdmin.useQueryParams();
const isActive = query.id === version.id.toString();
const author = version.createdBy && users.getDisplayName(version.createdBy);
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
alignItems: "flex-start",
gap: 3,
hasRadius: true,
borderWidth: "1px",
borderStyle: "solid",
borderColor: isActive ? 'primary600' : 'neutral200',
color: "neutral800",
padding: 5,
tag: reactRouterDom.Link,
to: `?${qs.stringify({
...query,
id: version.id
})}`,
style: {
textDecoration: 'none'
},
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "column",
gap: 1,
alignItems: "flex-start",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
tag: "h3",
fontWeight: "semiBold",
children: formatDate(version.createdAt, {
day: 'numeric',
month: 'numeric',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
tag: "p",
variant: "pi",
textColor: "neutral600",
children: formatMessage({
id: 'content-manager.history.sidebar.versionDescription',
defaultMessage: '{distanceToNow}{isAnonymous, select, true {} other { by {author}}}{isCurrent, select, true { <b>(current)</b>} other {}}'
}, {
distanceToNow: /*#__PURE__*/ jsxRuntime.jsx(RelativeTime.RelativeTime, {
timestamp: new Date(version.createdAt)
}),
author,
isAnonymous: !Boolean(version.createdBy),
isCurrent,
b: BlueText
})
})
]
}),
version.status && /*#__PURE__*/ jsxRuntime.jsx(DocumentStatus.DocumentStatus, {
status: version.status,
size: "XS"
})
]
});
};
const PaginationButton = ({ page, children })=>{
const [{ query }] = strapiAdmin.useQueryParams();
// Remove the id from the pagination link, so that the history page can redirect
// to the id of the first history version in the new page once it's loaded
const { id: _id, ...queryRest } = query;
return /*#__PURE__*/ jsxRuntime.jsx(reactRouterDom.Link, {
to: {
search: qs.stringify({
...queryRest,
page
})
},
style: {
textDecoration: 'none'
},
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "omega",
textColor: "primary600",
children: children
})
});
};
/* -------------------------------------------------------------------------------------------------
* VersionsList
* -----------------------------------------------------------------------------------------------*/ const VersionsList = ()=>{
const { formatMessage } = reactIntl.useIntl();
const { versions, page } = History.useHistoryContext('VersionsList', (state)=>({
versions: state.versions,
page: state.page
}));
return /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
shrink: 0,
direction: "column",
alignItems: "stretch",
width: "320px",
height: "100vh",
background: "neutral0",
borderColor: "neutral200",
borderWidth: "0 0 0 1px",
borderStyle: "solid",
tag: "aside",
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "row",
justifyContent: "space-between",
padding: 4,
borderColor: "neutral200",
borderWidth: "0 0 1px",
borderStyle: "solid",
tag: "header",
children: [
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
tag: "h2",
variant: "omega",
fontWeight: "semiBold",
children: formatMessage({
id: 'content-manager.history.sidebar.title',
defaultMessage: 'Versions'
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
background: "neutral150",
hasRadius: true,
padding: 1,
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
variant: "sigma",
textColor: "neutral600",
children: versions.meta.pagination.total
})
})
]
}),
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Box, {
flex: 1,
overflow: "auto",
children: [
versions.meta.pagination.page > 1 && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
paddingTop: 4,
textAlign: "center",
children: /*#__PURE__*/ jsxRuntime.jsx(PaginationButton, {
page: page - 1,
children: formatMessage({
id: 'content-manager.history.sidebar.show-newer',
defaultMessage: 'Show newer versions'
})
})
}),
/*#__PURE__*/ jsxRuntime.jsx(designSystem.Flex, {
direction: "column",
gap: 3,
padding: 4,
tag: "ul",
alignItems: "stretch",
children: versions.data.map((version, index)=>/*#__PURE__*/ jsxRuntime.jsx("li", {
"aria-label": formatMessage({
id: 'content-manager.history.sidebar.title.version-card.aria-label',
defaultMessage: 'Version card'
}),
children: /*#__PURE__*/ jsxRuntime.jsx(VersionCard, {
version: version,
isCurrent: page === 1 && index === 0
})
}, version.id))
}),
versions.meta.pagination.page < versions.meta.pagination.pageCount && /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
paddingBottom: 4,
textAlign: "center",
children: /*#__PURE__*/ jsxRuntime.jsx(PaginationButton, {
page: page + 1,
children: formatMessage({
id: 'content-manager.history.sidebar.show-older',
defaultMessage: 'Show older versions'
})
})
})
]
})
]
});
};
exports.VersionsList = VersionsList;
//# sourceMappingURL=VersionsList.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,208 @@
import { jsxs, jsx } from 'react/jsx-runtime';
import 'react';
import { useQueryParams } from '@strapi/admin/strapi-admin';
import { Flex, Typography, Box } from '@strapi/design-system';
import { stringify } from 'qs';
import { useIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import { RelativeTime } from '../../components/RelativeTime.mjs';
import { DocumentStatus } from '../../pages/EditView/components/DocumentStatus.mjs';
import { getDisplayName } from '../../utils/users.mjs';
import { useHistoryContext } from '../pages/History.mjs';
/* -------------------------------------------------------------------------------------------------
* BlueText
* -----------------------------------------------------------------------------------------------*/ const BlueText = (children)=>/*#__PURE__*/ jsx(Typography, {
textColor: "primary600",
variant: "pi",
children: children
});
const VersionCard = ({ version, isCurrent })=>{
const { formatDate, formatMessage } = useIntl();
const [{ query }] = useQueryParams();
const isActive = query.id === version.id.toString();
const author = version.createdBy && getDisplayName(version.createdBy);
return /*#__PURE__*/ jsxs(Flex, {
direction: "column",
alignItems: "flex-start",
gap: 3,
hasRadius: true,
borderWidth: "1px",
borderStyle: "solid",
borderColor: isActive ? 'primary600' : 'neutral200',
color: "neutral800",
padding: 5,
tag: Link,
to: `?${stringify({
...query,
id: version.id
})}`,
style: {
textDecoration: 'none'
},
children: [
/*#__PURE__*/ jsxs(Flex, {
direction: "column",
gap: 1,
alignItems: "flex-start",
children: [
/*#__PURE__*/ jsx(Typography, {
tag: "h3",
fontWeight: "semiBold",
children: formatDate(version.createdAt, {
day: 'numeric',
month: 'numeric',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}),
/*#__PURE__*/ jsx(Typography, {
tag: "p",
variant: "pi",
textColor: "neutral600",
children: formatMessage({
id: 'content-manager.history.sidebar.versionDescription',
defaultMessage: '{distanceToNow}{isAnonymous, select, true {} other { by {author}}}{isCurrent, select, true { <b>(current)</b>} other {}}'
}, {
distanceToNow: /*#__PURE__*/ jsx(RelativeTime, {
timestamp: new Date(version.createdAt)
}),
author,
isAnonymous: !Boolean(version.createdBy),
isCurrent,
b: BlueText
})
})
]
}),
version.status && /*#__PURE__*/ jsx(DocumentStatus, {
status: version.status,
size: "XS"
})
]
});
};
const PaginationButton = ({ page, children })=>{
const [{ query }] = useQueryParams();
// Remove the id from the pagination link, so that the history page can redirect
// to the id of the first history version in the new page once it's loaded
const { id: _id, ...queryRest } = query;
return /*#__PURE__*/ jsx(Link, {
to: {
search: stringify({
...queryRest,
page
})
},
style: {
textDecoration: 'none'
},
children: /*#__PURE__*/ jsx(Typography, {
variant: "omega",
textColor: "primary600",
children: children
})
});
};
/* -------------------------------------------------------------------------------------------------
* VersionsList
* -----------------------------------------------------------------------------------------------*/ const VersionsList = ()=>{
const { formatMessage } = useIntl();
const { versions, page } = useHistoryContext('VersionsList', (state)=>({
versions: state.versions,
page: state.page
}));
return /*#__PURE__*/ jsxs(Flex, {
shrink: 0,
direction: "column",
alignItems: "stretch",
width: "320px",
height: "100vh",
background: "neutral0",
borderColor: "neutral200",
borderWidth: "0 0 0 1px",
borderStyle: "solid",
tag: "aside",
children: [
/*#__PURE__*/ jsxs(Flex, {
direction: "row",
justifyContent: "space-between",
padding: 4,
borderColor: "neutral200",
borderWidth: "0 0 1px",
borderStyle: "solid",
tag: "header",
children: [
/*#__PURE__*/ jsx(Typography, {
tag: "h2",
variant: "omega",
fontWeight: "semiBold",
children: formatMessage({
id: 'content-manager.history.sidebar.title',
defaultMessage: 'Versions'
})
}),
/*#__PURE__*/ jsx(Box, {
background: "neutral150",
hasRadius: true,
padding: 1,
children: /*#__PURE__*/ jsx(Typography, {
variant: "sigma",
textColor: "neutral600",
children: versions.meta.pagination.total
})
})
]
}),
/*#__PURE__*/ jsxs(Box, {
flex: 1,
overflow: "auto",
children: [
versions.meta.pagination.page > 1 && /*#__PURE__*/ jsx(Box, {
paddingTop: 4,
textAlign: "center",
children: /*#__PURE__*/ jsx(PaginationButton, {
page: page - 1,
children: formatMessage({
id: 'content-manager.history.sidebar.show-newer',
defaultMessage: 'Show newer versions'
})
})
}),
/*#__PURE__*/ jsx(Flex, {
direction: "column",
gap: 3,
padding: 4,
tag: "ul",
alignItems: "stretch",
children: versions.data.map((version, index)=>/*#__PURE__*/ jsx("li", {
"aria-label": formatMessage({
id: 'content-manager.history.sidebar.title.version-card.aria-label',
defaultMessage: 'Version card'
}),
children: /*#__PURE__*/ jsx(VersionCard, {
version: version,
isCurrent: page === 1 && index === 0
})
}, version.id))
}),
versions.meta.pagination.page < versions.meta.pagination.pageCount && /*#__PURE__*/ jsx(Box, {
paddingBottom: 4,
textAlign: "center",
children: /*#__PURE__*/ jsx(PaginationButton, {
page: page + 1,
children: formatMessage({
id: 'content-manager.history.sidebar.show-older',
defaultMessage: 'Show older versions'
})
})
})
]
})
]
});
};
export { VersionsList };
//# sourceMappingURL=VersionsList.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,21 @@
'use strict';
var HistoryAction = require('./components/HistoryAction.js');
const historyAdmin = {
bootstrap (app) {
const { addDocumentAction } = app.getPlugin('content-manager').apis;
/**
* Register the document action here using the public API, and not by setting the action in the
* Content Manager directly, because this API lets us control the order of the actions array.
* We want history to be the last non-delete action in the array.
*/ addDocumentAction((actions)=>{
const indexOfDeleteAction = actions.findIndex((action)=>action.type === 'delete');
actions.splice(indexOfDeleteAction, 0, HistoryAction.HistoryAction);
return actions;
});
}
};
exports.historyAdmin = historyAdmin;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sources":["../../../admin/src/history/index.ts"],"sourcesContent":["/* eslint-disable check-file/no-index */\n\nimport { type ContentManagerPlugin } from '../content-manager';\n\nimport { HistoryAction } from './components/HistoryAction';\n\nimport type { StrapiApp } from '@strapi/admin/strapi-admin';\nimport type { Plugin } from '@strapi/types';\n\nconst historyAdmin: Partial<Plugin.Config.AdminInput> = {\n bootstrap(app: StrapiApp) {\n const { addDocumentAction } = app.getPlugin('content-manager').apis as {\n addDocumentAction: ContentManagerPlugin['addDocumentAction'];\n };\n\n /**\n * Register the document action here using the public API, and not by setting the action in the\n * Content Manager directly, because this API lets us control the order of the actions array.\n * We want history to be the last non-delete action in the array.\n */\n addDocumentAction((actions) => {\n const indexOfDeleteAction = actions.findIndex((action) => action.type === 'delete');\n actions.splice(indexOfDeleteAction, 0, HistoryAction);\n return actions;\n });\n },\n};\n\nexport { historyAdmin };\n"],"names":["historyAdmin","bootstrap","app","addDocumentAction","getPlugin","apis","actions","indexOfDeleteAction","findIndex","action","type","splice","HistoryAction"],"mappings":";;;;AASA,MAAMA,YAAkD,GAAA;AACtDC,IAAAA,SAAAA,CAAAA,CAAUC,GAAc,EAAA;QACtB,MAAM,EAAEC,iBAAiB,EAAE,GAAGD,IAAIE,SAAS,CAAC,mBAAmBC,IAAI;AAInE;;;;AAIC,QACDF,kBAAkB,CAACG,OAAAA,GAAAA;YACjB,MAAMC,mBAAAA,GAAsBD,QAAQE,SAAS,CAAC,CAACC,MAAWA,GAAAA,MAAAA,CAAOC,IAAI,KAAK,QAAA,CAAA;YAC1EJ,OAAQK,CAAAA,MAAM,CAACJ,mBAAAA,EAAqB,CAAGK,EAAAA,2BAAAA,CAAAA;YACvC,OAAON,OAAAA;AACT,SAAA,CAAA;AACF;AACF;;;;"}

View File

@@ -0,0 +1,19 @@
import { HistoryAction } from './components/HistoryAction.mjs';
const historyAdmin = {
bootstrap (app) {
const { addDocumentAction } = app.getPlugin('content-manager').apis;
/**
* Register the document action here using the public API, and not by setting the action in the
* Content Manager directly, because this API lets us control the order of the actions array.
* We want history to be the last non-delete action in the array.
*/ addDocumentAction((actions)=>{
const indexOfDeleteAction = actions.findIndex((action)=>action.type === 'delete');
actions.splice(indexOfDeleteAction, 0, HistoryAction);
return actions;
});
}
};
export { historyAdmin };
//# sourceMappingURL=index.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.mjs","sources":["../../../admin/src/history/index.ts"],"sourcesContent":["/* eslint-disable check-file/no-index */\n\nimport { type ContentManagerPlugin } from '../content-manager';\n\nimport { HistoryAction } from './components/HistoryAction';\n\nimport type { StrapiApp } from '@strapi/admin/strapi-admin';\nimport type { Plugin } from '@strapi/types';\n\nconst historyAdmin: Partial<Plugin.Config.AdminInput> = {\n bootstrap(app: StrapiApp) {\n const { addDocumentAction } = app.getPlugin('content-manager').apis as {\n addDocumentAction: ContentManagerPlugin['addDocumentAction'];\n };\n\n /**\n * Register the document action here using the public API, and not by setting the action in the\n * Content Manager directly, because this API lets us control the order of the actions array.\n * We want history to be the last non-delete action in the array.\n */\n addDocumentAction((actions) => {\n const indexOfDeleteAction = actions.findIndex((action) => action.type === 'delete');\n actions.splice(indexOfDeleteAction, 0, HistoryAction);\n return actions;\n });\n },\n};\n\nexport { historyAdmin };\n"],"names":["historyAdmin","bootstrap","app","addDocumentAction","getPlugin","apis","actions","indexOfDeleteAction","findIndex","action","type","splice","HistoryAction"],"mappings":";;AASA,MAAMA,YAAkD,GAAA;AACtDC,IAAAA,SAAAA,CAAAA,CAAUC,GAAc,EAAA;QACtB,MAAM,EAAEC,iBAAiB,EAAE,GAAGD,IAAIE,SAAS,CAAC,mBAAmBC,IAAI;AAInE;;;;AAIC,QACDF,kBAAkB,CAACG,OAAAA,GAAAA;YACjB,MAAMC,mBAAAA,GAAsBD,QAAQE,SAAS,CAAC,CAACC,MAAWA,GAAAA,MAAAA,CAAOC,IAAI,KAAK,QAAA,CAAA;YAC1EJ,OAAQK,CAAAA,MAAM,CAACJ,mBAAAA,EAAqB,CAAGK,EAAAA,aAAAA,CAAAA;YACvC,OAAON,OAAAA;AACT,SAAA,CAAA;AACF;AACF;;;;"}

View File

@@ -0,0 +1,220 @@
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var React = require('react');
var strapiAdmin = require('@strapi/admin/strapi-admin');
var designSystem = require('@strapi/design-system');
var qs = require('qs');
var reactIntl = require('react-intl');
var reactRouterDom = require('react-router-dom');
var collections = require('../../constants/collections.js');
var plugin = require('../../constants/plugin.js');
var DocumentRBAC = require('../../features/DocumentRBAC.js');
var useDocument = require('../../hooks/useDocument.js');
var useDocumentLayout = require('../../hooks/useDocumentLayout.js');
var contentTypes = require('../../services/contentTypes.js');
var api = require('../../utils/api.js');
var VersionContent = require('../components/VersionContent.js');
var VersionHeader = require('../components/VersionHeader.js');
var VersionsList = require('../components/VersionsList.js');
var historyVersion = require('../services/historyVersion.js');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const [HistoryProvider, useHistoryContext] = strapiAdmin.createContext('HistoryPage');
/* -------------------------------------------------------------------------------------------------
* HistoryPage
* -----------------------------------------------------------------------------------------------*/ const HistoryPage = ()=>{
const headerId = React__namespace.useId();
const { formatMessage } = reactIntl.useIntl();
const { slug, id: documentId, collectionType } = reactRouterDom.useParams();
const { isLoading: isLoadingDocument, schema } = useDocument.useDocument({
collectionType: collectionType,
model: slug
});
const { isLoading: isLoadingLayout, edit: { layout, settings: { displayName, mainField } } } = useDocumentLayout.useDocumentLayout(slug);
const { data: configuration, isLoading: isLoadingConfiguration } = contentTypes.useGetContentTypeConfigurationQuery(slug);
// Parse state from query params
const [{ query }] = strapiAdmin.useQueryParams();
const { id: selectedVersionId, ...queryWithoutId } = query;
const validQueryParamsWithoutId = api.buildValidParams(queryWithoutId);
const page = validQueryParamsWithoutId.page ? Number(validQueryParamsWithoutId.page) : 1;
const versionsResponse = historyVersion.useGetHistoryVersionsQuery({
contentType: slug,
...documentId ? {
documentId
} : {},
// Omit id since it's not needed by the endpoint and caused extra refetches
...validQueryParamsWithoutId
}, {
refetchOnMountOrArgChange: true
});
/**
* When the page is first mounted, if there's already data in the cache, RTK has a fullfilled
* status for the first render, right before it triggers a new request. This means the code
* briefly reaches the part that redirects to the first history version (if none is set).
* But since that data is stale, that means auto-selecting a version that may not be the most
* recent. To avoid this, we identify through requestId if the query is stale despite the
* fullfilled status, and show the loader in that case.
* This means we essentially don't want cache. We always refetch when the page mounts, and
* we always show the loader until we have the most recent data. That's fine for this page.
*/ const initialRequestId = React__namespace.useRef(versionsResponse.requestId);
const isStaleRequest = versionsResponse.requestId === initialRequestId.current;
/**
* Ensure that we have the necessary data to render the page:
* - slug for single types
* - slug _and_ documentId for collection types
*/ if (!slug || collectionType === collections.COLLECTION_TYPES && !documentId) {
return /*#__PURE__*/ jsxRuntime.jsx(reactRouterDom.Navigate, {
to: "/content-manager"
});
}
if (isLoadingDocument || isLoadingLayout || versionsResponse.isFetching || isStaleRequest || isLoadingConfiguration) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Loading, {});
}
// It was a success, handle empty data
if (!versionsResponse.isError && !versionsResponse.data?.data?.length) {
return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.NoData, {
action: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Link, {
tag: reactRouterDom.NavLink,
to: `/content-manager/${collectionType}/${slug}${documentId ? `/${documentId}` : ''}`,
children: formatMessage({
id: 'global.back',
defaultMessage: 'Back'
})
})
})
});
}
// We have data, handle selected version
if (versionsResponse.data?.data?.length && !selectedVersionId) {
return /*#__PURE__*/ jsxRuntime.jsx(reactRouterDom.Navigate, {
to: {
search: qs.stringify({
...query,
id: versionsResponse.data.data[0].id
})
},
replace: true
});
}
const selectedVersion = versionsResponse.data?.data?.find((version)=>version.id.toString() === selectedVersionId);
if (versionsResponse.isError || !layout || !schema || !selectedVersion || !configuration || // This should not happen as it's covered by versionsResponse.isError, but we need it for TS
versionsResponse.data.error) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Error, {});
}
return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
children: [
/*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Title, {
children: formatMessage({
id: 'content-manager.history.page-title',
defaultMessage: '{contentType} history'
}, {
contentType: displayName
})
}),
/*#__PURE__*/ jsxRuntime.jsx(HistoryProvider, {
contentType: slug,
id: documentId,
schema: schema,
layout: layout,
configuration: configuration,
selectedVersion: selectedVersion,
versions: versionsResponse.data,
page: page,
mainField: mainField,
children: /*#__PURE__*/ jsxRuntime.jsxs(designSystem.Flex, {
direction: "row",
alignItems: "flex-start",
children: [
/*#__PURE__*/ jsxRuntime.jsxs(designSystem.Main, {
grow: 1,
height: "100vh",
background: "neutral100",
paddingBottom: 6,
overflow: "auto",
labelledBy: headerId,
children: [
/*#__PURE__*/ jsxRuntime.jsx(VersionHeader.VersionHeader, {
headerId: headerId
}),
/*#__PURE__*/ jsxRuntime.jsx(VersionContent.VersionContent, {})
]
}),
/*#__PURE__*/ jsxRuntime.jsx(VersionsList.VersionsList, {})
]
})
})
]
});
};
/* -------------------------------------------------------------------------------------------------
* ProtectedHistoryPage
* -----------------------------------------------------------------------------------------------*/ const ProtectedHistoryPageImpl = ()=>{
const { slug } = reactRouterDom.useParams();
const { permissions = [], isLoading, error } = strapiAdmin.useRBAC(plugin.PERMISSIONS.map((action)=>({
action,
subject: slug
})));
if (isLoading) {
return /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Loading, {});
}
if (error || !slug) {
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
height: "100vh",
width: "100vw",
position: "fixed",
top: 0,
left: 0,
zIndex: 2,
background: "neutral0",
children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Error, {})
});
}
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
height: "100vh",
width: "100vw",
position: "fixed",
top: 0,
left: 0,
zIndex: 2,
background: "neutral0",
children: /*#__PURE__*/ jsxRuntime.jsx(strapiAdmin.Page.Protect, {
permissions: permissions,
children: ({ permissions })=>/*#__PURE__*/ jsxRuntime.jsx(DocumentRBAC.DocumentRBAC, {
permissions: permissions,
children: /*#__PURE__*/ jsxRuntime.jsx(HistoryPage, {})
})
})
});
};
const ProtectedHistoryPage = ()=>{
return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Portal, {
children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.FocusTrap, {
children: /*#__PURE__*/ jsxRuntime.jsx(ProtectedHistoryPageImpl, {})
})
});
};
exports.HistoryProvider = HistoryProvider;
exports.ProtectedHistoryPage = ProtectedHistoryPage;
exports.useHistoryContext = useHistoryContext;
//# sourceMappingURL=History.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,197 @@
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
import * as React from 'react';
import { createContext, useRBAC, Page, useQueryParams } from '@strapi/admin/strapi-admin';
import { Portal, FocusTrap, Box, Link, Flex, Main } from '@strapi/design-system';
import { stringify } from 'qs';
import { useIntl } from 'react-intl';
import { useParams, Navigate, NavLink } from 'react-router-dom';
import { COLLECTION_TYPES } from '../../constants/collections.mjs';
import { PERMISSIONS } from '../../constants/plugin.mjs';
import { DocumentRBAC } from '../../features/DocumentRBAC.mjs';
import { useDocument } from '../../hooks/useDocument.mjs';
import { useDocumentLayout } from '../../hooks/useDocumentLayout.mjs';
import { useGetContentTypeConfigurationQuery } from '../../services/contentTypes.mjs';
import { buildValidParams } from '../../utils/api.mjs';
import { VersionContent } from '../components/VersionContent.mjs';
import { VersionHeader } from '../components/VersionHeader.mjs';
import { VersionsList } from '../components/VersionsList.mjs';
import { useGetHistoryVersionsQuery } from '../services/historyVersion.mjs';
const [HistoryProvider, useHistoryContext] = createContext('HistoryPage');
/* -------------------------------------------------------------------------------------------------
* HistoryPage
* -----------------------------------------------------------------------------------------------*/ const HistoryPage = ()=>{
const headerId = React.useId();
const { formatMessage } = useIntl();
const { slug, id: documentId, collectionType } = useParams();
const { isLoading: isLoadingDocument, schema } = useDocument({
collectionType: collectionType,
model: slug
});
const { isLoading: isLoadingLayout, edit: { layout, settings: { displayName, mainField } } } = useDocumentLayout(slug);
const { data: configuration, isLoading: isLoadingConfiguration } = useGetContentTypeConfigurationQuery(slug);
// Parse state from query params
const [{ query }] = useQueryParams();
const { id: selectedVersionId, ...queryWithoutId } = query;
const validQueryParamsWithoutId = buildValidParams(queryWithoutId);
const page = validQueryParamsWithoutId.page ? Number(validQueryParamsWithoutId.page) : 1;
const versionsResponse = useGetHistoryVersionsQuery({
contentType: slug,
...documentId ? {
documentId
} : {},
// Omit id since it's not needed by the endpoint and caused extra refetches
...validQueryParamsWithoutId
}, {
refetchOnMountOrArgChange: true
});
/**
* When the page is first mounted, if there's already data in the cache, RTK has a fullfilled
* status for the first render, right before it triggers a new request. This means the code
* briefly reaches the part that redirects to the first history version (if none is set).
* But since that data is stale, that means auto-selecting a version that may not be the most
* recent. To avoid this, we identify through requestId if the query is stale despite the
* fullfilled status, and show the loader in that case.
* This means we essentially don't want cache. We always refetch when the page mounts, and
* we always show the loader until we have the most recent data. That's fine for this page.
*/ const initialRequestId = React.useRef(versionsResponse.requestId);
const isStaleRequest = versionsResponse.requestId === initialRequestId.current;
/**
* Ensure that we have the necessary data to render the page:
* - slug for single types
* - slug _and_ documentId for collection types
*/ if (!slug || collectionType === COLLECTION_TYPES && !documentId) {
return /*#__PURE__*/ jsx(Navigate, {
to: "/content-manager"
});
}
if (isLoadingDocument || isLoadingLayout || versionsResponse.isFetching || isStaleRequest || isLoadingConfiguration) {
return /*#__PURE__*/ jsx(Page.Loading, {});
}
// It was a success, handle empty data
if (!versionsResponse.isError && !versionsResponse.data?.data?.length) {
return /*#__PURE__*/ jsx(Fragment, {
children: /*#__PURE__*/ jsx(Page.NoData, {
action: /*#__PURE__*/ jsx(Link, {
tag: NavLink,
to: `/content-manager/${collectionType}/${slug}${documentId ? `/${documentId}` : ''}`,
children: formatMessage({
id: 'global.back',
defaultMessage: 'Back'
})
})
})
});
}
// We have data, handle selected version
if (versionsResponse.data?.data?.length && !selectedVersionId) {
return /*#__PURE__*/ jsx(Navigate, {
to: {
search: stringify({
...query,
id: versionsResponse.data.data[0].id
})
},
replace: true
});
}
const selectedVersion = versionsResponse.data?.data?.find((version)=>version.id.toString() === selectedVersionId);
if (versionsResponse.isError || !layout || !schema || !selectedVersion || !configuration || // This should not happen as it's covered by versionsResponse.isError, but we need it for TS
versionsResponse.data.error) {
return /*#__PURE__*/ jsx(Page.Error, {});
}
return /*#__PURE__*/ jsxs(Fragment, {
children: [
/*#__PURE__*/ jsx(Page.Title, {
children: formatMessage({
id: 'content-manager.history.page-title',
defaultMessage: '{contentType} history'
}, {
contentType: displayName
})
}),
/*#__PURE__*/ jsx(HistoryProvider, {
contentType: slug,
id: documentId,
schema: schema,
layout: layout,
configuration: configuration,
selectedVersion: selectedVersion,
versions: versionsResponse.data,
page: page,
mainField: mainField,
children: /*#__PURE__*/ jsxs(Flex, {
direction: "row",
alignItems: "flex-start",
children: [
/*#__PURE__*/ jsxs(Main, {
grow: 1,
height: "100vh",
background: "neutral100",
paddingBottom: 6,
overflow: "auto",
labelledBy: headerId,
children: [
/*#__PURE__*/ jsx(VersionHeader, {
headerId: headerId
}),
/*#__PURE__*/ jsx(VersionContent, {})
]
}),
/*#__PURE__*/ jsx(VersionsList, {})
]
})
})
]
});
};
/* -------------------------------------------------------------------------------------------------
* ProtectedHistoryPage
* -----------------------------------------------------------------------------------------------*/ const ProtectedHistoryPageImpl = ()=>{
const { slug } = useParams();
const { permissions = [], isLoading, error } = useRBAC(PERMISSIONS.map((action)=>({
action,
subject: slug
})));
if (isLoading) {
return /*#__PURE__*/ jsx(Page.Loading, {});
}
if (error || !slug) {
return /*#__PURE__*/ jsx(Box, {
height: "100vh",
width: "100vw",
position: "fixed",
top: 0,
left: 0,
zIndex: 2,
background: "neutral0",
children: /*#__PURE__*/ jsx(Page.Error, {})
});
}
return /*#__PURE__*/ jsx(Box, {
height: "100vh",
width: "100vw",
position: "fixed",
top: 0,
left: 0,
zIndex: 2,
background: "neutral0",
children: /*#__PURE__*/ jsx(Page.Protect, {
permissions: permissions,
children: ({ permissions })=>/*#__PURE__*/ jsx(DocumentRBAC, {
permissions: permissions,
children: /*#__PURE__*/ jsx(HistoryPage, {})
})
})
});
};
const ProtectedHistoryPage = ()=>{
return /*#__PURE__*/ jsx(Portal, {
children: /*#__PURE__*/ jsx(FocusTrap, {
children: /*#__PURE__*/ jsx(ProtectedHistoryPageImpl, {})
})
});
};
export { HistoryProvider, ProtectedHistoryPage, useHistoryContext };
//# sourceMappingURL=History.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,41 @@
'use strict';
var React = require('react');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
const ProtectedHistoryPage = /*#__PURE__*/ React__namespace.lazy(()=>Promise.resolve().then(function () { return require('./pages/History.js'); }).then((mod)=>({
default: mod.ProtectedHistoryPage
})));
/**
* These routes will be merged with the rest of the Content Manager routes
*/ const routes = [
{
path: ':collectionType/:slug/:id/history',
Component: ProtectedHistoryPage
},
{
path: ':collectionType/:slug/history',
Component: ProtectedHistoryPage
}
];
exports.routes = routes;
//# sourceMappingURL=routes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"routes.js","sources":["../../../admin/src/history/routes.tsx"],"sourcesContent":["/* eslint-disable check-file/filename-naming-convention */\nimport * as React from 'react';\n\nimport { type PathRouteProps } from 'react-router-dom';\n\nconst ProtectedHistoryPage = React.lazy(() =>\n import('./pages/History').then((mod) => ({ default: mod.ProtectedHistoryPage }))\n);\n\n/**\n * These routes will be merged with the rest of the Content Manager routes\n */\nconst routes: PathRouteProps[] = [\n {\n path: ':collectionType/:slug/:id/history',\n Component: ProtectedHistoryPage,\n },\n {\n path: ':collectionType/:slug/history',\n Component: ProtectedHistoryPage,\n },\n];\n\nexport { routes };\n"],"names":["ProtectedHistoryPage","React","lazy","then","mod","default","routes","path","Component"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAKA,MAAMA,oBAAuBC,iBAAAA,gBAAAA,CAAMC,IAAI,CAAC,IACtC,oDAAO,oBAAA,KAAA,CAAmBC,IAAI,CAAC,CAACC,GAAAA,IAAS;AAAEC,YAAAA,OAAAA,EAASD,IAAIJ;SAAqB,CAAA,CAAA,CAAA;AAG/E;;AAEC,UACKM,MAA2B,GAAA;AAC/B,IAAA;QACEC,IAAM,EAAA,mCAAA;QACNC,SAAWR,EAAAA;AACb,KAAA;AACA,IAAA;QACEO,IAAM,EAAA,+BAAA;QACNC,SAAWR,EAAAA;AACb;AACD;;;;"}

View File

@@ -0,0 +1,20 @@
import * as React from 'react';
const ProtectedHistoryPage = /*#__PURE__*/ React.lazy(()=>import('./pages/History.mjs').then((mod)=>({
default: mod.ProtectedHistoryPage
})));
/**
* These routes will be merged with the rest of the Content Manager routes
*/ const routes = [
{
path: ':collectionType/:slug/:id/history',
Component: ProtectedHistoryPage
},
{
path: ':collectionType/:slug/history',
Component: ProtectedHistoryPage
}
];
export { routes };
//# sourceMappingURL=routes.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"routes.mjs","sources":["../../../admin/src/history/routes.tsx"],"sourcesContent":["/* eslint-disable check-file/filename-naming-convention */\nimport * as React from 'react';\n\nimport { type PathRouteProps } from 'react-router-dom';\n\nconst ProtectedHistoryPage = React.lazy(() =>\n import('./pages/History').then((mod) => ({ default: mod.ProtectedHistoryPage }))\n);\n\n/**\n * These routes will be merged with the rest of the Content Manager routes\n */\nconst routes: PathRouteProps[] = [\n {\n path: ':collectionType/:slug/:id/history',\n Component: ProtectedHistoryPage,\n },\n {\n path: ':collectionType/:slug/history',\n Component: ProtectedHistoryPage,\n },\n];\n\nexport { routes };\n"],"names":["ProtectedHistoryPage","React","lazy","then","mod","default","routes","path","Component"],"mappings":";;AAKA,MAAMA,oBAAuBC,iBAAAA,KAAAA,CAAMC,IAAI,CAAC,IACtC,OAAO,qBAAA,CAAA,CAAmBC,IAAI,CAAC,CAACC,GAAAA,IAAS;AAAEC,YAAAA,OAAAA,EAASD,IAAIJ;SAAqB,CAAA,CAAA,CAAA;AAG/E;;AAEC,UACKM,MAA2B,GAAA;AAC/B,IAAA;QACEC,IAAM,EAAA,mCAAA;QACNC,SAAWR,EAAAA;AACb,KAAA;AACA,IAAA;QACEO,IAAM,EAAA,+BAAA;QACNC,SAAWR,EAAAA;AACb;AACD;;;;"}

View File

@@ -0,0 +1,46 @@
'use strict';
var collections = require('../../constants/collections.js');
var api = require('../../services/api.js');
const historyVersionsApi = api.contentManagerApi.injectEndpoints({
endpoints: (builder)=>({
getHistoryVersions: builder.query({
query (params) {
return {
url: `/content-manager/history-versions`,
method: 'GET',
config: {
params
}
};
},
providesTags: [
'HistoryVersion'
]
}),
restoreVersion: builder.mutation({
query ({ params, body }) {
return {
url: `/content-manager/history-versions/${params.versionId}/restore`,
method: 'PUT',
data: body
};
},
invalidatesTags: (_res, _error, { documentId, collectionType, params })=>{
return [
'HistoryVersion',
{
type: 'Document',
id: collectionType === collections.COLLECTION_TYPES ? `${params.contentType}_${documentId}` : params.contentType
}
];
}
})
})
});
const { useGetHistoryVersionsQuery, useRestoreVersionMutation } = historyVersionsApi;
exports.useGetHistoryVersionsQuery = useGetHistoryVersionsQuery;
exports.useRestoreVersionMutation = useRestoreVersionMutation;
//# sourceMappingURL=historyVersion.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"historyVersion.js","sources":["../../../../admin/src/history/services/historyVersion.ts"],"sourcesContent":["import {\n GetHistoryVersions,\n RestoreHistoryVersion,\n} from '../../../../shared/contracts/history-versions';\nimport { COLLECTION_TYPES } from '../../constants/collections';\nimport { contentManagerApi } from '../../services/api';\n\nimport type { Data } from '@strapi/types';\n\ninterface RestoreVersion extends RestoreHistoryVersion.Request {\n documentId: Data.ID;\n collectionType?: string;\n}\n\nconst historyVersionsApi = contentManagerApi.injectEndpoints({\n endpoints: (builder) => ({\n getHistoryVersions: builder.query<\n GetHistoryVersions.Response,\n GetHistoryVersions.Request['query']\n >({\n query(params) {\n return {\n url: `/content-manager/history-versions`,\n method: 'GET',\n config: {\n params,\n },\n };\n },\n providesTags: ['HistoryVersion'],\n }),\n restoreVersion: builder.mutation<RestoreHistoryVersion.Response, RestoreVersion>({\n query({ params, body }) {\n return {\n url: `/content-manager/history-versions/${params.versionId}/restore`,\n method: 'PUT',\n data: body,\n };\n },\n invalidatesTags: (_res, _error, { documentId, collectionType, params }) => {\n return [\n 'HistoryVersion',\n {\n type: 'Document',\n id:\n collectionType === COLLECTION_TYPES\n ? `${params.contentType}_${documentId}`\n : params.contentType,\n },\n ];\n },\n }),\n }),\n});\n\nconst { useGetHistoryVersionsQuery, useRestoreVersionMutation } = historyVersionsApi;\n\nexport { useGetHistoryVersionsQuery, useRestoreVersionMutation };\n"],"names":["historyVersionsApi","contentManagerApi","injectEndpoints","endpoints","builder","getHistoryVersions","query","params","url","method","config","providesTags","restoreVersion","mutation","body","versionId","data","invalidatesTags","_res","_error","documentId","collectionType","type","id","COLLECTION_TYPES","contentType","useGetHistoryVersionsQuery","useRestoreVersionMutation"],"mappings":";;;;;AAcA,MAAMA,kBAAAA,GAAqBC,qBAAkBC,CAAAA,eAAe,CAAC;IAC3DC,SAAW,EAAA,CAACC,WAAa;YACvBC,kBAAoBD,EAAAA,OAAAA,CAAQE,KAAK,CAG/B;AACAA,gBAAAA,KAAAA,CAAAA,CAAMC,MAAM,EAAA;oBACV,OAAO;wBACLC,GAAK,EAAA,CAAC,iCAAiC,CAAC;wBACxCC,MAAQ,EAAA,KAAA;wBACRC,MAAQ,EAAA;AACNH,4BAAAA;AACF;AACF,qBAAA;AACF,iBAAA;gBACAI,YAAc,EAAA;AAAC,oBAAA;AAAiB;AAClC,aAAA,CAAA;YACAC,cAAgBR,EAAAA,OAAAA,CAAQS,QAAQ,CAAiD;AAC/EP,gBAAAA,KAAAA,CAAAA,CAAM,EAAEC,MAAM,EAAEO,IAAI,EAAE,EAAA;oBACpB,OAAO;AACLN,wBAAAA,GAAAA,EAAK,CAAC,kCAAkC,EAAED,OAAOQ,SAAS,CAAC,QAAQ,CAAC;wBACpEN,MAAQ,EAAA,KAAA;wBACRO,IAAMF,EAAAA;AACR,qBAAA;AACF,iBAAA;gBACAG,eAAiB,EAAA,CAACC,MAAMC,MAAQ,EAAA,EAAEC,UAAU,EAAEC,cAAc,EAAEd,MAAM,EAAE,GAAA;oBACpE,OAAO;AACL,wBAAA,gBAAA;AACA,wBAAA;4BACEe,IAAM,EAAA,UAAA;AACNC,4BAAAA,EAAAA,EACEF,cAAmBG,KAAAA,4BAAAA,GACf,CAAC,EAAEjB,MAAOkB,CAAAA,WAAW,CAAC,CAAC,EAAEL,UAAAA,CAAW,CAAC,GACrCb,OAAOkB;AACf;AACD,qBAAA;AACH;AACF,aAAA;SACF;AACF,CAAA,CAAA;AAEA,MAAM,EAAEC,0BAA0B,EAAEC,yBAAyB,EAAE,GAAG3B;;;;;"}

View File

@@ -0,0 +1,43 @@
import { COLLECTION_TYPES } from '../../constants/collections.mjs';
import { contentManagerApi } from '../../services/api.mjs';
const historyVersionsApi = contentManagerApi.injectEndpoints({
endpoints: (builder)=>({
getHistoryVersions: builder.query({
query (params) {
return {
url: `/content-manager/history-versions`,
method: 'GET',
config: {
params
}
};
},
providesTags: [
'HistoryVersion'
]
}),
restoreVersion: builder.mutation({
query ({ params, body }) {
return {
url: `/content-manager/history-versions/${params.versionId}/restore`,
method: 'PUT',
data: body
};
},
invalidatesTags: (_res, _error, { documentId, collectionType, params })=>{
return [
'HistoryVersion',
{
type: 'Document',
id: collectionType === COLLECTION_TYPES ? `${params.contentType}_${documentId}` : params.contentType
}
];
}
})
})
});
const { useGetHistoryVersionsQuery, useRestoreVersionMutation } = historyVersionsApi;
export { useGetHistoryVersionsQuery, useRestoreVersionMutation };
//# sourceMappingURL=historyVersion.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"historyVersion.mjs","sources":["../../../../admin/src/history/services/historyVersion.ts"],"sourcesContent":["import {\n GetHistoryVersions,\n RestoreHistoryVersion,\n} from '../../../../shared/contracts/history-versions';\nimport { COLLECTION_TYPES } from '../../constants/collections';\nimport { contentManagerApi } from '../../services/api';\n\nimport type { Data } from '@strapi/types';\n\ninterface RestoreVersion extends RestoreHistoryVersion.Request {\n documentId: Data.ID;\n collectionType?: string;\n}\n\nconst historyVersionsApi = contentManagerApi.injectEndpoints({\n endpoints: (builder) => ({\n getHistoryVersions: builder.query<\n GetHistoryVersions.Response,\n GetHistoryVersions.Request['query']\n >({\n query(params) {\n return {\n url: `/content-manager/history-versions`,\n method: 'GET',\n config: {\n params,\n },\n };\n },\n providesTags: ['HistoryVersion'],\n }),\n restoreVersion: builder.mutation<RestoreHistoryVersion.Response, RestoreVersion>({\n query({ params, body }) {\n return {\n url: `/content-manager/history-versions/${params.versionId}/restore`,\n method: 'PUT',\n data: body,\n };\n },\n invalidatesTags: (_res, _error, { documentId, collectionType, params }) => {\n return [\n 'HistoryVersion',\n {\n type: 'Document',\n id:\n collectionType === COLLECTION_TYPES\n ? `${params.contentType}_${documentId}`\n : params.contentType,\n },\n ];\n },\n }),\n }),\n});\n\nconst { useGetHistoryVersionsQuery, useRestoreVersionMutation } = historyVersionsApi;\n\nexport { useGetHistoryVersionsQuery, useRestoreVersionMutation };\n"],"names":["historyVersionsApi","contentManagerApi","injectEndpoints","endpoints","builder","getHistoryVersions","query","params","url","method","config","providesTags","restoreVersion","mutation","body","versionId","data","invalidatesTags","_res","_error","documentId","collectionType","type","id","COLLECTION_TYPES","contentType","useGetHistoryVersionsQuery","useRestoreVersionMutation"],"mappings":";;;AAcA,MAAMA,kBAAAA,GAAqBC,iBAAkBC,CAAAA,eAAe,CAAC;IAC3DC,SAAW,EAAA,CAACC,WAAa;YACvBC,kBAAoBD,EAAAA,OAAAA,CAAQE,KAAK,CAG/B;AACAA,gBAAAA,KAAAA,CAAAA,CAAMC,MAAM,EAAA;oBACV,OAAO;wBACLC,GAAK,EAAA,CAAC,iCAAiC,CAAC;wBACxCC,MAAQ,EAAA,KAAA;wBACRC,MAAQ,EAAA;AACNH,4BAAAA;AACF;AACF,qBAAA;AACF,iBAAA;gBACAI,YAAc,EAAA;AAAC,oBAAA;AAAiB;AAClC,aAAA,CAAA;YACAC,cAAgBR,EAAAA,OAAAA,CAAQS,QAAQ,CAAiD;AAC/EP,gBAAAA,KAAAA,CAAAA,CAAM,EAAEC,MAAM,EAAEO,IAAI,EAAE,EAAA;oBACpB,OAAO;AACLN,wBAAAA,GAAAA,EAAK,CAAC,kCAAkC,EAAED,OAAOQ,SAAS,CAAC,QAAQ,CAAC;wBACpEN,MAAQ,EAAA,KAAA;wBACRO,IAAMF,EAAAA;AACR,qBAAA;AACF,iBAAA;gBACAG,eAAiB,EAAA,CAACC,MAAMC,MAAQ,EAAA,EAAEC,UAAU,EAAEC,cAAc,EAAEd,MAAM,EAAE,GAAA;oBACpE,OAAO;AACL,wBAAA,gBAAA;AACA,wBAAA;4BACEe,IAAM,EAAA,UAAA;AACNC,4BAAAA,EAAAA,EACEF,cAAmBG,KAAAA,gBAAAA,GACf,CAAC,EAAEjB,MAAOkB,CAAAA,WAAW,CAAC,CAAC,EAAEL,UAAAA,CAAW,CAAC,GACrCb,OAAOkB;AACf;AACD,qBAAA;AACH;AACF,aAAA;SACF;AACF,CAAA,CAAA;AAEA,MAAM,EAAEC,0BAA0B,EAAEC,yBAAyB,EAAE,GAAG3B;;;;"}